diff --git a/lecture/common/co2tablesbenchmark3.hh b/lecture/common/co2tablesbenchmark3.hh index be5ed36ed8e57e7faeecedb5180d85f35416f452..651a4ac1204ffcbfa2bf70ac04ba4c04b9716eaf 100644 --- a/lecture/common/co2tablesbenchmark3.hh +++ b/lecture/common/co2tablesbenchmark3.hh @@ -27,14 +27,12 @@ #include -namespace Dumux -{ -namespace CO2TablesBenchmarkThree -{ +namespace Dumux { +namespace CO2TablesBenchmarkThree { // the real work is done by some external program which provides // ready-to-use tables. #include "co2values.inc" -} -} +} // end namespace CO2TablesBenchmarkThree +} // end namespace Dumux -#endif +#endif // DUMUX_HETEROGENEOUS_CO2TABLES_HH diff --git a/lecture/common/viplaboutput.hh b/lecture/common/viplaboutput.hh index aa16d2d3dc7153c23d5c6b9eeeaf1710a0de4bbc..c8fdab35d925c89030043228ec4958ecce8c3efc 100644 --- a/lecture/common/viplaboutput.hh +++ b/lecture/common/viplaboutput.hh @@ -24,8 +24,7 @@ #ifndef DUMUX_VIPLAB_OUTPUT_HH #define DUMUX_VIPLAB_OUTPUT_HH -namespace Dumux -{ +namespace Dumux { /** * \brief Writes output to use with ViPLab. * @@ -35,8 +34,8 @@ namespace Dumux template class ViplabOutput { - using Problem = GET_PROP_TYPE(TypeTag, Problem); - using Scalar = GET_PROP_TYPE(TypeTag, Scalar); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using Indices = typename GET_PROP_TYPE(TypeTag, Indices); enum{ wPhaseIdx = Indices::wPhaseIdx }; @@ -92,9 +91,9 @@ public: { setColor(color); - const auto cellNumber = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, Grid, Cells); + const auto cellNumber = getParam>("Grid.Cells"); assert(cellNumber[0] == solution.size()); - upperRight_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, Grid, UpperRight); + upperRight_ = getParam>("Grid.UpperRight"); const Scalar discretizationLength = upperRight_[0] / cellNumber[0]; dataFile_.open(outputName_, std::fstream::app); @@ -108,8 +107,9 @@ public: dataFile_.close(); } - void writeViplabOutput2d(const std::vector &solution){ - + void writeViplabOutput2d(const std::vector &solution) + { + // TODO: check if this is used anymore: // double ymin= *min_element(solution.begin(),solution.end()); // Scalar zmax, zmin; // zmax=problem_.variables().cellData(0).pressure()/(Fluid::density(0,0)*9.81); @@ -254,6 +254,7 @@ private: std::string outputName_; std::vector upperRight_; }; + }//end namespace Dumux -#endif +#endif // DUMUX_VIPLAB_OUTPUT_HH diff --git a/lecture/efm/1p2c_2p_2p2c/CMakeLists.txt b/lecture/efm/1p2c_2p_2p2c/CMakeLists.txt index e0634800e20ea93a3fa050225fc9b35912bf2ee4..2b87c0a5000f8af585c8ca55c5d99f3d598c1724 100644 --- a/lecture/efm/1p2c_2p_2p2c/CMakeLists.txt +++ b/lecture/efm/1p2c_2p_2p2c/CMakeLists.txt @@ -5,21 +5,21 @@ add_dumux_test(lens2pexercise3 lens2pexercise3 lens2pexercise3.cc --script fuzzy --files ${CMAKE_SOURCE_DIR}/lecture/references/lens-2p-exercise3-reference.vtu ${CMAKE_CURRENT_BINARY_DIR}/lens-2p-00011.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/lens2pexercise3 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise3.input") + --command "${CMAKE_CURRENT_BINARY_DIR}/lens2pexercise3 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise3.input -Problem.Name lens-2p") add_dumux_test(lens2p2cexercise3 lens2p2cexercise3 lens2p2cexercise3.cc python ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py --script fuzzy --files ${CMAKE_SOURCE_DIR}/lecture/references/lens-2p2c-exercise3-reference.vtu ${CMAKE_CURRENT_BINARY_DIR}/lens-2p2c-00011.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/lens2p2cexercise3 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise3.input") + --command "${CMAKE_CURRENT_BINARY_DIR}/lens2p2cexercise3 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise3.input -Problem.Name lens-2p2c") add_dumux_test(lens1p2cexercise3 lens1p2cexercise3 lens1p2cexercise3.cc python ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py --script fuzzy --files ${CMAKE_SOURCE_DIR}/lecture/references/lens-1p2c-exercise3-reference.vtu ${CMAKE_CURRENT_BINARY_DIR}/lens-1p2c-00011.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/lens1p2cexercise3 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise3.input") + --command "${CMAKE_CURRENT_BINARY_DIR}/lens1p2cexercise3 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise3.input -Problem.Name lens-1p2c") # headers for installation and headercheck install(FILES diff --git a/lecture/efm/1p2c_2p_2p2c/lens1p2cexercise3.cc b/lecture/efm/1p2c_2p_2p2c/lens1p2cexercise3.cc index 3c9a7aa6f72dea625af7f850c65e27e6408f8637..d70edb3fadfab35f3301f170b825a541bf91c084 100755 --- a/lecture/efm/1p2c_2p_2p2c/lens1p2cexercise3.cc +++ b/lecture/efm/1p2c_2p_2p2c/lens1p2cexercise3.cc @@ -40,7 +40,7 @@ #include #include - +#include #include /*! @@ -52,9 +52,6 @@ * Comprises the thing that went wrong and a general help message. */ - - - void usage(const char *progName, const std::string &errorMsg) { if (errorMsg.size() > 0) { @@ -86,12 +83,10 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// int main(int argc, char** argv) try { - // typedef TTAG(LensOnePTwoCProblem) TypeTag; - // return Dumux::start(argc, argv, usage); using namespace Dumux; // define the type tag for this problem - using TypeTag = TTAG(LensOnePTwoCProblem); + using TypeTag = TTAG(LensOnePTwoCProblemTypeTag); // initialize MPI, finalize is done automatically on exit const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); @@ -103,17 +98,19 @@ int main(int argc, char** argv) try // parse command line arguments and input file Parameters::init(argc, argv, usage); + ////////////////////////////////////////////////////////////////////// // try to create a grid (from the given grid file or the input file) - using GridCreator = typename GET_PROP_TYPE(TypeTag, GridCreator); - GridCreator::makeGrid(); - GridCreator::loadBalance(); + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); //////////////////////////////////////////////////////////// // run instationary non-linear problem on this grid //////////////////////////////////////////////////////////// // we compute on the leaf grid view - const auto& leafGridView = GridCreator::grid().leafGridView(); + const auto& leafGridView = gridManager.grid().leafGridView(); // create the finite volume grid geometry using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); @@ -148,7 +145,7 @@ int main(int argc, char** argv) try // intialize the vtk output module using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); - VtkOutputModule vtkWriter(*problem, *fvGridGeometry, *gridVariables, x, problem->name()); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); VtkOutputFields::init(vtkWriter); //!< Add model specific output fields vtkWriter.write(0.0); @@ -208,8 +205,7 @@ int main(int argc, char** argv) try DumuxMessage::print(/*firstCall=*/false); } - - + return 0; } catch (Dumux::ParameterException &e) @@ -217,6 +213,7 @@ catch (Dumux::ParameterException &e) std::cerr << std::endl << e << " ---> Abort!" << std::endl; return 1; } + catch (Dune::DGFException & e) { std::cerr << "DGF exception thrown (" << e << @@ -226,11 +223,13 @@ catch (Dune::DGFException & e) << " ---> Abort!" << std::endl; return 2; } + catch (Dune::Exception &e) { std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; return 3; } + catch (...) { std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; diff --git a/lecture/efm/1p2c_2p_2p2c/lens1p2cproblem.hh b/lecture/efm/1p2c_2p_2p2c/lens1p2cproblem.hh index a1465935d24809522ea677724584fa7050df1d0b..a58fb3e6f67e8504664906edd1396e7c325eb26d 100644 --- a/lecture/efm/1p2c_2p_2p2c/lens1p2cproblem.hh +++ b/lecture/efm/1p2c_2p_2p2c/lens1p2cproblem.hh @@ -22,21 +22,19 @@ * \brief Exercise to show the diffusive spreading of contaminants. */ - #ifndef DUMUX_LENS_1P2C_PROBLEM_HH #define DUMUX_LENS_1P2C_PROBLEM_HH #include #include #include -// #include #include #include +#include #include "lens1p2cspatialparams.hh" -namespace Dumux -{ +namespace Dumux { template class LensOnePTwoCProblem; @@ -44,29 +42,28 @@ class LensOnePTwoCProblem; ////////// // Specify the properties for the lens problem ////////// -namespace Properties -{ -NEW_TYPE_TAG(LensOnePTwoCProblem, INHERITS_FROM(BoxModel, OnePNC, Lens1p2cSpatialParams)); +namespace Properties { + +NEW_TYPE_TAG(LensOnePTwoCProblemTypeTag, INHERITS_FROM(BoxModel, OnePNC, Lens1p2cSpatialParamsTypeTag)); // Set the grid type -SET_TYPE_PROP(LensOnePTwoCProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(LensOnePTwoCProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(LensOnePTwoCProblem, Problem, LensOnePTwoCProblem); +SET_TYPE_PROP(LensOnePTwoCProblemTypeTag, Problem, LensOnePTwoCProblem); // Set fluid configuration -SET_PROP(LensOnePTwoCProblem, FluidSystem) -{private: +SET_PROP(LensOnePTwoCProblemTypeTag, FluidSystem) +{ using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); - static const bool useComplexRelations = false; -public: - using type = FluidSystems::H2ON2; + using H2ON2 = FluidSystems::H2ON2>; + using type = FluidSystems::OnePAdapter; }; // Define whether mole(true) or mass(false) fractions are used -SET_BOOL_PROP(LensOnePTwoCProblem, UseMoles, true); +SET_BOOL_PROP(LensOnePTwoCProblemTypeTag, UseMoles, true); -} +} // end namespace Properties /*! * \ingroup TwoPBoxProblems @@ -97,43 +94,33 @@ template class LensOnePTwoCProblem : public PorousMediumFlowProblem { using ParentType = PorousMediumFlowProblem; - using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); - using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); - using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; - using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); - using Element = typename GridView::template Codim<0>::Entity; - using GlobalPosition = typename Element::Geometry::GlobalCoordinate; using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); - using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); - - - - - + enum { + H2OIdx = FluidSystem::compIdx(FluidSystem::MultiPhaseFluidSystem::H2OIdx), + N2Idx = FluidSystem::compIdx(FluidSystem::MultiPhaseFluidSystem::N2Idx) + }; public: /*! * \brief The constructor * - * \param timeManager The time manager - * \param gridView The grid view */ LensOnePTwoCProblem(std::shared_ptr fvGridGeometry ) - : ParentType(fvGridGeometry) + : ParentType(fvGridGeometry) { // the boundary condition data lowerPressure_ = getParam("Boundary.LowerPressure"); @@ -145,16 +132,6 @@ public: */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { - return "lens-1p2c"; - } - /*! * \brief Returns the temperature within the domain. * This problem assumes a temperature of 10 degrees Celsius. @@ -162,7 +139,7 @@ public: Scalar temperature() const { return 273.15 + 10; // -> 10 degrees Celsius - }; + } // \} @@ -184,6 +161,7 @@ public: BoundaryTypes values; if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) values.setAllDirichlet(); + else values.setAllNeumann(); @@ -207,10 +185,12 @@ public: { values[Indices::pressureIdx] = upperPressure_; } + else if (onLowerBoundary_(globalPos)) { values[Indices::pressureIdx] = lowerPressure_; } + return values; } @@ -268,7 +248,7 @@ public: const Scalar height = this->fvGridGeometry().bBoxMax()[1] - this->fvGridGeometry().bBoxMin()[1]; values[Indices::conti0EqIdx] = upperPressure_ - depth/height*(upperPressure_ - lowerPressure_); - values[FluidSystem::N2Idx] = (isInitial_(globalPos)) ? 9.12825137e-6 : 0.0; + values[N2Idx] = (isInitial_(globalPos)) ? 9.12825137e-6 : 0.0; return values; } @@ -312,7 +292,9 @@ private: Scalar infiltrationEndTime_; bool paraviewOutput_; std::vector numCells_; + }; -} //end namespace -#endif +} //end namespace Dumux + +#endif // DUMUX_LENS_1P2C_PROBLEM_HH diff --git a/lecture/efm/1p2c_2p_2p2c/lens1p2cspatialparams.hh b/lecture/efm/1p2c_2p_2p2c/lens1p2cspatialparams.hh index fa5aea3b45f518b03ecf339e4c2e15e2c2cd0488..ea0658a2daa89096bc222bded89430056e77a1f5 100644 --- a/lecture/efm/1p2c_2p_2p2c/lens1p2cspatialparams.hh +++ b/lecture/efm/1p2c_2p_2p2c/lens1p2cspatialparams.hh @@ -28,20 +28,18 @@ * @author Bernd Flemisch, Klaus Mosthaf, Markus Wolff */ -namespace Dumux -{ +namespace Dumux { //forward declaration template class Lens1p2cSpatialParams; -namespace Properties -{ +namespace Properties { // The spatial parameters TypeTag -NEW_TYPE_TAG(Lens1p2cSpatialParams); +NEW_TYPE_TAG(Lens1p2cSpatialParamsTypeTag); // Set the spatial parameters -SET_PROP(Lens1p2cSpatialParams, SpatialParams) +SET_PROP(Lens1p2cSpatialParamsTypeTag, SpatialParams) { private: using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); @@ -49,7 +47,8 @@ private: public: using type = Lens1p2cSpatialParams; }; -} + +} // end namespace Properties /** \todo Please doc me! */ @@ -58,12 +57,10 @@ class Lens1p2cSpatialParams : public FVSpatialParamsOneP; using ParentType = FVSpatialParamsOneP; - using GridView = typename FVGridGeometry::GridView; using Element = typename GridView::template Codim<0>::Entity; using FVElementGeometry = typename FVGridGeometry::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; - static constexpr int dim = GridView::dimension; static constexpr int dimWorld = GridView::dimensionworld; using GlobalPosition = typename Element::Geometry::GlobalCoordinate; @@ -109,6 +106,7 @@ public: { if (isInLens_(globalPos)) return lensK_; + return outerK_; } @@ -116,6 +114,7 @@ public: { if (isInLens_(globalPos)) return lensPorosity_; + return outerPorosity_; } /*! @@ -125,8 +124,7 @@ public: */ //! Set the bounding box of the fine-sand lens - void setLensCoords(const GlobalPosition& lensLowerLeft, - const GlobalPosition& lensUpperRight) + void setLensCoords(const GlobalPosition& lensLowerLeft, const GlobalPosition& lensUpperRight) { lensLowerLeft_ = lensLowerLeft; lensUpperRight_ = lensUpperRight; @@ -140,8 +138,8 @@ public: * \param scvIdx The local index of the sub-control volume where */ Dune::FieldVector dispersivity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + const FVElementGeometry &fvElemGeom, + int scvIdx) const { Dune::FieldVector values (0); values[0] = longitudinalDispersivity_; // alpha_l @@ -155,14 +153,11 @@ public: * \param vertexI DOC ME! * \param vertexJ DOC ME! */ - bool useTwoPointGradient(const Element &elem, - int vertexI, - int vertexJ) const + bool useTwoPointGradient(const Element &elem, int vertexI, int vertexJ) const { return false; } - private: /*! * \brief DOC ME! @@ -170,10 +165,12 @@ private: */ bool isInLens_(const GlobalPosition &pos) const { - for (int i = 0; i < dimWorld; ++i) { + for (int i = 0; i < dimWorld; ++i) + { if (pos[i] < lensLowerLeft_[i] || pos[i] > lensUpperRight_[i]) return false; } + return true; } @@ -188,5 +185,6 @@ private: Scalar transverseDispersivity_; }; -} // end namespace -#endif +} // end namespace Dumux + +#endif // DUMUX_1P2C_SPATIALPARAMS_HH diff --git a/lecture/efm/1p2c_2p_2p2c/lens2p2cexercise3.cc b/lecture/efm/1p2c_2p_2p2c/lens2p2cexercise3.cc index 01d7da47c8dfa3c16a526ddd1a39bcbf579dff20..30c8dc2862d2eac65533ca342ef5a481b0ffa73e 100644 --- a/lecture/efm/1p2c_2p_2p2c/lens2p2cexercise3.cc +++ b/lecture/efm/1p2c_2p_2p2c/lens2p2cexercise3.cc @@ -49,10 +49,9 @@ #include #include - +#include #include -// #include "config.h" #include "lens2p2cproblem.hh" /*! * \brief Provides an interface for customizing error messages associated with @@ -62,7 +61,6 @@ * \param errorMsg The error message that was issued by the start function. * Comprises the thing that went wrong and a general help message. */ -// TODO: do we need this void-function anymore? void usage(const char *progName, const std::string &errorMsg) { if (errorMsg.size() > 0) { @@ -106,12 +104,10 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// int main(int argc, char** argv) try { - // typedef TTAG(LensOnePTwoCProblem) TypeTag; - // return Dumux::start(argc, argv, usage); using namespace Dumux; // define the type tag for this problem - using TypeTag = TTAG(LensTwoPTwoCProblem); + using TypeTag = TTAG(LensTwoPTwoCProblemTypeTag); // initialize MPI, finalize is done automatically on exit const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); @@ -123,17 +119,19 @@ int main(int argc, char** argv) try // parse command line arguments and input file Parameters::init(argc, argv, usage); + ////////////////////////////////////////////////////////////////////// // try to create a grid (from the given grid file or the input file) - using GridCreator = typename GET_PROP_TYPE(TypeTag, GridCreator); - GridCreator::makeGrid(); - GridCreator::loadBalance(); + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); //////////////////////////////////////////////////////////// // run instationary non-linear problem on this grid //////////////////////////////////////////////////////////// // we compute on the leaf grid view - const auto& leafGridView = GridCreator::grid().leafGridView(); + const auto& leafGridView = gridManager.grid().leafGridView(); // create the finite volume grid geometry using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); @@ -168,7 +166,7 @@ int main(int argc, char** argv) try // intialize the vtk output module using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); - VtkOutputModule vtkWriter(*problem, *fvGridGeometry, *gridVariables, x, problem->name()); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); VtkOutputFields::init(vtkWriter); //!< Add model specific output fields vtkWriter.write(0.0); @@ -228,6 +226,8 @@ int main(int argc, char** argv) try Parameters::print(); DumuxMessage::print(/*firstCall=*/false); } + + return 0; } catch (Dumux::ParameterException &e) @@ -235,6 +235,7 @@ catch (Dumux::ParameterException &e) std::cerr << std::endl << e << " ---> Abort!" << std::endl; return 1; } + catch (Dune::DGFException & e) { std::cerr << "DGF exception thrown (" << e << @@ -244,11 +245,13 @@ catch (Dune::DGFException & e) << " ---> Abort!" << std::endl; return 2; } + catch (Dune::Exception &e) { std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; return 3; } + catch (...) { std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; diff --git a/lecture/efm/1p2c_2p_2p2c/lens2p2cproblem.hh b/lecture/efm/1p2c_2p_2p2c/lens2p2cproblem.hh index a13705aa7547b5ab75f74a074d292c35ab496290..3b77cdcc428db232542a4fcbc7567198cc05c8e3 100644 --- a/lecture/efm/1p2c_2p_2p2c/lens2p2cproblem.hh +++ b/lecture/efm/1p2c_2p_2p2c/lens2p2cproblem.hh @@ -28,7 +28,7 @@ #define DUMUX_LENS_2P2C_PROBLEM_HH #include -// #include + #include #include "lens2pspatialparams.hh" @@ -36,11 +36,10 @@ #include #include #include -// #include + #include #include -namespace Dumux -{ +namespace Dumux { template class LensTwoPTwoCProblem; @@ -48,25 +47,22 @@ class LensTwoPTwoCProblem; ////////// // Specify the properties for the lens problem ////////// -namespace Properties -{ -NEW_TYPE_TAG(LensTwoPTwoCProblem, INHERITS_FROM(TwoPTwoC, BoxModel, Lens2pSpatialParams)); +namespace Properties { + +NEW_TYPE_TAG(LensTwoPTwoCProblemTypeTag, INHERITS_FROM(TwoPTwoC, BoxModel, Lens2pSpatialParamsTypeTag)); // Set the grid type -SET_TYPE_PROP(LensTwoPTwoCProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(LensTwoPTwoCProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(LensTwoPTwoCProblem, Problem, LensTwoPTwoCProblem); +SET_TYPE_PROP(LensTwoPTwoCProblemTypeTag, Problem, LensTwoPTwoCProblem); // Set fluid configuration -SET_PROP(LensTwoPTwoCProblem, FluidSystem) -{private: - using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); - static const bool useComplexRelations = false; -public: - using type = FluidSystems::H2ON2; -}; -} +SET_TYPE_PROP(LensTwoPTwoCProblemTypeTag, + FluidSystem, + FluidSystems::H2ON2>); + +} // end namespace Properties /*! * \ingroup TwoPTwoCModel @@ -87,35 +83,28 @@ class LensTwoPTwoCProblem: public PorousMediumFlowProblem using ParentType = PorousMediumFlowProblem; using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); - using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; - using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); - using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; using Element = typename GridView::template Codim<0>::Entity; - using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); - static constexpr int dim = GridView::dimension; static constexpr int dimWorld = GridView::dimensionworld; using GlobalPosition = typename Element::Geometry::GlobalCoordinate; - public: /*! * \brief The constructor * * \param gridView The grid view */ - LensTwoPTwoCProblem(std::shared_ptr fvGridGeometry ) : ParentType(fvGridGeometry) { @@ -139,15 +128,6 @@ public: * \name Problem parameters */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { - return "lens-2p2c"; - } /*! * \brief Returns the temperature within the domain. @@ -181,6 +161,7 @@ public: BoundaryTypes values; if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) values.setAllDirichlet(); + else values.setAllNeumann(); @@ -205,6 +186,7 @@ public: values[Indices::pressureIdx] = upperPressure_; values[Indices::switchIdx] = 0.0; } + else if (onLowerBoundary_(globalPos)) { values[Indices::pressureIdx] = lowerPressure_; @@ -275,6 +257,7 @@ public: if (isInitial_(globalPos)) values.setState(Indices::bothPhases); + else values.setState(Indices::firstPhaseOnly); @@ -289,9 +272,7 @@ public: } // \} - private: - Scalar eps_; Scalar episodeLength_; // [s] Scalar temperature_; // [K] @@ -322,10 +303,10 @@ private: */ bool isInitial_(const GlobalPosition &globalPos) const { - return (globalPos[1] > 1.0 && globalPos[1] < 1.25 && globalPos[0] > 1.7 - && globalPos[0] < 2.3); + return (globalPos[1] > 1.0 && globalPos[1] < 1.25 && globalPos[0] > 1.7 && globalPos[0] < 2.3); } }; -} //end namespace -#endif +} //end namespace Dumux + +#endif // DUMUX_LENS_2P2C_PROBLEM_HH diff --git a/lecture/efm/1p2c_2p_2p2c/lens2pexercise3.cc b/lecture/efm/1p2c_2p_2p2c/lens2pexercise3.cc index a4ebf8bae469b3bf8c5df21fcda1e2ec8d1c7ea7..dbffe84db67d6cd96750d544dbbaa13c5da6af0d 100755 --- a/lecture/efm/1p2c_2p_2p2c/lens2pexercise3.cc +++ b/lecture/efm/1p2c_2p_2p2c/lens2pexercise3.cc @@ -46,7 +46,7 @@ #include #include - +#include #include /*! @@ -57,7 +57,7 @@ * \param errorMsg The error message that was issued by the start function. * Comprises the thing that went wrong and a general help message. */ -// TODO is this void-function still in use? + void usage(const char *progName, const std::string &errorMsg) { if (errorMsg.size() > 0) { @@ -101,12 +101,10 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// int main(int argc, char** argv) try { - // typedef TTAG(LensOnePTwoCProblem) TypeTag; - // return Dumux::start(argc, argv, usage); using namespace Dumux; // define the type tag for this problem - using TypeTag = TTAG(LensTwoPProblem); + using TypeTag = TTAG(LensTwoPProblemTypeTag); // initialize MPI, finalize is done automatically on exit const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); @@ -118,17 +116,19 @@ int main(int argc, char** argv) try // parse command line arguments and input file Parameters::init(argc, argv, usage); + ////////////////////////////////////////////////////////////////////// // try to create a grid (from the given grid file or the input file) - using GridCreator = typename GET_PROP_TYPE(TypeTag, GridCreator); - GridCreator::makeGrid(); - GridCreator::loadBalance(); + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); //////////////////////////////////////////////////////////// // run instationary non-linear problem on this grid //////////////////////////////////////////////////////////// // we compute on the leaf grid view - const auto& leafGridView = GridCreator::grid().leafGridView(); + const auto& leafGridView = gridManager.grid().leafGridView(); // create the finite volume grid geometry using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); @@ -163,7 +163,7 @@ int main(int argc, char** argv) try // intialize the vtk output module using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); - VtkOutputModule vtkWriter(*problem, *fvGridGeometry, *gridVariables, x, problem->name()); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); VtkOutputFields::init(vtkWriter); //!< Add model specific output fields vtkWriter.write(0.0); @@ -223,8 +223,7 @@ int main(int argc, char** argv) try DumuxMessage::print(/*firstCall=*/false); } - - + return 0; } catch (Dumux::ParameterException &e) @@ -232,6 +231,7 @@ catch (Dumux::ParameterException &e) std::cerr << std::endl << e << " ---> Abort!" << std::endl; return 1; } + catch (Dune::DGFException & e) { std::cerr << "DGF exception thrown (" << e << @@ -241,11 +241,13 @@ catch (Dune::DGFException & e) << " ---> Abort!" << std::endl; return 2; } + catch (Dune::Exception &e) { std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; return 3; } + catch (...) { std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; diff --git a/lecture/efm/1p2c_2p_2p2c/lens2pproblem.hh b/lecture/efm/1p2c_2p_2p2c/lens2pproblem.hh index 84bd9e4624ae6fb0f1440e832e0a4f0e091daf74..621c344ae8609b77605f60b3ca8d7ae9353e4cf6 100644 --- a/lecture/efm/1p2c_2p_2p2c/lens2pproblem.hh +++ b/lecture/efm/1p2c_2p_2p2c/lens2pproblem.hh @@ -38,8 +38,7 @@ #include "lens2pspatialparams.hh" -namespace Dumux -{ +namespace Dumux { template class LensTwoPProblem; @@ -47,25 +46,26 @@ class LensTwoPProblem; ////////// // Specify the properties for the lens problem ////////// -namespace Properties -{ -NEW_TYPE_TAG(LensTwoPProblem, INHERITS_FROM(TwoP, BoxModel, Lens2pSpatialParams)); +namespace Properties { + +NEW_TYPE_TAG(LensTwoPProblemTypeTag, INHERITS_FROM(TwoP, BoxModel, Lens2pSpatialParamsTypeTag)); // Set the grid type -SET_TYPE_PROP(LensTwoPProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(LensTwoPProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(LensTwoPProblem, Problem, LensTwoPProblem); +SET_TYPE_PROP(LensTwoPProblemTypeTag, Problem, LensTwoPProblem); // Set the fluid system -SET_PROP(LensTwoPProblem, FluidSystem) +SET_PROP(LensTwoPProblemTypeTag, FluidSystem) { using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using WettingPhase = FluidSystems::OnePLiquid >; using NonwettingPhase = FluidSystems::OnePGas >; using type = FluidSystems::TwoPImmiscible; }; -} + +} // end namespace Properties /*! * \ingroup TwoPBoxProblems @@ -108,14 +108,10 @@ class LensTwoPProblem : public PorousMediumFlowProblem using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); - using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); - - using Element = typename GridView::template Codim<0>::Entity; using GlobalPosition = typename Element::Geometry::GlobalCoordinate; - using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); @@ -123,18 +119,13 @@ class LensTwoPProblem : public PorousMediumFlowProblem public: /*! * \brief The constructor - * - * \param timeManager The time manager - * \param gridView The grid view */ LensTwoPProblem(std::shared_ptr fvGridGeometry) : ParentType(fvGridGeometry) { - // this is placed down to the declaration of the private varialbes as it is done in all other cases i have seen eps_ = 3e-6; - // the boundary condition data - lowerPressure_ = getParam("Boundary.LowerPressure"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, LowerPressure); - upperPressure_ = getParam("Boundary.UpperPressure"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, UpperPressure); + lowerPressure_ = getParam("Boundary.LowerPressure"); + upperPressure_ = getParam("Boundary.UpperPressure"); } /*! @@ -142,16 +133,6 @@ public: */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { - return "lens-2p"; - } - /*! * \brief Returns the temperature within the domain. * \param temperature DOC ME! @@ -181,6 +162,7 @@ public: BoundaryTypes values; if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) values.setAllDirichlet(); + else values.setAllNeumann(); @@ -209,6 +191,7 @@ public: values[Indices::pressureIdx] = upperPressure_; values[Indices::saturationIdx] = 0.0; } + else if (onLowerBoundary_(globalPos)) { values[Indices::pressureIdx] = lowerPressure_; @@ -286,7 +269,6 @@ public: // \} private: - static constexpr Scalar eps_ = 3e-6; Scalar episodeLength_; @@ -320,6 +302,7 @@ private: && globalPos[0] < 2.3 - eps_); } }; -} //end namespace -#endif +} //end namespace Dumux + +#endif // DUMUX_LENS2P_PROBLEM_HH diff --git a/lecture/efm/1p2c_2p_2p2c/lens2pspatialparams.hh b/lecture/efm/1p2c_2p_2p2c/lens2pspatialparams.hh index 8e76790d49fea38ee692aa5dcb82894bb54f4021..ba611f4d5a605bc479f0bf2a1e376c8bdf302697 100644 --- a/lecture/efm/1p2c_2p_2p2c/lens2pspatialparams.hh +++ b/lecture/efm/1p2c_2p_2p2c/lens2pspatialparams.hh @@ -26,29 +26,25 @@ #ifndef DUMUX_LENS2P_SPATIALPARAMS_HH #define DUMUX_LENS2P_SPATIALPARAMS_HH -// #include #include #include #include #include #include - - -namespace Dumux -{ +namespace Dumux { //forward declaration template class Lens2pSpatialParams; -namespace Properties -{ +namespace Properties { + // The spatial parameters TypeTag -NEW_TYPE_TAG(Lens2pSpatialParams); +NEW_TYPE_TAG(Lens2pSpatialParamsTypeTag); // Set the spatial parameters -SET_PROP(Lens2pSpatialParams, SpatialParams) +SET_PROP(Lens2pSpatialParamsTypeTag, SpatialParams) { private: using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); @@ -56,25 +52,23 @@ private: public: using type = Lens2pSpatialParams; }; -} + +} // end namespace Properties /** \todo Please doc me! */ template class Lens2pSpatialParams -: public FVSpatialParams> +: public FVSpatialParams> { using GridView = typename FVGridGeometry::GridView; using Element = typename GridView::template Codim<0>::Entity; using FVElementGeometry = typename FVGridGeometry::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; using ThisType = Lens2pSpatialParams; - using ParentType = FVSpatialParams; - static constexpr int dimWorld = GridView::dimensionworld; using GlobalPosition = typename Element::Geometry::GlobalCoordinate; - using EffectiveLaw = RegularizedBrooksCorey; public: @@ -131,6 +125,7 @@ public: { if (isInLens_(element.geometry().center())) return lensK_; + else return outerK_; } @@ -152,6 +147,7 @@ public: { if (isInLens_(element.geometry().center())) return lensPorosity_; + else return outerPorosity_; } @@ -172,6 +168,7 @@ public: // const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; if (isInLense_(element.geometry().center() )) return std::cbrt(lensPorosity_); + else return std::cbrt(outerPorosity_); } @@ -192,6 +189,7 @@ public: { if (isInLens_(element.geometry().center())) return lensMaterialParams_; + else return outerMaterialParams_; } @@ -225,6 +223,7 @@ private: { // points on the lens boundary are considered inside for (int i = 0; i < dimWorld; ++i) + if (globalPos[i] < lensLowerLeft_[i] - eps_ || globalPos[i] > lensUpperRight_[i] + eps_) return false; @@ -243,5 +242,6 @@ private: Scalar eps_ = 1e-6; }; -} // end namespace -#endif +} // end namespace Dumux + +#endif // DUMUX_LENS2P_SPATIALPARAMS_HH diff --git a/lecture/efm/1p2cvs2p/CMakeLists.txt b/lecture/efm/1p2cvs2p/CMakeLists.txt index 9abd249284b15f9c4f411156c9624982370ccc2e..ae85d77ac02a4fb1e1c81b6ba74f66a62b105b0c 100644 --- a/lecture/efm/1p2cvs2p/CMakeLists.txt +++ b/lecture/efm/1p2cvs2p/CMakeLists.txt @@ -5,14 +5,14 @@ add_dumux_test(lens1p2cexercise1 lens1p2cexercise1 lens1p2cexercise1.cc --script fuzzy --files ${CMAKE_SOURCE_DIR}/lecture/references/lens-1p2c-exercise1-reference.vtu ${CMAKE_CURRENT_BINARY_DIR}/lens-1p2c-00101.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/lens1p2cexercise1 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise1.input") + --command "${CMAKE_CURRENT_BINARY_DIR}/lens1p2cexercise1 ${CMAKE_CURRENT_SOURCE_DIR}/exercise1.input -Problem.Name lens-1p2c") add_dumux_test(lens2pexercise1 lens2pexercise1 lens2pexercise1.cc python ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py --script fuzzy --files ${CMAKE_SOURCE_DIR}/lecture/references/lens-2p-exercise1-reference.vtu ${CMAKE_CURRENT_BINARY_DIR}/lens-2p-00101.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/lens2pexercise1 -ParameterFile ${CMAKE_CURRENT_SOURCE_DIR}/exercise1.input") + --command "${CMAKE_CURRENT_BINARY_DIR}/lens2pexercise1 ${CMAKE_CURRENT_SOURCE_DIR}/exercise1.input -Problem.Name lens-2p") # headers for installation and headercheck install(FILES diff --git a/lecture/efm/1p2cvs2p/exercise1.input b/lecture/efm/1p2cvs2p/exercise1.input index f6c73d502669faee864dab72cb9cf82dba2df435..4df2de24501160b325470c869e7a09a759cb1e7f 100644 --- a/lecture/efm/1p2cvs2p/exercise1.input +++ b/lecture/efm/1p2cvs2p/exercise1.input @@ -1,4 +1,4 @@ -[TimeManager] +[TimeLoop] MaxTimeStepSize = 5.0e1 # maximal time step size [s] TEnd = 5.0e3 # end time of the simulation [s] DtInitial = 1e1 # initial time step for the simulation [s] diff --git a/lecture/efm/1p2cvs2p/lens1p2cexercise1.cc b/lecture/efm/1p2cvs2p/lens1p2cexercise1.cc index 2ce8af16e4ade2e07c437a56f84d4388a67533e8..0ebb5a8a3c19d5938707434ddd5f5cd6316b2c12 100644 --- a/lecture/efm/1p2cvs2p/lens1p2cexercise1.cc +++ b/lecture/efm/1p2cvs2p/lens1p2cexercise1.cc @@ -16,10 +16,35 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -#include "config.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include #include "lens1p2cproblem.hh" -#include /*! * \brief Provides an interface for customizing error messages associated with @@ -60,8 +85,155 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// // the main function //////////////////////// -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(LensOnePTwoCProblemTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + ////////////////////////////////////////////////////////////////////// + // try to create a grid (from the given grid file or the input file) + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + problem->setTimeLoop(timeLoop); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = Dumux::NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the Newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + return 0; +} // end main + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(LensOnePTwoCProblem) TypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/efm/1p2cvs2p/lens1p2cproblem.hh b/lecture/efm/1p2cvs2p/lens1p2cproblem.hh index 77782f603fd896ad4c42d90b79602dd01738a569..30ee24865c0abff707f5b1f5a22113bdd9a5431b 100644 --- a/lecture/efm/1p2cvs2p/lens1p2cproblem.hh +++ b/lecture/efm/1p2cvs2p/lens1p2cproblem.hh @@ -25,14 +25,16 @@ #ifndef DUMUX_LENS_1P2C_PROBLEM_HH #define DUMUX_LENS_1P2C_PROBLEM_HH -#include -#include +#include +#include #include +#include +#include +#include #include "../1p2c_2p_2p2c/lens1p2cspatialparams.hh" -namespace Dumux -{ +namespace Dumux { template class LensOnePTwoCProblem; @@ -40,34 +42,35 @@ class LensOnePTwoCProblem; ////////// // Specify the properties for the lens problem ////////// -namespace Properties -{ -NEW_TYPE_TAG(LensOnePTwoCProblem, INHERITS_FROM(BoxOnePTwoC)); +namespace Properties { + +NEW_TYPE_TAG(LensOnePTwoCProblemTypeTag, INHERITS_FROM(OnePNC, BoxModel)); // Set the grid type -SET_TYPE_PROP(LensOnePTwoCProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(LensOnePTwoCProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(LensOnePTwoCProblem, Problem, LensOnePTwoCProblem); +SET_TYPE_PROP(LensOnePTwoCProblemTypeTag, Problem, LensOnePTwoCProblem); // set fluid system -SET_TYPE_PROP(LensOnePTwoCProblem, FluidSystem, H2ON2FluidSystem); +SET_PROP(LensOnePTwoCProblemTypeTag, FluidSystem) +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using H2ON2 = FluidSystems::H2ON2>; + using type = FluidSystems::OnePAdapter; +}; // Set the spatial parameters -SET_TYPE_PROP(LensOnePTwoCProblem, SpatialParams, Lens1p2cSpatialParams); - -// Enable partial reassembly of the jacobian matrix? -SET_BOOL_PROP(LensOnePTwoCProblem, ImplicitEnablePartialReassemble, false); - -// Define whether mole(true) or mass(false) fractions are used -SET_BOOL_PROP(LensOnePTwoCProblem, UseMoles, true); - -// Disable gravity -SET_BOOL_PROP(LensOnePTwoCProblem, ProblemEnableGravity, true); +SET_PROP(LensOnePTwoCProblemTypeTag, SpatialParams) +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); +public: + using type = Lens1p2cSpatialParams; +}; -// Disable Jacobian recycling -SET_BOOL_PROP(LensOnePTwoCProblem, ImplicitEnableJacobianRecycling, false); -} +} // end namespace Properties /*! * \ingroup TwoPBoxProblems @@ -97,85 +100,51 @@ SET_BOOL_PROP(LensOnePTwoCProblem, ImplicitEnableJacobianRecycling, false); * To run the simulation execute the following line in shell: * ./lens_1p2c 50000 100 */ -template -class LensOnePTwoCProblem : public ImplicitPorousMediaProblem +template +class LensOnePTwoCProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum - { - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld, - - // indices of the equations - conti0EqIdx = Indices::conti0EqIdx, - transportEqIdx = Indices::transportEqIdx, - - // indices of the primary variables - pressureIdx = Indices::pressureIdx, - massOrMoleFracIdx = Indices::massOrMoleFracIdx - }; - - 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 GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef Dune::FieldVector GlobalPosition; - typedef typename GET_PROP(TypeTag, ParameterTree) ParameterTree; - typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - + using ParentType = PorousMediumFlowProblem; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); - + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using TimeLoopPtr = std::shared_ptr>; + enum { + H2OIdx = FluidSystem::compIdx(FluidSystem::MultiPhaseFluidSystem::H2OIdx), + N2Idx = FluidSystem::compIdx(FluidSystem::MultiPhaseFluidSystem::N2Idx) + }; public: /*! * \brief The constructor * - * \param timeManager The time manager - * \param gridView The grid view + * \param fvGridGeometry The Finite-Volume-Grid-Geometry */ - LensOnePTwoCProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-8) + LensOnePTwoCProblem( std::shared_ptr fvGridGeometry ) + : ParentType(fvGridGeometry) { FluidSystem::init(); //This overwrites the lens settings made in the spatialparameter file this->spatialParams().setLensCoords({0.8, 2.0}, {4.0, 3.0}); - infiltrationRate_ = getParam("Boundary.InfiltrationRate"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, InfiltrationRate); + infiltrationRate_ = getParam("Boundary.InfiltrationRate"); infiltrationStartTime_= 1.0e-9; //The infiltrations starts always after the first time step! - infiltrationEndTime_= getParam("Boundary.InfiltrationEndTime"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, InfiltrationEndTime); - - episodeLength_ = getParam("TimeManager.EpisodeLength"); // (TypeTag, Scalar, TimeManager, EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); + infiltrationEndTime_= getParam("Boundary.InfiltrationEndTime"); // the boundary condition data - lowerPressure_ = getParam("Boundary.LowerPressure"); //GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, LowerPressure); - upperPressure_ = getParam("Boundary.UpperPressure"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, UpperPressure); - - paraviewOutput_ = ParameterTree::tree().template get("Output.paraviewOutput", true); - if (!paraviewOutput_) - { - // the number of cells in x and y direction - numCells_ = getParam >("Grid.Cells"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, Grid, Cells); - - // Write header for output file - std::ofstream dataFile; - dataFile.open("dumux-out.vgfc"); - // dataFile << "Gridplot" << std::endl; - dataFile << "## This is a DuMuX output for the ViPLab graphics driver. \n"; - dataFile << "## This output file was generated at " << __TIME__ <<", "<< __DATE__<< "\n"; - dataFile << "# x-range " << this->bBoxMin()[0] << " " << this->bBoxMax()[0] << "\n" ; - dataFile << "# y-range " << this->bBoxMin()[1] << " " << this->bBoxMax()[1] << "\n" ; - dataFile << "# x-count " << numCells_[0]+1 << "\n" ; - dataFile << "# y-count " << numCells_[1]+1 << "\n" ; - // dataFile << "# min-color 0 0 0\n"; - // dataFile << "# max-color 255 255 255\n"; - dataFile.close(); - } + lowerPressure_ = getParam("Boundary.LowerPressure"); + upperPressure_ = getParam("Boundary.UpperPressure"); } /*! @@ -183,16 +152,6 @@ public: */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - const std::string name() const - { - return "lens-1p2c"; - } - /*! * \brief Returns the temperature within the domain. * \param temperature DOC ME! @@ -223,13 +182,14 @@ public: BoundaryTypes values; if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) values.setAllDirichlet(); + else values.setAllNeumann(); if (onInlet_(globalPos)) - values.setNeumann(transportEqIdx); + values.setNeumann(N2Idx); - return BoundaryTypes; + return values; } /*! @@ -247,15 +207,13 @@ public: if (onUpperBoundary_(globalPos)) { - values[pressureIdx] = upperPressure_; - values[massOrMoleFracIdx] = 0.0; + values[Indices::pressureIdx] = upperPressure_; } + else if (onLowerBoundary_(globalPos)) { - values[pressureIdx] = lowerPressure_; - values[massOrMoleFracIdx] = 0.0; + values[Indices::pressureIdx] = lowerPressure_; } - // else {values = 0.0;} return values; } @@ -271,11 +229,12 @@ public: */ PrimaryVariables neumannAtPos(const GlobalPosition &globalPos) const { - PrimaryVaribles values(0.0); - // TODO: Apply the new kind of time Management implementation. Or is it maybe already done???? - const Scalar time = this->timeManager().time(); - if (time <= infiltrationEndTime_ && infiltrationStartTime_ <= time && onInlet_(globalPos)) - values[transportEqIdx] = -infiltrationRate_; + PrimaryVariables values(0.0); + + if (time() <= infiltrationEndTime_ && infiltrationStartTime_ <= time() && onInlet_(globalPos)) + values[N2Idx] = -infiltrationRate_; + + return values; } /*! @@ -292,14 +251,10 @@ public: * 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& globalPos) const - { - values = Scalar(0.0); - }*/ - NumEqVector sourceAtPos(const GlobalPosition &globalPos) const - { return NumEqVector(0.0); } + { + return NumEqVector(0.0); + } /*! * \brief Evaluate the initial value for a control volume. @@ -312,63 +267,24 @@ public: */ PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const { - PrimaryVariables values; + PrimaryVariables values(0.0); // no contaminant, hydrostatic pressure - const Scalar depth = this->bBoxMax()[1] - globalPos[1]; - const Scalar height = this->bBoxMax()[1] - this->bBoxMin()[1]; + const Scalar depth = this->fvGridGeometry().bBoxMax()[1] - globalPos[1]; + const Scalar height = this->fvGridGeometry().bBoxMax()[1] - this->fvGridGeometry().bBoxMin()[1]; - values[conti0EqIdx] = upperPressure_ - depth/height*(upperPressure_-lowerPressure_); - values[transportEqIdx] = 0.0; + values[Indices::pressureIdx] = upperPressure_ - depth/height*(upperPressure_-lowerPressure_); return values; } - void writeOutput() - { - if (paraviewOutput_) ParentType::writeOutput(); - else - { - //TODO: This is not finished yet - - const Scalar time = this->timeManager().time(); - if (time<0) return; - - const SolutionVector &sol = this->model().curSol(); - - std::ofstream dataFile; - dataFile.open("dumux-out.vgfc", std::fstream::app); - - dataFile << "# time "<< time <<"\n" ; - dataFile << "# label Concentration \n"; - dataFile << "# min-color 0 0 0\n"; - dataFile << "# max-color 255 255 255\n"; - - for (int j=0; j < numCells_[1]+1; j++) - { - for (int i=0; i < numCells_[0]+1; i++) - { - int currentIdx = i*(numCells_[1]+1)+j; - dataFile << sol[currentIdx][massOrMoleFracIdx]; - if(i != numCells_[0]) // all but last entry - dataFile << " "; - else // write the last entry - dataFile << "\n"; - } - } - dataFile.close(); - } - } - - bool shouldWriteOutput() const + void setTimeLoop(TimeLoopPtr timeLoop) { - return this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); + timeLoop_ = timeLoop; } - void episodeEnd() + Scalar time() const { - this->timeManager().startNextEpisode(episodeLength_); + return timeLoop_->time(); } private: @@ -378,7 +294,7 @@ private: */ bool onLowerBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] < this->bBoxMin()[1] + eps_; + return globalPos[1] < this->fvGridGeometry().bBoxMin()[1] + eps_; } /*! * \brief Returns true if the point is located on the upper boundary @@ -386,7 +302,7 @@ private: */ bool onUpperBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] > this->bBoxMax()[1] - eps_; + return globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_; } /*! @@ -395,13 +311,14 @@ private: */ 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) && (this->bBoxMax()[0]-0.45*width)/width > lambda - && lambda > (this->bBoxMax()[0]-0.55*width)/width); + Scalar width = this->fvGridGeometry().bBoxMax()[0] - this->fvGridGeometry().bBoxMin()[0]; + Scalar lambda = (this->fvGridGeometry().bBoxMax()[0] - globalPos[0])/width; + + return (onUpperBoundary_(globalPos) && (this->fvGridGeometry().bBoxMax()[0]-0.45*width)/width > lambda + && lambda > (this->fvGridGeometry().bBoxMax()[0]-0.55*width)/width); } - Scalar eps_; + Scalar eps_ = 1e-8; Scalar episodeLength_; Scalar upperPressure_; @@ -410,8 +327,10 @@ private: Scalar infiltrationStartTime_; Scalar infiltrationEndTime_; bool paraviewOutput_; + TimeLoopPtr timeLoop_; std::vector numCells_; }; -} //end namespace -#endif +} //end namespace Dumux + +#endif // DUMUX_LENS_1P2C_PROBLEM_HH diff --git a/lecture/efm/1p2cvs2p/lens2pexercise1.cc b/lecture/efm/1p2cvs2p/lens2pexercise1.cc index 6dc240191a001d02e71736039c79ebc3468c58c8..a5aec9e623f3cf3aedc4bad7114e5aa214722ba2 100644 --- a/lecture/efm/1p2cvs2p/lens2pexercise1.cc +++ b/lecture/efm/1p2cvs2p/lens2pexercise1.cc @@ -21,9 +21,34 @@ * * \brief DOC ME! */ -#include "config.h" +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + #include "lens2pproblem.hh" -#include /*! * \brief Provides an interface for customizing error messages associated with @@ -76,8 +101,155 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// // the main function //////////////////////// -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(LensTwoPProblemTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + ////////////////////////////////////////////////////////////////////// + // try to create a grid (from the given grid file or the input file) + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDivisions = getParam("TimeLoop.MaxTimeStepDivisions"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + problem->setTimeLoop(timeLoop); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = Dumux::NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the Newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + return 0; +} // end main +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(LensTwoPProblem) TypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/efm/1p2cvs2p/lens2pproblem.hh b/lecture/efm/1p2cvs2p/lens2pproblem.hh index c3c14b3c0676369e8a47a7985236a5dd33f6c850..cf8710fa2dff748cc6ad2b7888d245550eb90d51 100644 --- a/lecture/efm/1p2cvs2p/lens2pproblem.hh +++ b/lecture/efm/1p2cvs2p/lens2pproblem.hh @@ -25,64 +25,45 @@ #ifndef DUMUX_LENS2P_PROBLEM_HH #define DUMUX_LENS2P_PROBLEM_HH -#include -#include +#include +#include +#include +#include #include -#include -#include +#include + +#include +#include #include "../1p2c_2p_2p2c/lens2pspatialparams.hh" -namespace Dumux -{ +namespace Dumux { -template +template class LensTwoPProblem; ////////// // Specify the properties for the lens problem ////////// -namespace Properties -{ -NEW_TYPE_TAG(LensTwoPProblem, INHERITS_FROM(BoxTwoP, Lens2pSpatialParams)); +namespace Properties { +NEW_TYPE_TAG(LensTwoPProblemTypeTag, INHERITS_FROM(TwoP, BoxModel, Lens2pSpatialParamsTypeTag)); // Set the grid type -SET_TYPE_PROP(LensTwoPProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(LensTwoPProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(LensTwoPProblem, Problem, LensTwoPProblem); +SET_TYPE_PROP(LensTwoPProblemTypeTag, Problem, LensTwoPProblem); -// Set the wetting phase -SET_PROP(LensTwoPProblem, WettingPhase) +// Set the fluid system +SET_PROP(LensTwoPProblemTypeTag, FluidSystem) { -private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: typedef FluidSystems::LiquidPhase > type; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using WettingPhase = FluidSystems::OnePLiquid >; + using NonwettingPhase = FluidSystems::OnePLiquid >; + using type = FluidSystems::TwoPImmiscible; }; -// Set the non-wetting phase -SET_PROP(LensTwoPProblem, NonwettingPhase) -{ -private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: typedef FluidSystems::LiquidPhase > type; -}; - -SET_TYPE_PROP(LensTwoPProblem, FluidSystem, TwoPImmiscibleFluidSystem); - -// Enable partial reassembly of the jacobian matrix? -SET_BOOL_PROP(LensTwoPProblem, ImplicitEnablePartialReassemble, false); - -// Enable reuse of jacobian matrices? -SET_BOOL_PROP(LensTwoPProblem, ImplicitEnableJacobianRecycling, true); - -// Write the solutions of individual newton iterations? -SET_BOOL_PROP(LensTwoPProblem, NewtonWriteConvergence, false); - -// Use forward differences instead of central differences -SET_INT_PROP(LensTwoPProblem, ImplicitNumericDifferenceMethod, +1); - -// Enable gravity -SET_BOOL_PROP(LensTwoPProblem, ProblemEnableGravity, true); -} +} // end namespace Properties /*! * \ingroup TwoPBoxProblems @@ -118,42 +99,36 @@ SET_BOOL_PROP(LensTwoPProblem, ProblemEnableGravity, true); * To run the simulation execute the following line in shell: * ./test_2p -parameterFile test_2p.input */ -template -class LensTwoPProblem : public ImplicitPorousMediaProblem +template +class LensTwoPProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; - typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; - typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; - - enum - { - // primary variable indices - pressureIdx = Indices::pressureIdx, - snIdx = Indices::snIdx, - - // equation indices - contiNEqIdx = Indices::contiNEqIdx, - - // phase indices - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld - }; - - 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 GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef Dune::FieldVector GlobalPosition; + using ParentType = PorousMediumFlowProblem; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using Element = typename GridView::template Codim<0>::Entity; + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + // primary variable indices + static constexpr int pressureIdx = Indices::pressureIdx; + static constexpr int saturationIndex = Indices::saturationIdx; + // equation indices + static constexpr int contiNEqIdx = Indices::conti0EqIdx + FluidSystem::comp1Idx; + // phase indices + static constexpr int wPhaseIdx = Indices::phase0Idx; + static constexpr int nPhaseIdx = Indices::phase1Idx; + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using TimeLoopPtr = std::shared_ptr>; public: /*! @@ -162,25 +137,19 @@ public: * \param timeManager The time manager * \param gridView The grid view */ - LensTwoPProblem(TimeManager &timeManager, - const GridView &gridView) - : ParentType(timeManager, gridView), - eps_(1e-8) + LensTwoPProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { //This overwrites the lens settings made in the spatialparameter file this->spatialParams().setLensCoords({0.8, 2.0}, {4.0, 3.0}); - episodeLength_ = getParam("TimeManager.EpisodeLength"); // GET_RUNTIME_PARAM(TypeTag, Scalar, TimeManager.EpisodeLength); - // TODO: Correct this - this->timeManager().startNextEpisode(episodeLength_); - // the boundary condition data - lowerPressure_ = getParam("Boundary.LowerPressure"); //GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, LowerPressure); - upperPressure_ = getParam(("Boundary.UpperPressure"); //GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, UpperPressure); + lowerPressure_ = getParam("Boundary.LowerPressure"); + upperPressure_ = getParam("Boundary.UpperPressure"); - infiltrationRate_ = getParam("Boundary.InfiltrationRate"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, InfiltrationRate); + infiltrationRate_ = getParam("Boundary.InfiltrationRate"); infiltrationStartTime_= 1.0e-9; //The infiltrations starts always after the first time step! - infiltrationEndTime_= getParam("Boundary.InfiltratinoEndTime"); //GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, InfiltrationEndTime); + infiltrationEndTime_= getParam("Boundary.InfiltrationEndTime"); } /*! @@ -188,14 +157,6 @@ public: */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { return "lens-2p"; } - /*! * \brief Returns the temperature within the domain. * This problem assumes a uniform temperature of 10 degrees Celsius. @@ -224,6 +185,7 @@ public: BoundaryTypes values; if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) values.setAllDirichlet(); + else values.setAllNeumann(); @@ -248,12 +210,13 @@ public: if (onUpperBoundary_(globalPos)) { values[pressureIdx] = upperPressure_; - values[snIdx] = 0.0; + values[saturationIndex] = 0.0; } + else if (onLowerBoundary_(globalPos)) { values[pressureIdx] = lowerPressure_; - values[snIdx] = 0.0; + values[saturationIndex] = 0.0; } return values; @@ -269,12 +232,12 @@ public: * For this method, the \a values parameter stores the mass flux * in normal direction of each phase. Negative values mean influx. */ - PrimaryVariables neumannAtPos( - const GlobalPosition &globalPos) const + + PrimaryVariables neumannAtPos( const GlobalPosition &globalPos) const { PrimaryVariables values(0.0); - const Scalar time = this->timeManager().time(); - if (time <= infiltrationEndTime_ && onInlet_(globalPos)) + + if (time() <= infiltrationEndTime_ && onInlet_(globalPos)) values[contiNEqIdx] = -infiltrationRate_; // kg/(m*s) return values; @@ -300,11 +263,9 @@ public: * generated or annihilate per volume unit. Positive values mean * that mass is created, negative ones mean that it vanishes. */ - PrimaryVariables sourceAtPos(const GlobalPosition &globalPos) const + NumEqVector sourceAtPos(const GlobalPosition &globalPos) const { - PrimaryVariables values (0.0); - return values; - + return NumEqVector(0.0); } /*! @@ -319,32 +280,32 @@ public: PrimaryVariables initialAtPos( const GlobalPosition &globalPos) const { PrimaryVariables values(0.0); + + using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); FluidState fluidState; fluidState.setTemperature(this->temperature()); - fluidState.setPressure(FluidSystem::wPhaseIdx, /*pressure=*/1e5); - fluidState.setPressure(FluidSystem::nPhaseIdx, /*pressure=*/1e5); + fluidState.setPressure(FluidSystem::phase0Idx, /*pressure=*/1e5); + fluidState.setPressure(FluidSystem::phase1Idx, /*pressure=*/1e5); // no DNAPL, hydrostatic pressure - const Scalar depth = this->bBoxMax()[1] - globalPos[1]; - const Scalar height = this->bBoxMax()[1] - this->bBoxMin()[1]; + const Scalar depth = this->fvGridGeometry().bBoxMax()[1] - globalPos[1]; + const Scalar height = this->fvGridGeometry().bBoxMax()[1] - this->fvGridGeometry().bBoxMin()[1]; values[pressureIdx] = upperPressure_ - depth/height*(upperPressure_-lowerPressure_); - values[snIdx] = 0.0; + values[saturationIndex] = 0.0; return values; } // \} - bool shouldWriteOutput() const + void setTimeLoop(TimeLoopPtr timeLoop) { - return this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); + timeLoop_ = timeLoop; } - void episodeEnd() + Scalar time() const { - this->timeManager().startNextEpisode(episodeLength_); + return timeLoop_->time(); } private: @@ -354,7 +315,7 @@ private: */ bool onLowerBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] < this->bBoxMin()[1] + eps_; + return globalPos[1] < this->fvGridGeometry().bBoxMin()[1] + eps_; } /*! * \brief Returns true if the point is located on the upper boundary @@ -362,7 +323,7 @@ private: */ bool onUpperBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] > this->bBoxMax()[1] - eps_; + return globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_; } /*! @@ -371,13 +332,14 @@ private: */ bool onInlet_(const GlobalPosition &globalPos) const { - const Scalar width = this->bBoxMax()[0] - this->bBoxMin()[0]; - const Scalar lambda = (this->bBoxMax()[0] - globalPos[0])/width; - return (onUpperBoundary_(globalPos) && (this->bBoxMax()[0]-0.45*width)/width > lambda + eps_ - && (this->bBoxMax()[0]-0.55*width)/width < lambda - eps_); + const Scalar width = this->fvGridGeometry().bBoxMax()[0] - this->fvGridGeometry().bBoxMin()[0]; + const Scalar lambda = (this->fvGridGeometry().bBoxMax()[0] - globalPos[0])/width; + + return (onUpperBoundary_(globalPos) && (this->fvGridGeometry().bBoxMax()[0]-0.45*width)/width > lambda + eps_ + && (this->fvGridGeometry().bBoxMax()[0]-0.55*width)/width < lambda - eps_); } - Scalar eps_; + static constexpr Scalar eps_ = 1e-8; Scalar episodeLength_; Scalar upperPressure_; @@ -385,7 +347,9 @@ private: Scalar infiltrationRate_; Scalar infiltrationStartTime_; Scalar infiltrationEndTime_; + TimeLoopPtr timeLoop_; }; -} //end namespace -#endif +} //end namespace Dumux + +#endif // DUMUX_LENS2P_PROBLEM_HH diff --git a/lecture/efm/2p/lens2pexercise2.cc b/lecture/efm/2p/lens2pexercise2.cc index 6dc240191a001d02e71736039c79ebc3468c58c8..8037b5d454c7f70bbd360989371b9ca4284a6ad4 100755 --- a/lecture/efm/2p/lens2pexercise2.cc +++ b/lecture/efm/2p/lens2pexercise2.cc @@ -23,7 +23,31 @@ */ #include "config.h" #include "lens2pproblem.hh" -#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include /*! * \brief Provides an interface for customizing error messages associated with @@ -76,8 +100,159 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// // the main function //////////////////////// -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + // typedef TTAG(LensOnePTwoCProblem) TypeTag; + // return Dumux::start(argc, argv, usage); + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(LensTwoPProblemTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + ////////////////////////////////////////////////////////////////////// + // try to create a grid (from the given grid file or the input file) + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDivisions = getParam("TimeLoop.MaxTimeStepDivisions"); // Q: What is this? Do we need this here? + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) // Fehlt ebenso + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + problem->setTimeLoop(timeLoop); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = Dumux::NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the Newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + + +} + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(LensTwoPProblem) TypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/efm/2p/lens2pexercise2.input b/lecture/efm/2p/lens2pexercise2.input index 6cdbee5aa1b7e0ddddcc53e2a4b1b00dc935449b..1087b4304f9abb272ab79086e48bda56dcc9a465 100755 --- a/lecture/efm/2p/lens2pexercise2.input +++ b/lecture/efm/2p/lens2pexercise2.input @@ -1,4 +1,4 @@ -[TimeManager] +[TimeLoop] MaxTimeStepSize = 1.0e5 # maximal time step size [s] TEnd = 5.0e6 # end time of the simulation [s] DtInitial = 1e2 # initial time step for the simulation [s] @@ -28,3 +28,6 @@ LowerPressure = 1.19612e5 # Dirichlet pressure value for the b UpperPressure = 1.0e5 # Dirichlet pressure value for the boundary condition at the upper boundary [Pa] InfiltrationRate = 0.01 # infiltration rate [kg/(ms)] InfiltrationEndTime = 10000 # time when the infiltration process will stop [s] + +[Problem] +Name = lens-2p diff --git a/lecture/efm/2p/lens2pproblem.hh b/lecture/efm/2p/lens2pproblem.hh index d8b86e9ee04abeb7998d7656e06eb74e96fd6a67..bbefdcd9faababcd08828d9c757350e68a8415bb 100644 --- a/lecture/efm/2p/lens2pproblem.hh +++ b/lecture/efm/2p/lens2pproblem.hh @@ -25,57 +25,47 @@ #ifndef DUMUX_LENS2P_PROBLEM_HH #define DUMUX_LENS2P_PROBLEM_HH -#include -#include +#include +#include #include -#include -#include +#include + +#include + +#include #include +#include + +#include +#include #include "../1p2c_2p_2p2c/lens2pspatialparams.hh" -namespace Dumux -{ +namespace Dumux { -template +template class LensTwoPProblem; -////////// -// Specify the properties for the lens problem -////////// - +namespace Properties { -namespace Properties -{ -NEW_TYPE_TAG(LensTwoPProblem, INHERITS_FROM(BoxTwoP, Lens2pSpatialParams)); +NEW_TYPE_TAG(LensTwoPProblemTypeTag, INHERITS_FROM(TwoP, BoxModel, Lens2pSpatialParamsTypeTag)); // Set the grid type -SET_TYPE_PROP(LensTwoPProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(LensTwoPProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(LensTwoPProblem, Problem, LensTwoPProblem); - -// Set the wetting phase -SET_PROP(LensTwoPProblem, WettingPhase) -{ -private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - -public: typedef FluidSystems::LiquidPhase > type; -}; +SET_TYPE_PROP(LensTwoPProblemTypeTag, Problem, LensTwoPProblem); -// Set the non-wetting phase -SET_PROP(LensTwoPProblem, NonwettingPhase) +SET_PROP(LensTwoPProblemTypeTag, FluidSystem) { -private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - -public: typedef FluidSystems::LiquidPhase > type; + using Scalar = typename GET_PROP_TYPE(TypeTag,Scalar); + using WettingPhase = FluidSystems::OnePLiquid >; + using NonwettingPhase = FluidSystems::OnePLiquid >; + using type = FluidSystems::TwoPImmiscible; }; -SET_TYPE_PROP(LensTwoPProblem, FluidSystem, TwoPImmiscibleFluidSystem); +} // end namespace Properties -// Enable gravity -SET_BOOL_PROP(LensTwoPProblem, ProblemEnableGravity, true); -} /*! * \ingroup TwoPBoxProblems * \brief Soil decontamination problem where DNAPL infiltrates a fully @@ -107,95 +97,69 @@ SET_BOOL_PROP(LensTwoPProblem, ProblemEnableGravity, true); * To run the simulation execute the following line in shell: * ./lens_2p 50000 100 */ -template -class LensTwoPProblem : public ImplicitPorousMediaProblem +template +class LensTwoPProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; - typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; - typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; - // TODO: get all using-directives which have to be defined - // New (kaiw): - using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); - using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); - - enum - { - // primary variable indices - pressureIdx = Indices::pressureIdx, - snIdx = Indices::snIdx, - - // equation indices - contiNEqIdx = Indices::contiNEqIdx, - - // phase indices - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld - }; - - 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 GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; - - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef Dune::FieldVector GlobalPosition; - typedef typename GET_PROP(TypeTag, ParameterTree) Params; + using ParentType = PorousMediumFlowProblem; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + // primary variable indices + static constexpr int pressureIdx = Indices::pressureIdx; + static constexpr int saturationIdx = Indices::saturationIdx; + // equation indices + static constexpr int contiNEqIdx = Indices::conti0EqIdx + FluidSystem::comp1Idx; + // phase indices + static constexpr int wPhaseIdx = Indices::phase0Idx; + static constexpr int nPhaseIdx = Indices::phase1Idx; + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + using TimeLoopPtr = std::shared_ptr>; public: /*! * \brief The constructor - * - * \param timeManager The time manager - * \param gridView The grid view + * DOC ME! */ - LensTwoPProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-6) + LensTwoPProblem( std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - episodeLength_ = getParam("TimeManager.EpisodeLength"); //GET_RUNTIME_PARAM(TypeTag, Scalar, TimeManager.EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); - - //this overwrites the settings in the spatialparameters file! - this->spatialParams().setLensCoords({0.0, 0.5}, {3.0, 1.0}); + episodeLength_ = getParam("TimeLoop.EpisodeLength"); // the boundary condition data - lowerPressure_ = getParam("Boundary.LowerPressure"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, LowerPressure); - upperPressure_ = getParam("Boundary.UpperPressure"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, UpperPressure); + lowerPressure_ = getParam("Boundary.LowerPressure"); + upperPressure_ = getParam("Boundary.UpperPressure"); // infiltration parameters - infiltrationRate_ = getParam("Boundary.InfiltrationRate"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, InfiltrationRate); + infiltrationRate_ = getParam("Boundary.InfiltrationRate"); infiltrationStartTime_= 1.0e-9; //The infiltrations starts always after the first time step! - infiltrationEndTime_= getParam("Boundary.InfiltrationEndTime"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Boundary, InfiltrationEndTime); + infiltrationEndTime_= getParam("Boundary.InfiltrationEndTime"); - paraviewOutput_ = Params::tree().template get("Output.paraviewOutput", true); + paraviewOutput_ = getParam("Output.paraviewOutput", true); if (!paraviewOutput_) { - numCells_ = getParam >("Grid.Cells"); // GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, Grid, Cells); + numCells_ = getParam >("Grid.Cells"); // Write header for output file std::ofstream dataFile; dataFile.open("dumux-out.vgfc"); - // dataFile << "Gridplot" << std::endl; dataFile << "## This is a DuMuX output for the ViPLab graphics driver. \n"; dataFile << "## This output file was generated at " << __TIME__ <<", "<< __DATE__<< "\n"; - dataFile << "# x-range " << this->bBoxMin()[0] << " " << this->bBoxMax()[0] << "\n" ; - dataFile << "# y-range " << this->bBoxMin()[1] << " " << this->bBoxMax()[1] << "\n" ; + dataFile << "# x-range " << this->fvGridGeometry().bBoxMin()[0] << " " << this->fvGridGeometry().bBoxMax()[0] << "\n" ; + dataFile << "# y-range " << this->fvGridGeometry().bBoxMin()[1] << " " << this->fvGridGeometry().bBoxMax()[1] << "\n" ; dataFile << "# x-count " << numCells_[0]+1 << "\n" ; dataFile << "# y-count " << numCells_[1]+1 << "\n" ; - // dataFile << "# min-color 0 0 0\n"; - // dataFile << "# max-color 255 255 255\n"; dataFile.close(); } - } /*! @@ -203,14 +167,6 @@ public: */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { return "lens-2p"; } - /*! * \brief Returns the temperature within the domain. * @@ -235,26 +191,12 @@ public: * \param values The boundary types for the conservation equations * \param globalPos The position of the center of the finite volume */ - - - /* void boundaryTypesAtPos(BoundaryTypes &values, - const GlobalPosition &globalPos) const - { - if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) - values.setAllDirichlet(); - else - values.setAllNeumann(); - - if (onInlet_(globalPos)) - values.setNeumann(contiNEqIdx); - }*/ - - // Neu (kaiw): - BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { BoundaryTypes bcTypes; if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) bcTypes.setAllDirichlet(); + else bcTypes.setAllNeumann(); @@ -272,29 +214,27 @@ public: * * For this method, the \a values parameter stores primary variables. */ - PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - PrimaryVariables Values(0.0); + PrimaryVariables values(0.0); FluidState fluidState; fluidState.setTemperature(this->temperature()); - fluidState.setPressure(FluidSystem::wPhaseIdx, /*pressure=*/1e5); - fluidState.setPressure(FluidSystem::nPhaseIdx, /*pressure=*/1e5); + fluidState.setPressure(FluidSystem::phase0Idx, /*pressure=*/1e5); + fluidState.setPressure(FluidSystem::phase1Idx, /*pressure=*/1e5); if (onUpperBoundary_(globalPos)) { values[pressureIdx] = upperPressure_; - values[snIdx] = 0.0; + values[saturationIdx] = 0.0; } + else if (onLowerBoundary_(globalPos)) { values[pressureIdx] = lowerPressure_; - values[snIdx] = 0.0; + values[saturationIdx] = 0.0; } - /* else // kaiw: This is not necessary anymore because of the definition in line 278 where the object values is defined. - values = 0.0;*/ - return Values; + return values; } /*! @@ -313,9 +253,7 @@ public: { PrimaryVariables values(0.0); - // TODO: Find out, how the TimeManager is now implemented and change this paragraph - const Scalar time = this->timeManager().time(); - if (time >= infiltrationStartTime_ && time <= infiltrationEndTime_ && onInlet_(globalPos)) + if (time() >= infiltrationStartTime_ && time() <= infiltrationEndTime_ && onInlet_(globalPos)) values[contiNEqIdx] = -infiltrationRate_; // kg/(m*s) return values; @@ -340,15 +278,11 @@ public: * generated or annihilate per volume unit. Positive values mean * that mass is created, negative ones mean that it vanishes. */ - - // kaiw: changed - PointSource sourceAtPos(const GlobalPosition &globalPos) const + NumEqVector sourceAtPos(const GlobalPosition &globalPos) const { - PointSource ps; - return ps; + return NumEqVector(0.0); } - /*! * \brief Evaluate the initial value for a control volume. * @@ -363,69 +297,27 @@ public: PrimaryVariables values; FluidState fluidState; fluidState.setTemperature(this->temperature()); - fluidState.setPressure(FluidSystem::wPhaseIdx, /*pressure=*/1e5); - fluidState.setPressure(FluidSystem::nPhaseIdx, /*pressure=*/1e5); + fluidState.setPressure(FluidSystem::phase0Idx, /*pressure=*/1e5); + fluidState.setPressure(FluidSystem::phase1Idx, /*pressure=*/1e5); // no DNAPL, hydrostatic pressure - const Scalar depth = this->bBoxMax()[1] - globalPos[1]; - const Scalar height = this->bBoxMax()[1] - this->bBoxMin()[1]; + const Scalar depth = this->fvGridGeometry().bBoxMax()[1] - globalPos[1]; + const Scalar height = this->fvGridGeometry().bBoxMax()[1] - this->fvGridGeometry().bBoxMin()[1]; values[pressureIdx] = upperPressure_ - depth/height*(upperPressure_-lowerPressure_); - values[snIdx] = 0.0; + values[saturationIdx] = 0.0; return values; } - void writeOutput() - { - if (paraviewOutput_) ParentType::writeOutput(); - else - { - //TODO: This is not finished yet - - const Scalar time = this->timeManager().time(); - if (time<0) return; - - const SolutionVector &sol = this->model().curSol(); - - std::ofstream dataFile; - dataFile.open("dumux-out.vgfc", std::fstream::app); - - dataFile << "# time "<< time <<"\n" ; - dataFile << "# label Concentration \n"; - dataFile << "# min-color 0 0 0\n"; - dataFile << "# max-color 255 255 255\n"; - - for (int j=0; j < numCells_[1]+1; j++) - { - for (int i=0; i < numCells_[0]+1; i++) - { - int currentIdx = i*(numCells_[1]+1)+j; - dataFile << sol[currentIdx][snIdx]; - if(i != numCells_[0]) // all but last entry - dataFile << " "; - else // write the last entry - { - dataFile << "\n"; - } - } - } - dataFile.close(); - } - } - // \} - - bool shouldWriteOutput() const + void setTimeLoop(TimeLoopPtr timeLoop) { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); + timeLoop_ = timeLoop; } - void episodeEnd() + Scalar time() const { - this->timeManager().startNextEpisode(episodeLength_); + return timeLoop_->time(); } private: @@ -435,7 +327,7 @@ private: */ bool onLowerBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] < this->bBoxMin()[1] + eps_; + return globalPos[1] < this->fvGridGeometry().bBoxMin()[1] + eps_; } /*! * \brief Returns true if the point is located on the upper boundary @@ -443,7 +335,7 @@ private: */ bool onUpperBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] > this->bBoxMax()[1] - eps_; + return globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_; } /*! @@ -454,13 +346,15 @@ private: */ bool onInlet_(const GlobalPosition &globalPos) const { - const Scalar width = this->bBoxMax()[0] - this->bBoxMin()[0]; - const Scalar lambda = (this->bBoxMax()[0] - globalPos[0])/width; - return (onUpperBoundary_(globalPos) && (this->bBoxMax()[0]-0.40*width)/width > lambda + eps_ - && (this->bBoxMax()[0]-0.60*width)/width < lambda - eps_); + const Scalar width = this->fvGridGeometry().bBoxMax()[0] - this->fvGridGeometry().bBoxMin()[0]; + const Scalar lambda = (this->fvGridGeometry().bBoxMax()[0] - globalPos[0])/width; + + return (onUpperBoundary_(globalPos) && (this->fvGridGeometry().bBoxMax()[0]-0.40*width)/width > lambda + eps_ + && (this->fvGridGeometry().bBoxMax()[0]-0.60*width)/width < lambda - eps_); } - Scalar eps_; + static constexpr Scalar eps_ = 1e-6; + Scalar episodeLength_; Scalar upperPressure_; @@ -468,9 +362,13 @@ private: Scalar infiltrationRate_; Scalar infiltrationStartTime_; Scalar infiltrationEndTime_; + bool paraviewOutput_; + std::vector numCells_; + TimeLoopPtr timeLoop_; }; -} //end namespace -#endif +} //end namespace Dumux + +#endif // DUMUX_LENS2P_PROBLEM_HH diff --git a/lecture/mhs/groundwater/groundwater.cc b/lecture/mhs/groundwater/groundwater.cc index eac2c87fbac3358d17ae698f60b027d6ec4a32d0..54a4ca70d67d58d3ace671f67c9a6dd2d9675eb8 100644 --- a/lecture/mhs/groundwater/groundwater.cc +++ b/lecture/mhs/groundwater/groundwater.cc @@ -25,9 +25,11 @@ */ #include -#include "groundwaterproblem.hh" +#include #include +#include "groundwaterproblem.hh" + /*! * \brief Provides an interface for customizing error messages associated with * reading in parameters. @@ -59,6 +61,6 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// int main(int argc, char** argv) { - typedef TTAG(GroundwaterProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + using TypeTag = TTAG(GroundwaterProblemTypeTag); + return Dumux::start(argc, argv, usage); } diff --git a/lecture/mhs/groundwater/groundwater.input b/lecture/mhs/groundwater/groundwater.input index 189eea19b67c2379a52e3140e0540d45783d661b..7cce56abc28d6c51759a7ad1cc52c8390d041ef7 100644 --- a/lecture/mhs/groundwater/groundwater.input +++ b/lecture/mhs/groundwater/groundwater.input @@ -23,6 +23,7 @@ Left = " " Right =" + " Bottom=" @@ -30,6 +31,7 @@ Bottom=" " Top=" + " [SpatialParams] @@ -39,7 +41,6 @@ Permeability = 1e-4 # the background permeability k_f [m/s] # Insert in the following format: # Left Right Bottom Top Permeability - Lenses = " 20 50 50 80 1e-2 " diff --git a/lecture/mhs/groundwater/groundwaterproblem.hh b/lecture/mhs/groundwater/groundwaterproblem.hh index 033614b19e54ed1eb63b317a58f4223985ad6824..ed114e0cb4e9888d8d05b86f1a9f9e9950946ecc 100644 --- a/lecture/mhs/groundwater/groundwaterproblem.hh +++ b/lecture/mhs/groundwater/groundwaterproblem.hh @@ -27,13 +27,12 @@ #include #include #include -#include +#include #include #include "groundwaterspatialparams.hh" -namespace Dumux -{ +namespace Dumux { template class GroundwaterProblem; @@ -41,31 +40,26 @@ class GroundwaterProblem; ////////// // Specify the properties ////////// -namespace Properties -{ -NEW_TYPE_TAG(GroundwaterProblem, INHERITS_FROM(FVPressureOneP)); +namespace Properties { +NEW_TYPE_TAG(GroundwaterProblemTypeTag, INHERITS_FROM(FVPressureOneP)); // Set the grid type -SET_TYPE_PROP(GroundwaterProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(GroundwaterProblemTypeTag, Grid, Dune::YaspGrid<2>); -// Set the wetting phase -SET_PROP(GroundwaterProblem, Fluid) +// Set the fluid system +SET_PROP(GroundwaterProblemTypeTag, FluidSystem) { -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef FluidSystems::LiquidPhase > type; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = FluidSystems::OnePLiquid >; }; // Set the spatial parameters -SET_TYPE_PROP(GroundwaterProblem, SpatialParams, Dumux::GroundwaterSpatialParams); - -// Enable gravity -SET_BOOL_PROP(GroundwaterProblem, ProblemEnableGravity, false); +SET_TYPE_PROP(GroundwaterProblemTypeTag, SpatialParams, Dumux::GroundwaterSpatialParams); //Set the problem -SET_TYPE_PROP(GroundwaterProblem, Problem, Dumux::GroundwaterProblem); -} +SET_TYPE_PROP(GroundwaterProblemTypeTag, Problem, Dumux::GroundwaterProblem); + +} // end namespace Properties /*! * \ingroup MHS @@ -73,12 +67,13 @@ SET_TYPE_PROP(GroundwaterProblem, Problem, Dumux::GroundwaterProblem); * \brief Dumux-equivalent for GRUWA (1p-stationary, finite volumes) */ template -class GroundwaterProblem: public DiffusionProblem1P +class GroundwaterProblem : public DiffusionProblem1P { typedef DiffusionProblem1P ParentType; typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Fluid) Fluid; + typedef typename GridView::Grid Grid; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; typedef typename GET_PROP_TYPE(TypeTag, CellData) CellData; @@ -116,16 +111,16 @@ class GroundwaterProblem: public DiffusionProblem1P }; public: - GroundwaterProblem(TimeManager& timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), velocity_(*this) + GroundwaterProblem(TimeManager& timeManager, Grid& grid) + : ParentType(timeManager, grid), velocity_(*this) { - Fluid::Component::setViscosity(0.001); - Fluid::Component::setDensity(1000); + FluidSystem::Component::setViscosity(0.001); + FluidSystem::Component::setDensity(1000); // Read input parameters and write them into private variables - domainSize_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, GlobalPosition, Grid, UpperRight); - geometryDepth_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, Depth); - numCells_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, ResolutionVector, Grid, Cells); + domainSize_ = getParam("Grid.UpperRight"); + geometryDepth_ = getParam("Grid.Depth"); + numCells_ = getParam("Grid.Cells"); // check input for too large grid size if (numCells_[0]*numCells_[1] > 10000) @@ -141,7 +136,7 @@ public: } // Read sources - std::vector sources = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, Source, Sources); + auto sources = getParam>("Source.Sources"); unsigned int numSources = std::trunc(sources.size()/3); for (int sourceCount = 0; sourceCount < numSources; sourceCount++) @@ -158,7 +153,7 @@ public: } // Read boundary conditions - std::vector bc = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, BoundaryConditions, Left); + auto bc = getParam>("BoundaryConditions.Left"); int NumberOfSegments = std::trunc(bc.size()/4); for (int segmentCount = 0; segmentCount < NumberOfSegments; segmentCount++) { @@ -169,7 +164,7 @@ public: tempSegment.value = bc[segmentCount*4+3]; boundaryConditions_[2].push_back(tempSegment); } - bc = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, BoundaryConditions, Right); + bc = getParam>("BoundaryConditions.Right"); NumberOfSegments = std::trunc(bc.size()/4); for (int segmentCount = 0; segmentCount < NumberOfSegments; segmentCount++) { @@ -180,7 +175,7 @@ public: tempSegment.value = bc[segmentCount*4+3]; boundaryConditions_[3].push_back(tempSegment); } - bc = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, BoundaryConditions, Bottom); + bc = getParam>("BoundaryConditions.Bottom"); NumberOfSegments = std::trunc(bc.size()/4); for (int segmentCount = 0; segmentCount < NumberOfSegments; segmentCount++) { @@ -191,7 +186,7 @@ public: tempSegment.value = bc[segmentCount*4+3]; boundaryConditions_[1].push_back(tempSegment); } - bc = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, BoundaryConditions, Top); + bc = getParam>("BoundaryConditions.Top"); NumberOfSegments = std::trunc(bc.size()/4); for (int segmentCount = 0; segmentCount < NumberOfSegments; segmentCount++) { @@ -242,7 +237,7 @@ public: void source(PrimaryVariables& values, const Element& element) const { values = 0; - Scalar density=Fluid::density(0.0,0.0); + Scalar density=FluidSystem::density(0.0,0.0); for (unsigned int sourceCount = 0; sourceCount != sources_.size(); sourceCount++) { if (this->variables().index(element) == sources_[sourceCount].index) @@ -340,7 +335,7 @@ public: if ((boundaryConditions_[boundaryIndex][segmentCount].from < coordinate) && (coordinate < boundaryConditions_[boundaryIndex][segmentCount].to)) { - values = boundaryConditions_[boundaryIndex][segmentCount].value*Fluid::density(0.0,0.0)*9.81; + values = boundaryConditions_[boundaryIndex][segmentCount].value*FluidSystem::density(0.0,0.0)*9.81; return; } } @@ -384,7 +379,7 @@ public: if ((boundaryConditions_[boundaryIndex][segmentCount].from < coordinate) && (coordinate < boundaryConditions_[boundaryIndex][segmentCount].to)) { - values = boundaryConditions_[boundaryIndex][segmentCount].value*Fluid::density(0.0,0.0)*(-1); + values = boundaryConditions_[boundaryIndex][segmentCount].value*FluidSystem::density(0.0,0.0)*(-1); return; } } @@ -400,12 +395,12 @@ public: { ParentType::writeOutput(); Scalar zmax, zmin; - zmax=this->variables().cellData(0).pressure()/(Fluid::density(0.0,0.0)*9.81); - zmin=this->variables().cellData(0).pressure()/(Fluid::density(0.0,0.0)*9.81); + zmax=this->variables().cellData(0).pressure()/(FluidSystem::density(0.0,0.0)*9.81); + zmin=this->variables().cellData(0).pressure()/(FluidSystem::density(0.0,0.0)*9.81); for (int i = 0; i < numCells_[0]*numCells_[1]; i++) { - Scalar currentHead= this->variables().cellData(i).pressure()/(Fluid::density(0.0,0.0)*9.81); + Scalar currentHead= this->variables().cellData(i).pressure()/(FluidSystem::density(0.0,0.0)*9.81); zmax = std::max(currentHead,zmax); zmin = std::min(currentHead,zmin); } @@ -433,7 +428,7 @@ public: for (int j = 0; j < numCells_[0]; j++) { int currentIdx = i*numCells_[0]+j; - dataFile << this->variables().cellData(currentIdx).pressure()/(Fluid::density(0.0,0.0)*9.81); + dataFile << this->variables().cellData(currentIdx).pressure()/(FluidSystem::density(0.0,0.0)*9.81); if(j != numCells_[0]-1) // all but last entry dataFile << " "; else // write the last entry @@ -460,7 +455,7 @@ public: if (std::abs(v_x) < 1e-17) v_x = 0; if (std::abs(v_y) < 1e-17) v_y = 0; - piezo = cellData.pressure()/(Fluid::density(0.0,0.0)*9.81); + piezo = cellData.pressure()/(FluidSystem::density(0.0,0.0)*9.81); x = element.geometry().center()[0]; y = element.geometry().center()[1]; @@ -479,6 +474,7 @@ private: Scalar geometryDepth_; Dumux::FVVelocity velocity_; }; + } //end namespace Dumux #endif //DUMUX_LECTURE_MHS_GROUNDWATER_PROBLEM_HH diff --git a/lecture/mhs/groundwater/groundwaterspatialparams.hh b/lecture/mhs/groundwater/groundwaterspatialparams.hh index 39c20b6f61dcb0ca0ecff28d792877a28725568f..d388d4d67c2aaf28c9c027ba8a9cd45a3cbc7ad0 100644 --- a/lecture/mhs/groundwater/groundwaterspatialparams.hh +++ b/lecture/mhs/groundwater/groundwaterspatialparams.hh @@ -22,7 +22,7 @@ #ifndef DUMUX_LECTURE_MHS_GROUNDWATER_SPATIALPARAMS_HH #define DUMUX_LECTURE_MHS_GROUNDWATER_SPATIALPARAMS_HH -#include +#include namespace Dumux { @@ -32,9 +32,10 @@ namespace Dumux * \brief spatial parameters for the test problem for diffusion models. */ template -class GroundwaterSpatialParams: public FVSpatialParamsOneP +class GroundwaterSpatialParams: public SequentialFVSpatialParamsOneP { typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; enum @@ -62,17 +63,17 @@ class GroundwaterSpatialParams: public FVSpatialParamsOneP public: - GroundwaterSpatialParams(const GridView& gridView) : FVSpatialParamsOneP(gridView) + GroundwaterSpatialParams(const Problem& problem) : SequentialFVSpatialParamsOneP(problem) { // factor converting K_f to instrinsic permeability const Scalar permFactor = 0.001/(1000*9.81); // the background permeability - auto kii = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, Permeability)*permFactor; + auto kii = getParam("SpatialParams.Permeability")*permFactor; permeability_ = {{kii, 0}, {0, kii}}; // Read the lenses - std::vector lenses = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, SpatialParams, Lenses); + auto lenses = getParam>("SpatialParams.Lenses"); unsigned int numLenses = std::trunc(lenses.size()/5); // make it robust by truncating for (unsigned int lensIdx = 0; lensIdx < numLenses; ++lensIdx) diff --git a/lecture/mm/CMakeLists.txt b/lecture/mm/CMakeLists.txt index 690c8498082950c1afeef7f3a0dc3dfd0cd7b7a8..efe19ceacd5ff8a87f600bb82c99fcfaddf7c9af 100644 --- a/lecture/mm/CMakeLists.txt +++ b/lecture/mm/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(co2plume) add_subdirectory(columnxylene) add_subdirectory(convectivemixing) add_subdirectory(fuelcell) +add_subdirectory(fractures) add_subdirectory(heatpipe) add_subdirectory(heavyoil) add_subdirectory(henryproblem) diff --git a/lecture/mm/buckleyleverett/buckleyleverettanalyticsolution.hh b/lecture/mm/buckleyleverett/buckleyleverettanalyticsolution.hh index b80a6a537871cc915e0acad0d55fd7f2ad58631f..d62540ecbdd76970b3b8b144d377726dc208efc3 100644 --- a/lecture/mm/buckleyleverett/buckleyleverettanalyticsolution.hh +++ b/lecture/mm/buckleyleverett/buckleyleverettanalyticsolution.hh @@ -23,15 +23,13 @@ #include #include - /** * @file * @brief Analytical solution of the buckley-leverett problem * @author Markus Wolff */ -namespace Dumux -{ +namespace Dumux { /** * \ingroup fracflow * @brief IMplicit Pressure Explicit Saturation (IMPES) scheme for the solution of @@ -67,36 +65,26 @@ struct CheckMaterialLaw > > template class BuckleyLeverettAnalytic { - typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - - typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; - typedef typename SpatialParams::MaterialLaw MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - typedef typename GET_PROP_TYPE(TypeTag, CellData) CellData; - - enum - { - dim = GridView::dimension, dimworld = GridView::dimensionworld - }; - enum - { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - satEqIdx = Indices::satEqIdx, - }; - - typedef Dune::BlockVector > BlockVector; - typedef typename GridView::Traits::template Codim<0>::Entity Element; - typedef typename GridView::template Codim<0>::Iterator ElementIterator; - typedef Dune::FieldVector GlobalPosition; + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Grid = typename GET_PROP_TYPE(TypeTag, Grid); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using SpatialParams = typename GET_PROP_TYPE(TypeTag, SpatialParams); + using MaterialLaw = typename SpatialParams::MaterialLaw; + using MaterialLawParams = typename MaterialLaw::Params; + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); + using CellData = typename GET_PROP_TYPE(TypeTag, CellData); + static constexpr int dim = GridView::dimension; + static constexpr int dimworld = GridView::dimensionworld; + static constexpr int wPhaseIdx = Indices::wPhaseIdx; + static constexpr int nPhaseIdx = Indices::nPhaseIdx; + static constexpr int satEqIdx = Indices::satEqIdx; + using BlockVector = Dune::BlockVector >; + using Element = typename GridView::Traits::template Codim<0>::Entity; + using ElementIterator = typename GridView::template Codim<0>::Iterator; + using GlobalPosition = Dune::FieldVector; public: @@ -110,9 +98,8 @@ public: error_ = 0; elementVolume_.resize(size_); elementVolume_ = 0; - - return; } + /*! * \brief DOC ME! * \param materialLawParams DOC ME! @@ -124,15 +111,13 @@ public: swr_ = materialLawParams.swr(); snr_ = materialLawParams.snr(); porosity_ = problem_.spatialParams().porosity(dummyElement_); - time_ = 0; - satVec_ = swr_; + for (int i = 1; i < pointNum_; i++) { satVec_[i] = satVec_[i - 1] + (1 - snr_ - swr_) / intervalNum_; } -// std::cout<<"satVec_ = "< satVec_; Dune::FieldVector fractionalW_; Dune::FieldVector dfwdsw_; @@ -421,5 +369,7 @@ private: GlobalPosition dummyGlobal_; }; -} -#endif + +} // end namespace Dumux + +#endif // DUMUX_BUCKLEYLEVERETT_ANALYTICAL_HH diff --git a/lecture/mm/buckleyleverett/buckleyleverettexercise.cc b/lecture/mm/buckleyleverett/buckleyleverettexercise.cc index 942d733cb2b8f85781494359705e4edfd1114d5a..f40882ba14fb6e9ccef8c0b816740f730c9c68c3 100644 --- a/lecture/mm/buckleyleverett/buckleyleverettexercise.cc +++ b/lecture/mm/buckleyleverett/buckleyleverettexercise.cc @@ -65,6 +65,6 @@ void usage(const char *progName, const std::string &errorMsg) int main(int argc, char** argv) { - typedef TTAG(BuckleyLeverettProblem) TypeTag; + typedef TTAG(BuckleyLeverettProblemTypeTag) TypeTag; return Dumux::start(argc, argv, usage); } diff --git a/lecture/mm/buckleyleverett/buckleyleverettexercise.input b/lecture/mm/buckleyleverett/buckleyleverettexercise.input index 96b17188f9c04f889a1729e5676586936fe1a83a..4c6af3cfcf4161c9cc170e86baca6553e4dc3911 100644 --- a/lecture/mm/buckleyleverett/buckleyleverettexercise.input +++ b/lecture/mm/buckleyleverett/buckleyleverettexercise.input @@ -2,6 +2,9 @@ TEnd = 2e7 # end time of the simulation [s] DtInitial = 1e3 # initial time step for the simulation [s] +[Problem] +EnableGravity = false + [SpatialParams] Permeability = 1.01936799e-14 # intrinsic permeability of the porous medium [m^2] diff --git a/lecture/mm/buckleyleverett/buckleyleverettproblem.hh b/lecture/mm/buckleyleverett/buckleyleverettproblem.hh index 0855661a0defccf2a421e75eec4e4d367072d672..695590ec90f014d95aa51a15e882129145cf7cd8 100644 --- a/lecture/mm/buckleyleverett/buckleyleverettproblem.hh +++ b/lecture/mm/buckleyleverett/buckleyleverettproblem.hh @@ -21,7 +21,11 @@ #include -#include +#include + +#include +#include + #include #include #include @@ -34,49 +38,36 @@ #include "buckleyleverettanalyticsolution.hh" #include -namespace Dumux -{ -template +namespace Dumux { + +template class BuckleyLeverettProblem; ////////// // Specify the properties ////////// -namespace Properties -{ -NEW_TYPE_TAG(BuckleyLeverettProblem, INHERITS_FROM(FVPressureTwoP, FVTransportTwoP, IMPESTwoP, BuckleyLeverettSpatialParams)); +namespace Properties { + +NEW_TYPE_TAG(BuckleyLeverettProblemTypeTag, INHERITS_FROM(FVPressureTwoP, FVTransportTwoP, IMPESTwoP, BuckleyLeverettSpatialParamsTypeTag)); // Set the grid type -SET_TYPE_PROP(BuckleyLeverettProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(BuckleyLeverettProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(BuckleyLeverettProblem, Problem, BuckleyLeverettProblem); +SET_TYPE_PROP(BuckleyLeverettProblemTypeTag, Problem, BuckleyLeverettProblem); -// Set the wetting phase -SET_PROP(BuckleyLeverettProblem, WettingPhase) +SET_PROP(BuckleyLeverettProblemTypeTag, FluidSystem) { -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef FluidSystems::LiquidPhase > type; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using WettingPhase = FluidSystems::OnePLiquid >; + using NonwettingPhase = FluidSystems::OnePLiquid >; + using type = FluidSystems::TwoPImmiscible; }; -// Set the non-wetting phase -SET_PROP(BuckleyLeverettProblem, NonwettingPhase) -{ -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef FluidSystems::LiquidPhase > type; -}; +SET_TYPE_PROP(BuckleyLeverettProblemTypeTag, EvalCflFluxFunction, EvalCflFluxCoats); +SET_INT_PROP(BuckleyLeverettProblemTypeTag, Formulation, SequentialTwoPCommonIndices::pwsw); -// Disable gravity -SET_BOOL_PROP(BuckleyLeverettProblem, ProblemEnableGravity, false); - -SET_TYPE_PROP(BuckleyLeverettProblem, EvalCflFluxFunction, EvalCflFluxCoats); -SET_INT_PROP(BuckleyLeverettProblem, Formulation, SequentialTwoPCommonIndices::pwsw); -SET_SCALAR_PROP(BuckleyLeverettProblem, ImpetCFLFactor, 0.95); -} +} // end namespace Properties /*! * \ingroup DecoupledProblems @@ -84,44 +75,27 @@ SET_SCALAR_PROP(BuckleyLeverettProblem, ImpetCFLFactor, 0.95); template class BuckleyLeverettProblem: public IMPESProblem2P { - typedef IMPESProblem2P ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; - - typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; - typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; - - typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; - - typedef typename GET_PROP_TYPE(TypeTag, CellData) CellData; - - typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; - typedef typename GET_PROP(TypeTag, SolutionTypes)::PrimaryVariables PrimaryVariables; - - enum - { - dim = GridView::dimension, - dimWorld = GridView::dimensionworld - }; - enum - { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - pressureIdx = Indices::pressureIdx, - swIdx = Indices::swIdx, - pressEqIdx = Indices::pressureEqIdx, - satEqIdx = Indices::satEqIdx - }; - - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GridView::Traits::template Codim<0>::Entity Element; - typedef typename GridView::Intersection Intersection; - typedef Dune::FieldVector GlobalPosition; - typedef typename GET_PROP(TypeTag, ParameterTree) Params; + using ParentType = IMPESProblem2P; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using TimeManager = typename GET_PROP_TYPE(TypeTag, TimeManager); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); + using CellData = typename GET_PROP_TYPE(TypeTag, CellData); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using PrimaryVariables = typename GET_PROP(TypeTag, SolutionTypes)::PrimaryVariables; + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + static constexpr int wPhaseIdx = Indices::wPhaseIdx; + static constexpr int nPhaseIdx = Indices::nPhaseIdx; + static constexpr int pressureIdx = Indices::pressureIdx; + static constexpr int swIdx = Indices::swIdx; + static constexpr int pressEqIdx = Indices::pressureEqIdx; + static constexpr int satEqIdx = Indices::satEqIdx; + using Element = typename GridView::Traits::template Codim<0>::Entity; + using Intersection = typename GridView::Intersection; + using GlobalPosition = Dune::FieldVector; public: /*! @@ -129,39 +103,35 @@ public: * \param timeManager DOC ME! * \param gridView DOC ME! */ - BuckleyLeverettProblem(TimeManager& timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-8), pLeftBc_(2.0e5), - analyticSolution_(*this, 3e-7), viplabOutput_(*this) + BuckleyLeverettProblem(TimeManager& timeManager, typename GridView::Grid &grid) + : ParentType(timeManager, grid) + , eps_(1e-8) + , pLeftBc_(2.0e5) + , analyticSolution_(*this, 3e-7) + , viplabOutput_(*this) { // check the cell size restriction - const auto cells = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::vector, Grid, Cells); + const auto cells = getParam >("Grid.Cells"); if (cells[1] != 1) DUNE_THROW(Dune::IOError, "This is a 1D problem. Do not change the number of cells in y-direction!"); + if (cells[0] > 200) DUNE_THROW(Dune::IOError, "Maximum allowed number of cells in x-direction for this problem is: 200!"); - upperRight_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, GlobalPosition, Grid, UpperRight); + upperRight_ = getParam("Grid.UpperRight"); - WettingPhase::Component::setViscosity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, ViscosityW)); - NonwettingPhase::Component::setViscosity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, ViscosityNW)); + PseudoOil::setViscosity( getParam("Fluid.ViscosityW") ); + PseudoH2O::setViscosity( getParam("Fluid.ViscosityNW") ); - WettingPhase::Component::setDensity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, DensityW)); - NonwettingPhase::Component::setDensity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, DensityNW)); + PseudoOil::setDensity( getParam("Fluid.DensityW") ); + PseudoH2O::setDensity( getParam("Fluid.DensityNW") ); - densityNonWetting_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, DensityNW); + densityNonWetting_ = getParam("Fluid.DensityNW"); - swr_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, ResidualSaturationWetting); - snr_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams ,ResidualSaturationNonWetting); + swr_ = getParam("SpatialParams.ResidualSaturationWetting"); + snr_ = getParam("SpatialParams.ResidualSaturationNonWetting"); - paraviewOutput_ = Params::tree().template get("Output.paraviewOutput", true); - // create output for ViPLab if no output for Paraview is created - if (!paraviewOutput_) - { - //Write header for ViPLab-Outputfile - std::ofstream dataFile; - dataFile.open("vipplot.vgf"); - dataFile.close(); - } + paraviewOutput_ = getParam("Output.paraviewOutput", true); } void init() @@ -181,16 +151,20 @@ public: * This is used as a prefix for files generated by the simulation. */ const std::string name() const - { return "buckleyleverett"; } + { + return "buckleyleverett"; + } bool shouldWriteRestartFile() const - { return false; } + { + return false; + } void postTimeStep() { analyticSolution_.calculateAnalyticSolution(); ParentType::postTimeStep(); - }; + } void addOutputVtkFields() { @@ -233,11 +207,13 @@ public: { bcTypes.setAllDirichlet(); } + else if (globalPos[0] > upperRight_[0] - eps_)//at east boundary { bcTypes.setNeumann(pressEqIdx);//Neumann for the pressure equation bcTypes.setOutflow(satEqIdx);//Outflow for the transport equation } + // all other boundaries, i.e., top and bottom, Neumann else { @@ -262,6 +238,7 @@ public: void neumannAtPos(PrimaryVariables &values, const GlobalPosition& globalPos) const { values = 0; //no-flow Neumann at top and bottom + if (globalPos[0] > upperRight_[0] - eps_) //east boundary { // the volume flux should remain constant, when density is changed @@ -289,6 +266,7 @@ public: { if (paraviewOutput_) ParentType::writeOutput(false); + else { std::size_t numElements = this->gridView().size(0); @@ -317,6 +295,8 @@ private: BuckleyLeverettAnalytic analyticSolution_; ViplabOutput viplabOutput_; bool paraviewOutput_; - }; -} +}; + +} // end namespace Dumux + #endif // DUMUX_LECTURE_MM_BUCKLEYLEVERETT_BUCKLEYLEVERETTPROBLEM_HH diff --git a/lecture/mm/buckleyleverett/buckleyleverettspatialparams.hh b/lecture/mm/buckleyleverett/buckleyleverettspatialparams.hh index 3b759093db5e768447235766f2a49e9eafd3835f..d0fecaaeb6c71ffa4b8a40e8ab8b3a523d7838a1 100644 --- a/lecture/mm/buckleyleverett/buckleyleverettspatialparams.hh +++ b/lecture/mm/buckleyleverett/buckleyleverettspatialparams.hh @@ -19,60 +19,57 @@ #ifndef BUCKLEYLEVERETT_SPATIALPARAMS_HH #define BUCKLEYLEVERETT_SPATIALPARAMS_HH -#include +#include #include #include #include -namespace Dumux -{ +namespace Dumux { //forward declaration template class BuckleyLeverettSpatialParams; -namespace Properties -{ +namespace Properties { + // The spatial parameters TypeTag -NEW_TYPE_TAG(BuckleyLeverettSpatialParams); +NEW_TYPE_TAG(BuckleyLeverettSpatialParamsTypeTag); // Set the spatial parameters -SET_TYPE_PROP(BuckleyLeverettSpatialParams, SpatialParams, Dumux::BuckleyLeverettSpatialParams); +SET_TYPE_PROP(BuckleyLeverettSpatialParamsTypeTag, SpatialParams, BuckleyLeverettSpatialParams); // Set the material law -SET_PROP(BuckleyLeverettSpatialParams, MaterialLaw) +SET_PROP(BuckleyLeverettSpatialParamsTypeTag, MaterialLaw) { private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef RegularizedBrooksCorey RawMaterialLaw; -// typedef LinearMaterial RawMaterialLaw; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using RawMaterialLaw = RegularizedBrooksCorey; public: - typedef EffToAbsLaw type; + using type = EffToAbsLaw; }; -} + +} // end namespace Properties /** \todo Please doc me! */ template -class BuckleyLeverettSpatialParams: public FVSpatialParams +class BuckleyLeverettSpatialParams: public SequentialFVSpatialParams { - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef FVSpatialParams ParentType; - typedef typename Grid::ctype CoordScalar; - - enum - {dim=Grid::dimension, dimWorld=Grid::dimensionworld, numEq=1}; - typedef typename Grid::Traits::template Codim<0>::Entity Element; - - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldMatrix FieldMatrix; + using Grid = typename GET_PROP_TYPE(TypeTag, Grid); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using ParentType = SequentialFVSpatialParams; + using CoordScalar = typename Grid::ctype; + static constexpr int dim=Grid::dimension; + static constexpr int dimWorld=Grid::dimensionworld, numEq=1; + using Element = typename Grid::Traits::template Codim<0>::Entity; + using GlobalPosition = Dune::FieldVector; + using FieldMatrix = Dune::FieldMatrix; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; - + using MaterialLaw = typename GET_PROP_TYPE(TypeTag, MaterialLaw); + using MaterialLawParams = typename MaterialLaw::Params; Scalar intrinsicPermeability (const Element& element) const { @@ -95,28 +92,18 @@ public: return materialLawParams_; } - - BuckleyLeverettSpatialParams(const GridView& gridView) - :ParentType(gridView) + BuckleyLeverettSpatialParams(const Problem& problem) + :ParentType(problem) { - Scalar permFactor = 1.0;//0.001/(1000*9.81); - - constPermeability_ = GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.Permeability)*permFactor; - materialLawParams_.setSwr(GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.ResidualSaturationWetting)); - materialLawParams_.setSnr(GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.ResidualSaturationNonWetting)); - -// if( GET_RUNTIME_PARAM(TypeTag,bool, SpatialParams.LinearLaw)/* true -> linear law */ ){ -// //set parameters -// materialLawParams_.setEntryPc(GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.LinearLawEntryPressure)); -// materialLawParams_.setMaxPc(GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.LinearLawMaxPc)); -// } -// else{ - //set Brooks-Corey parameters - materialLawParams_.setPe(GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.BrooksCoreyEntryPressure)); - materialLawParams_.setLambda(GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.BrooksCoreyLambda)); - // } + Scalar permFactor = 1.0; //0.001/(1000*9.81); - porosity_ = GET_RUNTIME_PARAM(TypeTag,double, SpatialParams.Porosity); + constPermeability_ = getParam("SpatialParams.Permeability")*permFactor; + materialLawParams_.setSwr( getParam("SpatialParams.ResidualSaturationWetting") ); + materialLawParams_.setSnr( getParam("SpatialParams.ResidualSaturationNonWetting") ); + //set Brooks-Corey parameters + materialLawParams_.setPe( getParam("SpatialParams.BrooksCoreyEntryPressure") ); + materialLawParams_.setLambda( getParam("SpatialParams.BrooksCoreyLambda") ); + porosity_ = getParam("SpatialParams.Porosity"); } private: @@ -126,5 +113,6 @@ private: }; -} // end namespace -#endif +} // end namespace Dumux + +#endif // BUCKLEYLEVERETT_SPATIALPARAMS_HH diff --git a/lecture/mm/buckleyleverett/pseudoh2o.hh b/lecture/mm/buckleyleverett/pseudoh2o.hh index 829c8ad3c87078be70c9fd10172de8bb16f079e6..65bbe3442105c70407ffd751386fc0c441f88619 100644 --- a/lecture/mm/buckleyleverett/pseudoh2o.hh +++ b/lecture/mm/buckleyleverett/pseudoh2o.hh @@ -25,24 +25,25 @@ #define DUMUX_PSEUDOH2O_HH -#include +#include -namespace Dumux -{ +namespace Dumux { /*! * \brief Rough estimate for testing purposes of some water. */ template -class PseudoH2O : public Component > +class PseudoH2O : public Components::Base > + , public Components::Liquid > { - typedef Component > ParentType; - typedef ScalarT Scalar; public: + using Scalar = ScalarT; /*! * \brief A human readable name for the water. */ static std::string name() - { return "H2O"; } + { + return "H2O"; + } /*! * \brief Rough estimate of the density of oil [kg/m^3]. @@ -62,7 +63,8 @@ public: static Scalar liquidViscosity(Scalar temperature, Scalar pressure) { return viscosity_; - }; + } + /*! * \brief DOC ME! * \param viscosity DOC ME! @@ -71,6 +73,7 @@ public: { viscosity_ = viscosity; } + /*! * \brief DOC ME! * \param density DOC ME! @@ -83,11 +86,15 @@ public: private: static Scalar viscosity_; static Scalar density_; + }; + template typename PseudoH2O::Scalar PseudoH2O::viscosity_ = 0.001; + template typename PseudoH2O::Scalar PseudoH2O::density_ = 1000; -} // end namepace -#endif +} // end namepace Dumux + +#endif // DUMUX_PSEUDOH2O_HH diff --git a/lecture/mm/buckleyleverett/pseudooil.hh b/lecture/mm/buckleyleverett/pseudooil.hh index 9dc2c26c8d5b75de401576b915b45399a4859aa6..dd138e5c6e66c3642b0245ca65c3dbb9a7f9372e 100644 --- a/lecture/mm/buckleyleverett/pseudooil.hh +++ b/lecture/mm/buckleyleverett/pseudooil.hh @@ -24,27 +24,27 @@ #ifndef DUMUX_PSEUDOOIL_HH #define DUMUX_PSEUDOOIL_HH +#include -#include -//#include "interface_BL.xml" +namespace Dumux { -namespace Dumux -{ /*! * \brief Rough estimate for testing purposes of some oil. */ template -class PseudoOil : public Component > +class PseudoOil : public Components::Base > + , public Components::Liquid > { - typedef Component > ParentType; - typedef ScalarT Scalar; - public: + using Scalar = ScalarT; + /*! * \brief A human readable name for the water. */ static std::string name() - { return "Oil"; } + { + return "Oil"; + } /*! * \brief Rough estimate of the density of oil [kg/m^3]. @@ -55,9 +55,6 @@ public: { return density_; } - // double densityOil = 1000; - // double viscosityWater = interfaceFluidProps.IFP_ViscosityWettingFluid; - // double viscosityOil = interfaceFluidProps.IFP_ViscosityNonWettingFluid; /*! * \brief Rough estimate of the viscosity of oil kg/(ms). @@ -67,7 +64,8 @@ public: static Scalar liquidViscosity(Scalar temperature, Scalar pressure) { return viscosity_; - }; + } + /*! * \brief DOC ME! * \param viscosity DOC ME! @@ -76,6 +74,7 @@ public: { viscosity_ = viscosity; } + /*! * \brief DOC ME! * \param density DOC ME! @@ -89,10 +88,14 @@ private: static Scalar viscosity_; static Scalar density_; }; + template typename PseudoOil::Scalar PseudoOil::viscosity_ = 0.01; + template typename PseudoOil::Scalar PseudoOil::density_ = 1000; -} // end namepace -#endif + +} // end namepace Dumux + +#endif // DUMUX_PSEUDOOIL_HH diff --git a/lecture/mm/co2plume/co2plumeshapeexercise.cc b/lecture/mm/co2plume/co2plumeshapeexercise.cc index cd2f47558617ab8e1a8332e51d5c19f9e985e131..62661696d2702035886fe2e1bdb2c478528bde9b 100644 --- a/lecture/mm/co2plume/co2plumeshapeexercise.cc +++ b/lecture/mm/co2plume/co2plumeshapeexercise.cc @@ -19,58 +19,182 @@ /*! * \file * - * \brief test for the 3p3c box model + * \brief Test for the two-phase two-component CC model. */ -#include "config.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +// the problem definitions #include "co2plumeshapeproblem.hh" -#include -/*! - * \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) +int main(int argc, char** argv) try { - if (errorMsg.size() > 0) { - std::string errorMessageOut = "\nUsage: "; - errorMessageOut += progName; - errorMessageOut += " [options]\n"; - errorMessageOut += errorMsg; - errorMessageOut += "\n\nThe List of Mandatory arguments for this program is:\n" - "\t-tEnd The end of the simulation. [s] \n" - "\t-dtInitial The initial timestep size. [s] \n" - "\t-gridFile The file name of the file containing the grid \n" - "\t definition in DGF format\n" - "\t-FluidSystem.nTemperature Number of tabularization entries [-] \n" - "\t-FluidSystem.nPressure Number of tabularization entries [-] \n" - "\t-FluidSystem.pressureLow Low end for tabularization of fluid properties [Pa] \n" - "\t-FluidSystem.pressureHigh High end for tabularization of fluid properties [Pa] \n" - "\t-FluidSystem.temperatureLow Low end for tabularization of fluid properties [Pa] \n" - "\t-FluidSystem.temperatureHigh High end for tabularization of fluid properties [Pa] \n" - "\t-SpatialParams.K Intrinsic permeability [m^2] \n" - "\t-SpatialParams.phi porosity [-] \n" - "\t-MaterialLaw.swr Residual saturation wetting-phase [-] \n" - "\t-MaterialLaw.snr Residual saturation non-wetting-phase [-] \n" - "\t-MaterialLaw.Pe capillary entry pressure [Pa] \n" - "\t-MaterialLaw.Lambda BrooksCorey paramter [-] \n" - "\t-SimulationControl.name The name of the output files [-] \n" - "\t-InitialConditions.temperature Initial temperature in the reservoir [K] \n"; - // "\t-InitialConditions.depthBOR Depth below ground surface [m] \n"; - - std::cout << errorMessageOut - << "\n"; - } -} + using namespace Dumux; -int main(int argc, char** argv) -{ - typedef TTAG(PlumeShapeProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); -} + // define the type tag for this problem + using TypeTag = TTAG(PlumeShapeBoxTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const Scalar finalStorage = 1.55e5; + const Scalar injectionRate = getParam("BoundaryConditions.InjectionRate"); + const Scalar tEnd = finalStorage/injectionRate; + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + // instantiate time loop + auto timeLoop = std::make_shared>(0, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + return 0; +} // end main +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) +{ + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; +} diff --git a/lecture/mm/co2plume/co2plumeshapeexercise.input b/lecture/mm/co2plume/co2plumeshapeexercise.input index 0e701ea3aab15709c17e87c0dd0e852226823a44..fde332b03fd5a007dabf9f24f5e25b019bab6956 100644 --- a/lecture/mm/co2plume/co2plumeshapeexercise.input +++ b/lecture/mm/co2plume/co2plumeshapeexercise.input @@ -1,6 +1,5 @@ -[TimeManager] +[TimeLoop] DtInitial = 250 # [s] -TEnd = 1e100 # [s] actual end time is controlled by injection rate EpisodeLength = 1e7 # [s] [Grid] @@ -14,11 +13,12 @@ PressureLow = 1e5# [Pa] low end for tabularization of fluid properties PressureHigh = 3e7# [Pa] high end for tabularization of fluid properties TemperatureLow = 283.15 # [Pa] low end for tabularization of fluid properties TemperatureHigh = 320.15 # [Pa] high end for tabularization of fluid properties +Salinity = 0.1 [SpatialParams] Permeability = 1e-13 # [m^2] intrinsic permeability DipAngle = 0.0 # [deg] dip angle for the domain -Porosity = 0.3 # porosity +Porosity = 0.35 # porosity [MaterialLaw] Swr = 0.2 # [-] residual wetting phase sat. @@ -26,7 +26,7 @@ Snr = 0.2 # [-] residual non-wetting phase sat. Pe = 5e3 # [Pa] capillary entry pressure Lambda = 2 # [-] Brooks Corey parameter -[SimulationControl] +[Problem] Name = co2plumeshape # the name of the output files [InitialConditions] @@ -39,4 +39,4 @@ InjectionTemperature = 313.15 # [K] temperature of injected CO2 [Newton] MaxRelativeShift = 1e-8 -SatisfyResidualAndShiftCriterion = true \ No newline at end of file +SatisfyResidualAndShiftCriterion = true diff --git a/lecture/mm/co2plume/co2plumeshapeproblem.hh b/lecture/mm/co2plume/co2plumeshapeproblem.hh index 0b8bde055f060b89d7e39970f856665047bea784..0abb66ed36c8c0dbae1ef1816106bd88b2ab1a25 100644 --- a/lecture/mm/co2plume/co2plumeshapeproblem.hh +++ b/lecture/mm/co2plume/co2plumeshapeproblem.hh @@ -18,140 +18,158 @@ *****************************************************************************/ /*! * \file - * - * \brief Non-iostohermal CO2 plume migration: CO2 is injected over 30 - * meters at the bottom of the left boundary and moves upwards towards - * an impermeable rock. + * \ingroup CO2Tests + * \brief Definition of a problem, where CO2 is injected in a reservoir. */ -#ifndef CO2_PLUMESHAPE_PROBLEM_HH -#define CO2_PLUMESHAPE_PROBLEM_HH +#ifndef DUMUX_PLUMESHAPE_PROBLEM_HH +#define DUMUX_PLUMESHAPE_PROBLEM_HH + +#include -#include +#include +#include -#include -#include +#include +#include + +#include +#include #include -#include -#include +#include #include - #include "co2plumeshapespatialparameters.hh" +// per default use isothermal model +#ifndef ISOTHERMAL +#define ISOTHERMAL 1 +#endif -namespace Dumux -{ +namespace Dumux { +/*! + * \ingroup CO2Tests + * \brief Definition of a problem, where CO2 is injected in a reservoir. + */ template class PlumeShapeProblem; -namespace Properties -{ -// for the correct type tag, see 2p2cniproperties.hh -NEW_TYPE_TAG(PlumeShapeProblem, INHERITS_FROM(BoxTwoPTwoCNI, PlumeShapeSpatialParams)); +namespace Properties { +NEW_TYPE_TAG(PlumeShapeTypeTag, INHERITS_FROM(TwoPTwoCCO2)); +NEW_TYPE_TAG(PlumeShapeBoxTypeTag, INHERITS_FROM(BoxModel, PlumeShapeTypeTag)); -// Set grid to a two-dimensional structured quadrilateral grid -SET_TYPE_PROP(PlumeShapeProblem, Grid, Dune::YaspGrid<2>); +//Set the grid type +SET_TYPE_PROP(PlumeShapeTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(PlumeShapeProblem, Problem, PlumeShapeProblem); - -// Set fluid configuration -SET_TYPE_PROP(PlumeShapeProblem, FluidSystem, BrineCO2FluidSystem); - -// Set the CO2 table to be used; in this case not the the default table -SET_TYPE_PROP(PlumeShapeProblem, CO2Table, CO2TablesBenchmarkThree::CO2Tables); -// Set the salinity mass fraction of the brine in the reservoir -SET_SCALAR_PROP(PlumeShapeProblem, ProblemSalinity, 1e-1); - +SET_TYPE_PROP(PlumeShapeTypeTag, Problem, PlumeShapeProblem); -//! the CO2 Model and VolumeVariables properties -SET_TYPE_PROP(PlumeShapeProblem, IsothermalVolumeVariables, CO2VolumeVariables); -SET_TYPE_PROP(PlumeShapeProblem, IsothermalModel, CO2Model); - - -SET_TYPE_PROP(PlumeShapeProblem, LinearSolver, ILU0BiCGSTABBackend); - -// Enable gravity? -SET_BOOL_PROP(PlumeShapeProblem, ProblemEnableGravity, true); +// Set the spatial parameters +SET_PROP(PlumeShapeTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = PlumeShapeSpatialParams; +}; -SET_BOOL_PROP(PlumeShapeProblem, ImplicitEnableJacobianRecycling, false); +// Set fluid configuration +SET_TYPE_PROP(PlumeShapeTypeTag, FluidSystem, + FluidSystems::BrineCO2>, + FluidSystems::BrineCO2DefaultPolicy>); -// set newton relative tolerance -SET_SCALAR_PROP(PlumeShapeProblem, NewtonMaxRelativeShift, 1e-6); -SET_BOOL_PROP(PlumeShapeProblem, UseMoles, false); +// Use Moles +SET_BOOL_PROP(PlumeShapeTypeTag, UseMoles, false); -} +} // end namespace Properties /*! - * \ingroup TwoPTwoCNIBoxModel - * \ingroup BoxTestProblems - * \brief Isothermal NAPL infiltration problem: LNAPL contaminates - * the unsaturated and the saturated groundwater zone. + * \ingroup CO2Model + * \ingroup ImplicitTestProblems + * \brief Definition of a problem, where CO2 is injected in a reservoir. * - * The 2D domain of this test problem is + * The domain is sized 200m times 100m and consists of four layers, a + * permeable reservoir layer at the bottom, a barrier rock layer with reduced permeability, another reservoir layer + * and at the top a barrier rock layer with a very low permeablility. * - * Left and right boundaries are - * Top and bottom are Neumann boundaries, all no-flow except for the small - * infiltration zone in the upper left part. + * CO2 is injected at the permeable bottom layer + * from the left side. The domain is initially filled with brine. * - * This problem uses the \ref TwoPTwoCNIModel. + * The grid is unstructered and permeability and porosity for the elements are read in from the grid file. The grid file + * also contains so-called boundary ids which can be used assigned during the grid creation in order to differentiate + * between different parts of the boundary. + * These boundary ids can be imported into the problem where the boundary conditions can then be assigned accordingly. * - * This problem should typically be simulated for - * A good choice for the initial time step size is - * To adjust the simulation time it is necessary to edit the file + * The model is able to use either mole or mass fractions. The property useMoles can be set to either true or false in the + * problem file. Make sure that the according units are used in the problem setup. The default setting for useMoles is false. * - * To run the simulation execute the following line in shell: - * - * */ + * To run the simulation execute the following line in shell (works with the box and cell centered spatial discretization method): + * ./test_ccco2 or ./test_boxco2 + */ template -class PlumeShapeProblem : public ImplicitPorousMediaProblem +class PlumeShapeProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; + using ParentType = PorousMediumFlowProblem; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, CO2Table) CO2Tables; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; - enum { - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld - }; + using ModelTraits = typename GET_PROP_TYPE(TypeTag, ModelTraits); + using Indices = typename ModelTraits::Indices; // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - lPhaseIdx = Indices::wPhaseIdx, - gPhaseIdx = Indices::nPhaseIdx, + enum + { + // primary variable indices + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + + wPhaseIdx = Indices::pressureIdx, + nPhaseIdx = Indices::switchIdx, + + // phase presence index + firstPhaseOnly = Indices::firstPhaseOnly, + // component indices BrineIdx = FluidSystem::BrineIdx, CO2Idx = FluidSystem::CO2Idx, + // equation indices conti0EqIdx = Indices::conti0EqIdx, - contiCO2EqIdx = conti0EqIdx + CO2Idx, + contiCO2EqIdx = conti0EqIdx + CO2Idx + }; - #if !ISOTHERMAL +#if !ISOTHERMAL + enum { temperatureIdx = Indices::temperatureIdx, energyEqIdx = Indices::energyEqIdx, - #endif }; +#endif - typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; - typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; - typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; - typedef typename GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; + using CO2 = Components::CO2; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, GridCreator) GridCreator; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + //! property that defines whether mole or mass fractions are used + static constexpr bool useMoles = ModelTraits::useMoles(); - typedef Dune::FieldVector GlobalPosition; - typedef Dumux::CO2 CO2; + // the discretization method we are using + static constexpr auto discMethod = GET_PROP_TYPE(TypeTag, FVGridGeometry)::discMethod; + + // world dimension to access gravity vector + static constexpr int dimWorld = GridView::dimensionworld; public: /*! @@ -160,70 +178,50 @@ public: * \param timeManager The time manager * \param gridView The grid view */ - PlumeShapeProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-6) + PlumeShapeProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) + , injectionTop_(1) + , injectionBottom_(2) + , dirichletBoundary_(3) + , noFlowBoundary_(4) { - nTemperature_ = GET_RUNTIME_PARAM(TypeTag, int, FluidSystem.NTemperature); - nPressure_ = GET_RUNTIME_PARAM(TypeTag, int, FluidSystem.NPressure); - pressureLow_ = GET_RUNTIME_PARAM(TypeTag, Scalar, FluidSystem.PressureLow); - pressureHigh_ = GET_RUNTIME_PARAM(TypeTag, Scalar, FluidSystem.PressureHigh); - pressure_ = GET_RUNTIME_PARAM(TypeTag, Scalar, InitialConditions.Pressure); // hydrodynamic pressure at top layer - temperatureLow_ = GET_RUNTIME_PARAM(TypeTag, Scalar, FluidSystem.TemperatureLow); - temperatureHigh_ = GET_RUNTIME_PARAM(TypeTag, Scalar, FluidSystem.TemperatureHigh); - temperature_ = GET_RUNTIME_PARAM(TypeTag, Scalar, InitialConditions.Temperature); - injectionRate_ = GET_RUNTIME_PARAM(TypeTag, Scalar, BoundaryConditions.InjectionRate); - injectionTemperature_ = GET_RUNTIME_PARAM(TypeTag, Scalar, BoundaryConditions.InjectionTemperature); - dipAngle_ = GET_RUNTIME_PARAM(TypeTag, Scalar, SpatialParams.DipAngle); // [deg] - name_ = GET_RUNTIME_PARAM(TypeTag, std::string, SimulationControl.Name); - - episodeLength_ = GET_RUNTIME_PARAM(TypeTag, Scalar, TimeManager.EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); + nTemperature_ = getParam("FluidSystem.NTemperature"); + nPressure_ = getParam("FluidSystem.NPressure"); + pressureLow_ = getParam("FluidSystem.PressureLow"); + pressureHigh_ = getParam("FluidSystem.PressureHigh"); + temperatureLow_ = getParam("FluidSystem.TemperatureLow"); + temperatureHigh_ = getParam("FluidSystem.TemperatureHigh"); + name_ = getParam("Problem.Name"); + pressure_ = getParam("InitialConditions.Pressure"); // hydrodynamic pressure at top layer + temperature_ = getParam("InitialConditions.Temperature"); + injectionRate_ = getParam("BoundaryConditions.InjectionRate"); + injectionTemperature_ = getParam("BoundaryConditions.InjectionTemperature"); + dipAngle_ = getParam("SpatialParams.DipAngle"); // [deg] dipAngleRadians_ = (dipAngle_ * M_PI)/180.0; // [rad] // rotate the coordinate system / gravity: gravity_[1] = -9.81 * cos(dipAngleRadians_); gravity_[0] = -9.81 * sin(dipAngleRadians_); - // initialize the tables of the fluid system - FluidSystem::init(/*Tmin=*/temperatureLow_, - /*Tmax=*/temperatureHigh_, - /*nT=*/nTemperature_, - /*pmin=*/pressureLow_, - /*pmax=*/pressureHigh_, - /*np=*/nPressure_); +#if !ISOTHERMAL + injectionPressure_ = getParam("Problem.InjectionPressure"); + injectionTemperature_ = getParam("Problem.InjectionTemperature"); +#endif - simulationFinished_ = false; //boolean controlling the simulation end - } - /*! - * \brief Called directly after the time integration. - */ - void postTimeStep() - { - // Calculate storage terms - PrimaryVariables storageL, storageG; - this->model().globalPhaseStorage(storageL, lPhaseIdx); - this->model().globalPhaseStorage(storageG, gPhaseIdx); - - // Write mass balance information for rank 0 - if (this->gridView().comm().rank() == 0) { - std::cout<<"Storage: liquid=[" << storageL << "]" - << " gas=[" << storageG << "]\n"; - } - - // tell the time manager in case we reached the end of the injection - if(simulationFinished_) - this->timeManager().setFinished(true); - } - - void init() - { - // set the simulation end time - const Scalar finalStorage = 1.55e5; // [kg] - // set TimeManager.TEnd in input file to small value to end the simulation earlier - const Scalar endTime = std::min(finalStorage/injectionRate_, this->timeManager().endTime()); // [s] - this->timeManager().setEndTime(endTime); - ParentType::init(); + // initialize the tables of the fluid system + FluidSystem::init(/*Tmin=*/temperatureLow_, + /*Tmax=*/temperatureHigh_, + /*nT=*/nTemperature_, + /*pmin=*/pressureLow_, + /*pmax=*/pressureHigh_, + /*np=*/nPressure_); + + //stating in the console whether mole or mass fractions are used + if(useMoles) + std::cout<<"problem uses mole fractions"< this->bBoxMax()[0] - eps_ ) + if ( globalPos[0] > this->fvGridGeometry().bBoxMax()[0] - eps_ ) { values.setAllDirichlet(); } - // set part of West boundaries to Dirichlet & Neumann values - else if ( globalPos[1] < 30.0 + eps_ && globalPos[0] < eps_) - { -#if !ISOTHERMAL - values.setDirichlet(temperatureIdx, energyEqIdx); -#endif - } + + return values; } /*! - * \brief Evaluate the boundary conditions for a dirichlet - * boundary segment. - * - * \param values The dirichlet values for the primary variables - * \param vertex The vertex for which the boundary type is set + * \brief Evaluates the boundary conditions for a Dirichlet + * boundary segment * - * For this method, the \a values parameter stores primary variables. + * \param returns the Dirichlet values for the conservation equations in + * \f$ [ \textnormal{unit of primary variable} ] \f$ + * \param globalPos The global position */ - void dirichlet(PrimaryVariables &values, const Vertex &vertex) const - { - const GlobalPosition& globalPos = vertex.geometry().center(); - - initial_(values, globalPos); - -#if !ISOTHERMAL - if (globalPos[1] < 30.0 + eps_ && globalPos[0] < eps_) - { - values[temperatureIdx] = injectionTemperature_; //in K - } -#endif - } + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const + { return initial_(globalPos); } /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * \param values The neumann values for the conservation equations + * This is the method for the case where the Neumann condition is + * potentially solution dependent and requires some quantities that + * are specific to the fully-implicit method. + * + * \param values The neumann values for the conservation equations in units of + * \f$ [ \textnormal{unit of conserved quantity} / (m^2 \cdot s )] \f$ * \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 fvGeometry The finite-volume geometry + * \param elemVolVars All volume variables for the element + * \param scvf The sub control volume face * - * For this method, the \a values parameter stores the mass flux + * For this method, the \a values parameter stores the flux * in normal direction of each phase. Negative values mean influx. + * E.g. for the mass balance that would the mass flux in \f$ [ kg / (m^2 \cdot s)] \f$. */ - void solDependentNeumann(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvGeometry, - const Intersection &intersection, - const int scvIdx, - const int boundaryFaceIdx, - const ElementVolumeVariables &elemVolVars) const + NumEqVector neumann(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolvars, + const SubControlVolumeFace& scvf) const { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - const Scalar temperature = elemVolVars[scvIdx].temperature(); - const Scalar pressure = elemVolVars[scvIdx].pressure(gPhaseIdx); - - values = 0; + NumEqVector fluxes(0.0); + // kg/(m^2*s) or mole/(m^2*s) depending on useMoles + const Scalar pressure = elemVolvars[scvf.insideScvIdx()].pressure(nPhaseIdx); + auto globalPos = scvf.ipGlobal(); if ( globalPos[1] < 30.0 + eps_ && globalPos[0] < eps_ ) { Scalar massFlux = injectionRate_ /30.0; // [kg/(s*m^2)] - values[contiCO2EqIdx] = -massFlux; // kg/(s*m^2) + fluxes[contiCO2EqIdx] = -massFlux; // kg/(s*m^2) #if !ISOTHERMAL - values[energyEqIdx] = -massFlux*CO2::gasEnthalpy(temperature, pressure); // W/(m^2) + fluxes[energyEqIdx] = -massFlux*CO2::gasEnthalpy(injectionTemperature_, pressure); #endif } + + return fluxes; } + // \} /*! - * \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. + * \name Volume terms */ - void initial(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - initial_(values, globalPos); - } + // \{ /*! - * \brief Return the initial phase state inside a control volume. + * \brief Evaluates the initial values at a position * + * \returns the initial values for the conservation equations in + * \f$ [ \textnormal{unit of primary variables} ] \f$ * \param globalPos The global position */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const - { return Indices::wPhaseOnly; } - - bool shouldWriteOutput() const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - return this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); + return initial_(globalPos); } - void episodeEnd() - { - this->timeManager().startNextEpisode(episodeLength_); - } + // \} + private: - // the internal method for the initial condition - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + /*! + * \brief Evaluates the initial values for a control volume + * + * The internal method for the initial condition + * + * \param values Stores the initial values for the conservation equations in + * \f$ [ \textnormal{unit of primary variables} ] \f$ + * \param globalPos The global position + */ + PrimaryVariables initial_(const GlobalPosition &globalPos) const { + PrimaryVariables values(0.0); + values.setState(firstPhaseOnly); + const Scalar densityW = FluidSystem::Brine::liquidDensity(temperature_, 1e5); const Scalar moleFracLiquidCO2 = 0.00; @@ -417,40 +376,48 @@ private: const Scalar massFracLiquidCO2 = moleFracLiquidCO2*FluidSystem::molarMass(CO2Idx)/meanM; // set initial pressure and pressure Dirichlet conditions: - const Scalar distanceToTopBoundary_ = ( this->bBoxMax()[1] - this->bBoxMin()[1] ) - globalPos[1]; // distance without rotation + const Scalar distanceToTopBoundary_ = ( this->fvGridGeometry().bBoxMax()[1] - this->fvGridGeometry().bBoxMin()[1] ) - globalPos[1]; // distance without rotation const Scalar distanceToTopBoundaryRotated_ = distanceToTopBoundary_ / std::cos(dipAngleRadians_); - const Scalar distanceTopBoundaryToSurface_ = std::sin(dipAngleRadians_) * (this->bBoxMax()[0] - globalPos[0]); + const Scalar distanceTopBoundaryToSurface_ = std::sin(dipAngleRadians_) * (this->fvGridGeometry().bBoxMax()[0] - globalPos[0]); values[Indices::pressureIdx] = pressure_ + densityW * 9.81 * distanceToTopBoundaryRotated_ + densityW * 9.81 * distanceTopBoundaryToSurface_; values[Indices::switchIdx] = massFracLiquidCO2; -#if !ISOTHERMAL - values[temperatureIdx] = temperature_;//290.0 + (depthBOR_ - globalPos[1])*0.03; -#endif + return values; } - Scalar temperature_,injectionTemperature_; - Scalar depthBOR_; - Scalar eps_; Scalar injectionRate_; - Scalar episodeLength_; - Scalar dipAngleRadians_, dipAngle_; - GlobalPosition gravity_; +#if !ISOTHERMAL + Scalar injectionPressure_, injectionTemperature_; +#endif int nTemperature_; int nPressure_; + Scalar temperature_; + Scalar injectionTemperature_; + Scalar pressure_; + + Scalar dipAngleRadians_, dipAngle_; + + GlobalPosition gravity_; + + Scalar eps_ = 1.e-7; + std::string name_ ; - Scalar pressureLow_, pressureHigh_, pressure_; + Scalar pressureLow_, pressureHigh_; Scalar temperatureLow_, temperatureHigh_; - bool simulationFinished_; + int injectionTop_; + int injectionBottom_; + int dirichletBoundary_; + int noFlowBoundary_; }; -} //end namespace +} //end namespace Dumux #endif diff --git a/lecture/mm/co2plume/co2plumeshapespatialparameters.hh b/lecture/mm/co2plume/co2plumeshapespatialparameters.hh index a2c331f6aa1ab78a1adce2033fa90a90742856fa..69c430e2380071fc0caf91d1d544a7a6547ff8aa 100644 --- a/lecture/mm/co2plume/co2plumeshapespatialparameters.hh +++ b/lecture/mm/co2plume/co2plumeshapespatialparameters.hh @@ -16,209 +16,139 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -#ifndef CO2_PLUMESHAPE_SPATIALPARAMETERS_HH -#define CO2_PLUMESHAPE_SPATIALPARAMETERS_HH +/*! + * \file + * \ingroup CO2Tests + * \brief Definition of the spatial parameters for the heterogeneous + * problem which uses the non-isothermal or isothermal CO2 + * fully implicit model. + */ + +#ifndef DUMUX_PLUMESHAPE_SPATIAL_PARAMS_HH +#define DUMUX_PLUMESHAPE_SPATIAL_PARAMS_HH -#include -#include +#include +#include #include #include -#include - -namespace Dumux -{ - -//forward declaration -template -class PlumeShapeSpatialParams; - -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(PlumeShapeSpatialParams); +#include -// Set the spatial parameters -SET_TYPE_PROP(PlumeShapeSpatialParams, SpatialParams, Dumux::PlumeShapeSpatialParams); - -// Set the material Law -SET_PROP(PlumeShapeSpatialParams, MaterialLaw) -{ -private: - // define the material law which is parameterized by effective - // saturations - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef RegularizedBrooksCorey EffMaterialLaw; -public: - // define the material law parameterized by absolute saturations - typedef EffToAbsLaw type; -}; -} +namespace Dumux { /*! - * \ingroup TwoPTwoCModel - * \ingroup BoxTestProblems - * \brief Definition of the spatial parameters for the injection - * problem which uses the isothermal 2p2c box model + * \ingroup CO2Model + * \ingroup ImplicitTestProblems + * \brief Definition of the spatial parameters for the CO2 plumeshape + * problem which uses the non-isothermal or isothermal CO2 + * fully implicit model. */ -template -class PlumeShapeSpatialParams : public ImplicitSpatialParams +template +class PlumeShapeSpatialParams +: public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef Dune::FieldVector GlobalPosition; + using Grid = typename FVGridGeometry::Grid; + using GridView = typename FVGridGeometry::GridView; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using Element = typename GridView::template Codim<0>::Entity; + using ParentType = FVSpatialParams>; - typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + using GlobalPosition = typename SubControlVolume::GlobalPosition; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; + using EffectiveLaw = RegularizedBrooksCorey; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; + using PermeabilityType = Scalar; /*! * \brief The constructor * - * \param gv The grid view + * \param gridView The grid view */ - PlumeShapeSpatialParams(const GridView &gridView) - : ParentType(gridView), k_(0) + PlumeShapeSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - // layerBottom_ = 22.0; + // intrinsic permeability + permeability_ = getParam("SpatialParams.Permeability"); - // intrinsic permeabilities - permeability_ = GET_RUNTIME_PARAM(TypeTag, Scalar, SpatialParams.Permeability); - - // porosity and permeability - porosity_ = 0.35; - for (int i=0; i < dim; i++ ) - k_[i][i] = permeability_; // [m^2] - - // porosities - porosity_ = GET_RUNTIME_PARAM(TypeTag, Scalar, SpatialParams.Porosity); + // porosity + porosity_ = getParam("SpatialParams.Porosity"); // residual saturations - materialParams_.setSwr(GET_RUNTIME_PARAM(TypeTag, Scalar, MaterialLaw.Swr)); - materialParams_.setSnr(GET_RUNTIME_PARAM(TypeTag, Scalar, MaterialLaw.Snr)); + materialParams_.setSwr(getParam("MaterialLaw.Swr")); + materialParams_.setSnr(getParam("MaterialLaw.Snr")); // parameters for the Brooks-Corey law - materialParams_.setPe(GET_RUNTIME_PARAM(TypeTag, Scalar, MaterialLaw.Pe)); - materialParams_.setLambda(GET_RUNTIME_PARAM(TypeTag, Scalar, MaterialLaw.Lambda)); + materialParams_.setPe(getParam("MaterialLaw.Pe")); + materialParams_.setLambda(getParam("MaterialLaw.Lambda")); } - ~PlumeShapeSpatialParams() - {} - /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. + * \brief Function for defining the (intrinsic) permeability \f$[m^2]\f$ + * \note It is possibly solution dependent. * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element. + * \return instrinsic permeability */ - const Dune::FieldMatrix& - intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + template + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { - return k_; + return permeability_; } /*! - * \brief Define the porosity \f$[-]\f$ of the spatial parameters + * \brief Returns the porosity \f$[-]\f$ * - * \param element The finite element - * \param fvElemGeom The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element + * \return porosity */ - const Scalar porosity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + template + Scalar porosity(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { return porosity_; } /*! - * \brief return the parameter object for the Brooks-Corey material law which depends on the position + * \brief Function for defining the parameters needed by constitutive relationships (kr-sw, pc-sw, etc.). * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume - */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return materialParams_; - } - - /*! - * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidHeatCapacity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return 790; // specific heat capacity of granite [J / (kg K)] - } - - /*! - * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume + * \return the material parameters object + * \param globalPos The position of the center of the element */ - Scalar solidDensity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { - return 2700; // density of granite [kg/m^3] + return materialParams_; } /*! - * \brief Returns the thermal conductivity \f$[W/m^2]\f$ of the porous material. + * \brief Function for defining which phase is to be considered as the wetting phase. * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the heat capacity needs to be defined + * \return the wetting phase index + * \param globalPos The position of the center of the element */ - Scalar solidThermalConductivity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return 2.8; - } + template + int wettingPhaseAtPos(const GlobalPosition& globalPos) const + { return FluidSystem::BrineIdx; } private: + Scalar permeability_; - Dune::FieldMatrix k_; Scalar porosity_; MaterialLawParams materialParams_; + }; } // end namespace Dumux diff --git a/lecture/mm/columnxylene/columnxyleneexercise.cc b/lecture/mm/columnxylene/columnxyleneexercise.cc index a2ae9f1dee4ae7f837e130fdfdad3fda844c9510..9c1936407bede823315a702a1596ecb319ababc4 100644 --- a/lecture/mm/columnxylene/columnxyleneexercise.cc +++ b/lecture/mm/columnxylene/columnxyleneexercise.cc @@ -22,10 +22,36 @@ * \brief test for the 3p3cni box model */ #include "config.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + #include "columnxyleneproblem.hh" -// #include "columnxyleneproblem2.hh" -#include +namespace Dumux { +namespace Properties { +NEW_PROP_TAG(FVGridGeometry); +} +} /*! * \brief Provides an interface for customizing error messages associated with @@ -47,14 +73,161 @@ void usage(const char *progName, const std::string &errorMsg) "\t-TimeManager.DtInitial Initial timestep size [s] \n" "\t-Grid.File Name of the file containing the grid \n" "\t definition in DGF format\n"; - std::cout << errorMessageOut << "\n"; } } +//////////////////////// +// the main function +//////////////////////// +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(ColumnProblemBoxTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) // Fehlt ebenso + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(0, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength", std::numeric_limits::max())); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); + while (!timeLoop->finished()) + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + static auto outputInterval = getParam("TimeLoop.OutputInterval", 1); + + if (timeLoop->timeStepIndex() % outputInterval == 0 || timeLoop->willBeFinished()) + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + } + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } +} -int main(int argc, char** argv) +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(ColumnProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/columnxylene/columnxyleneexercise.input b/lecture/mm/columnxylene/columnxyleneexercise.input index 372fd21ce5309a9926e9a716dd769dea4beec852..e14301b77f2f5ed7d9fc6661da3c5812519c6b82 100644 --- a/lecture/mm/columnxylene/columnxyleneexercise.input +++ b/lecture/mm/columnxylene/columnxyleneexercise.input @@ -1,6 +1,4 @@ - - -[TimeManager] +[TimeLoop] DtInitial = 1 # [s] TEnd = 1800 # [s] OutputInterval = 1 @@ -9,21 +7,27 @@ MaxTimeStepSize = 1 [Grid] File = ./grids/column.dgf -[] -steamFlux = -0.39571 # [mol / (s m)] -enthalpyFlux = -17452.97 # [J / (s m)] -permeability = 1.4e-11 # [m^2] -porosity = 0.46 -swr = 0.12 -snr = 0.1 -Sgr = 0.01 -vanGenuchtenAlpha = 0.0005 -vanGenuchtenN = 4.0 +[SpatialParams] +permeability = 1.4e-11 +permeabilityExtDomain = 1.e-8 betaNW = 1.83 betaGN = 2.2 betaGW = 1.0 -# some parameters of the extended domain -permeabilityExtDomain = 1.e-8 # [m^2] -heatCapExtDomain = 84000. # [J/(kg K)] +[Problem] +Name = columnxylol +steamFlux = -0.39571 # [mol / (s m)] +enthalpyFlux = -17452.97 # [J / (s m)] + + +[1.Component] +SolidDensity = 2650 +SolidThermalConductivity = 2.8 +SolidHeatCapacity = 850 + + +[2.Component] +SolidDensity = 2650 +SolidThermalConductivity = 2.8 +SolidHeatCapacity = 84000 diff --git a/lecture/mm/columnxylene/columnxyleneproblem.hh b/lecture/mm/columnxylene/columnxyleneproblem.hh index 6b8fd73acec915e50b4d4b6b14e0134f4d543fab..42acd613d4b023cdeba364d133d0504b6c4a9012 100644 --- a/lecture/mm/columnxylene/columnxyleneproblem.hh +++ b/lecture/mm/columnxylene/columnxyleneproblem.hh @@ -6,7 +6,7 @@ * 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. * + * (at your option) any later vesion. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * @@ -18,95 +18,145 @@ *****************************************************************************/ /*! * \file - * - * \brief Non-isothermal gas injection problem where a gas (e.g. air) - * is injected into a fully water saturated medium with a residually - * trapped NAPL contamination. + * \ingroup ThreePThreeCTests + * \brief Non-isothermal injection problem where water is injected into a + * sand column with a NAPL contamination. */ #ifndef DUMUX_COLUMNXYLENEPROBLEM_HH #define DUMUX_COLUMNXYLENEPROBLEM_HH +#include + #include +#include +#include +#include -#include -#include +#include +#include +#include +#include #include "columnxylenespatialparams.hh" #define ISOTHERMAL 0 -namespace Dumux -{ +namespace Dumux { +/*! + * \ingroup ThreePThreeCTests + * \brief Non-isothermal injection problem where water is injected into a + * sand column with a NAPL contamination. + */ template class ColumnProblem; -namespace Properties -{ -NEW_TYPE_TAG(ColumnProblem, INHERITS_FROM(BoxThreePThreeCNI, ColumnSpatialParams)); +namespace Properties { +NEW_TYPE_TAG(ColumnTypeTag, INHERITS_FROM(ThreePThreeCNI)); +NEW_TYPE_TAG(ColumnProblemBoxTypeTag, INHERITS_FROM(BoxModel, ColumnTypeTag)); // Set the grid type -SET_TYPE_PROP(ColumnProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(ColumnTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(ColumnProblem, Problem, ColumnProblem); +SET_TYPE_PROP(ColumnTypeTag, Problem, ColumnProblem); // Set the fluid system -SET_TYPE_PROP(ColumnProblem, +SET_TYPE_PROP(ColumnTypeTag, FluidSystem, FluidSystems::H2OAirXylene); -// Enable gravity -SET_BOOL_PROP(ColumnProblem, ProblemEnableGravity, true); +SET_PROP(ColumnTypeTag, SolidSystem) +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using ComponentOne = Dumux::Components::Constant<1, Scalar>; + using ComponentTwo = Dumux::Components::Constant<2, Scalar>; + static constexpr int numInertComponents = 2; + using type = SolidSystems::CompositionalSolidPhase; +}; -// Maximum tolerated relative error in the Newton method -SET_SCALAR_PROP(ColumnProblem, NewtonMaxRelativeShift, 1e-4); -} +//! The two-phase model uses the immiscible fluid state +SET_PROP(ColumnTypeTag, SolidState) +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using SolidSystem = typename GET_PROP_TYPE(TypeTag, SolidSystem); +public: + using type = CompositionalSolidState; +}; + +// Set the spatial parameters +SET_PROP(ColumnTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = ColumnSpatialParams; +}; +} // end namespace Properties /*! - * \ingroup ThreePThreeCNIBoxModel + * \ingroup ThreePThreeCModel + * \brief Non-isothermal injection problem where a water is injected into a + * sand column with a NAPL contamination. * - * */ + * The 2D domain of this test problem is 0.1m long and 1.2m high. + * Initially the column is filled with NAPL, gas and water, the NAPL saturation + * increases to the bottom of the columns, the water saturation is constant. + * Then water is injected from the top with a rate of 0.395710 mol/(s m) and + * an enthalpy of 17452.97 [J/(s m)]. * + * + * Left, right and top boundaries are Neumann boundaries. Left and right are + * no-flow boundaries, on top the injection takes places. + * The bottom is a Dirichlet boundary. + * + * This problem uses the \ref ThreePThreeCModel and \ref NIModel model. + * + * This problem should typically be simulated for 200 days. + * A good choice for the initial time step size is 1 s. + * To adjust the simulation time it is necessary to edit the input file. + * + * To run the simulation execute the following line in shell: + * ./test_box3p3cnicolumnxylol test_columnxylol_fv.input or + * ./test_cc3p3cnicolumnxylol test_columnxylol_fv.input + */ template -class ColumnProblem : public ImplicitPorousMediaProblem +class ColumnProblem : public PorousMediumFlowProblem { - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GridView::Grid Grid; - - typedef ImplicitPorousMediaProblem ParentType; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using ParentType = PorousMediumFlowProblem; - // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using ModelTraits = typename GET_PROP_TYPE(TypeTag, ModelTraits); + using Indices = typename ModelTraits::Indices; + enum + { + // primary variable indices pressureIdx = Indices::pressureIdx, switch1Idx = Indices::switch1Idx, switch2Idx = Indices::switch2Idx, temperatureIdx = Indices::temperatureIdx, + + // equation indices energyEqIdx = Indices::energyEqIdx, + contiWEqIdx = Indices::conti0EqIdx + FluidSystem::wCompIdx, //!< Index of the mass conservation equation for the water component, + contiGEqIdx = Indices::conti0EqIdx + FluidSystem::gCompIdx, //!< Index of the mass conservation equation for the gas component, + contiNEqIdx = Indices::conti0EqIdx + FluidSystem::nCompIdx, //!< Index of the mass conservation equation for the contaminant component // Phase State - threePhases = Indices::threePhases, - - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld + threePhases = Indices::threePhases }; - - 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 GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - - typedef Dune::FieldVector GlobalPosition; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; public: /*! @@ -115,10 +165,11 @@ public: * \param timeManager The time manager * \param gridView The grid view */ - ColumnProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-6) + ColumnProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { FluidSystem::init(); + name_ = getParam("Problem.Name"); } /*! @@ -131,15 +182,8 @@ public: * * This is used as a prefix for files generated by the simulation. */ - std::string name() const - { return "columnxylol"; } - - - void sourceAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - values = 0; - } + const std::string name() const + { return name_; } // \} @@ -153,16 +197,16 @@ public: * 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 + * \param globalPos The position for which the bc type should be evaluated */ - void boundaryTypes(BoundaryTypes &values, const Vertex &vertex) const + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition globalPos = vertex.geometry().center(); - - if(globalPos[1] < eps_) - values.setAllDirichlet(); + BoundaryTypes bcTypes; + if (globalPos[1] < eps_) + bcTypes.setAllDirichlet(); else - values.setAllNeumann(); + bcTypes.setAllNeumann(); + return bcTypes; } /*! @@ -170,153 +214,149 @@ public: * boundary segment. * * \param values The dirichlet values for the primary variables - * \param vertex The vertex for which the boundary type is set + * \param globalPos The position for which the bc type should be evaluated * * For this method, the \a values parameter stores primary variables. */ - void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition globalPos = vertex.geometry().center(); - - initial_(values, globalPos); + return initial_(globalPos); } /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * \param values The neumann values for the conservation equations - * \param element The finite element - * \param fvElemGeom The finite-volume geometry in the box scheme - * \param is The intersection between element and boundary + * \param element The finite element + * \param fvGeomtry The finite-volume geometry in the box scheme + * \param intersection 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 + NumEqVector neumannAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - values = 0; + NumEqVector values(0.0); // negative values for injection - if (globalPos[1] > 1.2 - eps_) + if (globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_) { - values[Indices::contiWEqIdx] = GET_RUNTIME_PARAM(TypeTag, Scalar, steamFlux); - values[Indices::contiGEqIdx] = -0.000001; - values[Indices::contiNEqIdx] = -0.00; - values[Indices::energyEqIdx] = GET_RUNTIME_PARAM(TypeTag, Scalar, enthalpyFlux); + values[FluidSystem::wCompIdx] = getParam("Problem.steamFlux"); + values[FluidSystem::gCompIdx] = -0.000001; + values[FluidSystem::nCompIdx] = -0.00; + values[Indices::energyEqIdx] = getParam("Problem.enthalpyFlux"); } + return values; } // \} - /*! - * \name Volume terms - */ - // \{ /*! * \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 + * \param globalPos The position for which the initial condition should be evaluated * * For this method, the \a values parameter stores primary * variables. */ - void initial(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - - initial_(values, globalPos); - + return initial_(globalPos); } + /*! - * \brief Return the initial phase state inside a control volume. - * - * \param globalPos The global position + * \brief Append all quantities of interest which can be derived + * from the solution of the current time step to the VTK + * writer. Adjust this in case of anisotropic permeabilities. */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const + template + void addVtkFields(VTKWriter& vtk) { - return threePhases; - } - - bool shouldWriteRestartFile() const - { - return 0; - } - - - - bool shouldWriteOutput() const - { - - int outputInterval = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, TimeManager, OutputInterval); - - return (this->timeManager().timeStepIndex() % outputInterval == 0 || - this->timeManager().willBeFinished()); - } + const auto& gg = this->fvGridGeometry(); + Kxx_.resize(gg.numDofs()); + vtk.addField(Kxx_, "permeability"); + for (const auto& element : elements(this->gridView())) + { + auto fvGeometry = localView(gg); + fvGeometry.bindElement(element); + for (const auto& scv : scvs(fvGeometry)) + Kxx_[scv.dofIndex()] = this->spatialParams().intrinsicPermeabilityAtPos(scv.dofPosition()); + } + } private: // internal method for the initial condition (reused for the // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initial_(const GlobalPosition &globalPos) const { - const Scalar y = globalPos[1]; + PrimaryVariables values; + values.setState(threePhases); + const auto y = globalPos[1]; + const auto yMax = this->fvGridGeometry().bBoxMax()[1]; values[temperatureIdx] = 296.15; values[pressureIdx] = 1.e5; - - if(y > 1.2 - eps_){ - values[switch2Idx] = 0.112; // almost no contaminant component - values[switch1Idx] = 0.005; - } else if(y < 1.2-0.3 + eps_){ // extended domain - values[switch2Idx] = 1.e-4; // almost no contaminant component - values[switch1Idx] = 0.005; - } else { - values[switch1Idx] = 0.005; - if((y<1.2-0.001+eps_)&&(y>1.2-0.0148-eps_)) values[switch2Idx] = 0+((1.2-y)/0.0148)*0.112; - if((y<1.2-0.0148-eps_)&&(y>1.2-0.0296-eps_)) values[switch2Idx] = 0.112+(((1.2-y)-0.0148)/0.0148)*(0.120-0.112); - if((y<1.2-0.0296-eps_)&&(y>1.2-0.0444-eps_)) values[switch2Idx] = 0.120+(((1.2-y)-0.0296)/0.0148)*(0.125-0.120); - if((y<1.2-0.0444-eps_)&&(y>1.2-0.0592-eps_)) values[switch2Idx] = 0.125+(((1.2-y)-0.0444)/0.0148)*(0.137-0.125); - if((y<1.2-0.0592-eps_)&&(y>1.2-0.0740-eps_)) values[switch2Idx] = 0.137+(((1.2-y)-0.0592)/0.0148)*(0.150-0.137); - if((y<1.2-0.0740-eps_)&&(y>1.2-0.0888-eps_)) values[switch2Idx] = 0.150+(((1.2-y)-0.0740)/0.0148)*(0.165-0.150); - if((y<1.2-0.0888-eps_)&&(y>1.2-0.1036-eps_)) values[switch2Idx] = 0.165+(((1.2-y)-0.0888)/0.0148)*(0.182-0.165); - if((y<1.2-0.1036-eps_)&&(y>1.2-0.1184-eps_)) values[switch2Idx] = 0.182+(((1.2-y)-0.1036)/0.0148)*(0.202-0.182); - if((y<1.2-0.1184-eps_)&&(y>1.2-0.1332-eps_)) values[switch2Idx] = 0.202+(((1.2-y)-0.1184)/0.0148)*(0.226-0.202); - if((y<1.2-0.1332-eps_)&&(y>1.2-0.1480-eps_)) values[switch2Idx] = 0.226+(((1.2-y)-0.1332)/0.0148)*(0.257-0.226); - if((y<1.2-0.1480-eps_)&&(y>1.2-0.1628-eps_)) values[switch2Idx] = 0.257+(((1.2-y)-0.1480)/0.0148)*(0.297-0.257); - if((y<1.2-0.1628-eps_)&&(y>1.2-0.1776-eps_)) values[switch2Idx] = 0.297+(((1.2-y)-0.1628)/0.0148)*(0.352-0.297); - if((y<1.2-0.1776-eps_)&&(y>1.2-0.1924-eps_)) values[switch2Idx] = 0.352+(((1.2-y)-0.1776)/0.0148)*(0.426-0.352); - if((y<1.2-0.1924-eps_)&&(y>1.2-0.2072-eps_)) values[switch2Idx] = 0.426+(((1.2-y)-0.1924)/0.0148)*(0.522-0.426); - if((y<1.2-0.2072-eps_)&&(y>1.2-0.2220-eps_)) values[switch2Idx] = 0.522+(((1.2-y)-0.2072)/0.0148)*(0.640-0.522); - if((y<1.2-0.2220-eps_)&&(y>1.2-0.2368-eps_)) values[switch2Idx] = 0.640+(((1.2-y)-0.2220)/0.0148)*(0.767-0.640); - if((y<1.2-0.2368-eps_)&&(y>1.2-0.2516-eps_)) values[switch2Idx] = 0.767+(((1.2-y)-0.2368)/0.0148)*(0.878-0.767); - if((y<1.2-0.2516-eps_)&&(y>1.2-0.2664-eps_)) values[switch2Idx] = 0.878+(((1.2-y)-0.2516)/0.0148)*(0.953-0.878); - if((y<1.2-0.2664-eps_)&&(y>1.2-0.2812-eps_)) values[switch2Idx] = 0.953+(((1.2-y)-0.2664)/0.0148)*(0.988-0.953); - if((y<1.2-0.2812-eps_)&&(y>1.2-0.3000-eps_)) values[switch2Idx] = 0.988; - } - + values[switch1Idx] = 0.005; + + if (y > yMax - eps_) + values[switch2Idx] = 0.112; + else if (y > yMax - 0.0148 - eps_) + values[switch2Idx] = 0 + ((yMax - y)/0.0148)*0.112; + else if (y > yMax - 0.0296 - eps_) + values[switch2Idx] = 0.112 + (((yMax - y) - 0.0148)/0.0148)*(0.120 - 0.112); + else if (y > yMax - 0.0444 - eps_) + values[switch2Idx] = 0.120 + (((yMax - y) - 0.0296)/0.0148)*(0.125 - 0.120); + else if (y > yMax - 0.0592 - eps_) + values[switch2Idx] = 0.125 + (((yMax - y) - 0.0444)/0.0148)*(0.137 - 0.125); + else if (y > yMax - 0.0740 - eps_) + values[switch2Idx] = 0.137 + (((yMax - y) - 0.0592)/0.0148)*(0.150 - 0.137); + else if (y > yMax - 0.0888 - eps_) + values[switch2Idx] = 0.150 + (((yMax - y) - 0.0740)/0.0148)*(0.165 - 0.150); + else if (y > yMax - 0.1036 - eps_) + values[switch2Idx] = 0.165 + (((yMax - y) - 0.0888)/0.0148)*(0.182 - 0.165); + else if (y > yMax - 0.1184 - eps_) + values[switch2Idx] = 0.182 + (((yMax - y) - 0.1036)/0.0148)*(0.202 - 0.182); + else if (y > yMax - 0.1332 - eps_) + values[switch2Idx] = 0.202 + (((yMax - y) - 0.1184)/0.0148)*(0.226 - 0.202); + else if (y > yMax - 0.1480 - eps_) + values[switch2Idx] = 0.226 + (((yMax - y) - 0.1332)/0.0148)*(0.257 - 0.226); + else if (y > yMax - 0.1628 - eps_) + values[switch2Idx] = 0.257 + (((yMax - y) - 0.1480)/0.0148)*(0.297 - 0.257); + else if (y > yMax - 0.1776 - eps_) + values[switch2Idx] = 0.297 + (((yMax - y) - 0.1628)/0.0148)*(0.352 - 0.297); + else if (y > yMax - 0.1924 - eps_) + values[switch2Idx] = 0.352 + (((yMax - y) - 0.1776)/0.0148)*(0.426 - 0.352); + else if (y > yMax - 0.2072 - eps_) + values[switch2Idx] = 0.426 + (((yMax - y) - 0.1924)/0.0148)*(0.522 - 0.426); + else if (y > yMax - 0.2220 - eps_) + values[switch2Idx] = 0.522 + (((yMax - y) - 0.2072)/0.0148)*(0.640 - 0.522); + else if (y > yMax - 0.2368 - eps_) + values[switch2Idx] = 0.640 + (((yMax - y) - 0.2220)/0.0148)*(0.767 - 0.640); + else if (y > yMax - 0.2516 - eps_) + values[switch2Idx] = 0.767 + (((yMax - y) - 0.2368)/0.0148)*(0.878 - 0.767); + else if (y > yMax - 0.2664 - eps_) + values[switch2Idx] = 0.878 + (((yMax - y) - 0.2516)/0.0148)*(0.953 - 0.878); + else if (y > yMax - 0.2812 - eps_) + values[switch2Idx] = 0.953 + (((yMax - y) - 0.2664)/0.0148)*(0.988 - 0.953); + else if (y > yMax - 0.3000 - eps_) + values[switch2Idx] = 0.988; + else + values[switch2Idx] = 1.e-4; + return values; } - const Scalar eps_; + static constexpr Scalar eps_ = 1e-6; + std::string name_; + std::vector Kxx_; }; -} //end namespace + +} //end namespace Dumux #endif diff --git a/lecture/mm/columnxylene/columnxyleneproblem2.hh b/lecture/mm/columnxylene/columnxyleneproblem2.hh deleted file mode 100644 index 1eff9091e84089948f578f767e8a44fa40c40dba..0000000000000000000000000000000000000000 --- a/lecture/mm/columnxylene/columnxyleneproblem2.hh +++ /dev/null @@ -1,325 +0,0 @@ -// -*- 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 . * - *****************************************************************************/ -/*! - * \file - * - * \brief Non-isothermal gas injection problem where a gas (e.g. air) - * is injected into a fully water saturated medium with a residually - * trapped NAPL contamination. - */ - - -// THIS IS THE SAME EXAMPLE BUT ONLY 0.3m AND WITH DIRICHLET CONDITION -// LIKE INITIAL, WHICH MEANS, WE CANNOT REALLY MODEL THE OUTFLOW AT -// THE BOTTOM, SINCE THE SATURATION CANNOT CHANGE - -#ifndef DUMUX_COLUMNXYLENEPROBLEM_HH -#define DUMUX_COLUMNXYLENEPROBLEM_HH - -#include - -#include -#include - -#include "columnxylenespatialparams2.hh" - -#define ISOTHERMAL 0 - -namespace Dumux -{ -template -class ColumnProblem; - -namespace Properties -{ -NEW_TYPE_TAG(ColumnProblem, INHERITS_FROM(BoxThreePThreeCNI, ColumnSpatialParams)); - -// Set the grid type -SET_TYPE_PROP(ColumnProblem, Grid, Dune::YaspGrid<2>); - -// Set the problem property -SET_TYPE_PROP(ColumnProblem, Problem, ColumnProblem); - -// Set the fluid system -SET_TYPE_PROP(ColumnProblem, - FluidSystem, - FluidSystems::H2OAirXylene); - -// Enable gravity -SET_BOOL_PROP(ColumnProblem, ProblemEnableGravity, true); - -// Maximum tolerated relative error in the Newton method -SET_SCALAR_PROP(ColumnProblem, NewtonMaxRelativeShift, 1e-4); -} - - -/*! - * \ingroup ThreePThreeCNIBoxModel - * - * */ -template -class ColumnProblem : public ImplicitPorousMediaProblem -{ - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GridView::Grid Grid; - - typedef ImplicitPorousMediaProblem ParentType; - - // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - - pressureIdx = Indices::pressureIdx, - switch1Idx = Indices::switch1Idx, - switch2Idx = Indices::switch2Idx, - temperatureIdx = Indices::temperatureIdx, - energyEqIdx = Indices::energyEqIdx, - - // Phase State - threePhases = Indices::threePhases, - - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld - }; - - - 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 GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - - typedef Dune::FieldVector GlobalPosition; - -public: - /*! - * \brief The constructor - * - * \param timeManager The time manager - * \param gridView The grid view - */ - ColumnProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-6) - { - FluidSystem::init(); - } - - /*! - * \name Problem parameters - */ - // \{ - - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { return "columnxylol"; } - - - void sourceAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - values = 0; - } - - // \} - - /*! - * \name Boundary conditions - */ - // \{ - - /*! - * \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 - { - const GlobalPosition globalPos = vertex.geometry().center(); - - if(globalPos[1] < eps_) - values.setAllDirichlet(); - else - values.setAllNeumann(); - } - - /*! - * \brief Evaluate the boundary conditions for a dirichlet - * boundary segment. - * - * \param values The dirichlet values for the primary variables - * \param vertex The vertex for which the boundary type is set - * - * For this method, the \a values parameter stores primary variables. - */ - void dirichlet(PrimaryVariables &values, const Vertex &vertex) const - { - const GlobalPosition globalPos = vertex.geometry().center(); - - initial_(values, globalPos); - } - - /*! - * \brief Evaluate the boundary conditions for a neumann - * boundary segment. - * - * \param values The neumann values for the conservation equations - * \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 - { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - values = 0; - - // negative values for injection - if (globalPos[1] > 0.3 - eps_) - { - values[Indices::contiWEqIdx] = GET_RUNTIME_PARAM(TypeTag, Scalar, steamFlux); - values[Indices::contiGEqIdx] = -0.000001; - values[Indices::contiNEqIdx] = -0.00; - values[Indices::energyEqIdx] = GET_RUNTIME_PARAM(TypeTag, Scalar, enthalpyFlux); - } - } - - // \} - - /*! - * \name Volume terms - */ - // \{ - - /*! - * \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 - { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - - initial_(values, globalPos); - - } - - /*! - * \brief Return the initial phase state inside a control volume. - * - * \param globalPos The global position - */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const - { - return threePhases; - } - - bool shouldWriteRestartFile() const - { - return 0; - } - - - - bool shouldWriteOutput() const - { - - int outputInterval = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, TimeManager, OutputInterval); - - return (this->timeManager().timeStepIndex() % outputInterval == 0 || - this->timeManager().willBeFinished()); - } - - - -private: - // internal method for the initial condition (reused for the - // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - const Scalar y = globalPos[1]; - - values[temperatureIdx] = 296.15; - values[pressureIdx] = 1.e5; - - if(y > 0.3 - eps_){ - values[switch2Idx] = 0.112; // almost no contaminant component - values[switch1Idx] = 0.005; - } else { - values[switch1Idx] = 0.005; - if((y<0.3-0.001+eps_)&&(y>0.3-0.0148-eps_)) values[switch2Idx] = 0+((0.3-y)/0.0148)*0.112; - if((y<0.3-0.0148-eps_)&&(y>0.3-0.0296-eps_)) values[switch2Idx] = 0.112+(((0.3-y)-0.0148)/0.0148)*(0.120-0.112); - if((y<0.3-0.0296-eps_)&&(y>0.3-0.0444-eps_)) values[switch2Idx] = 0.120+(((0.3-y)-0.0296)/0.0148)*(0.125-0.120); - if((y<0.3-0.0444-eps_)&&(y>0.3-0.0592-eps_)) values[switch2Idx] = 0.125+(((0.3-y)-0.0444)/0.0148)*(0.137-0.125); - if((y<0.3-0.0592-eps_)&&(y>0.3-0.0740-eps_)) values[switch2Idx] = 0.137+(((0.3-y)-0.0592)/0.0148)*(0.150-0.137); - if((y<0.3-0.0740-eps_)&&(y>0.3-0.0888-eps_)) values[switch2Idx] = 0.150+(((0.3-y)-0.0740)/0.0148)*(0.165-0.150); - if((y<0.3-0.0888-eps_)&&(y>0.3-0.1036-eps_)) values[switch2Idx] = 0.165+(((0.3-y)-0.0888)/0.0148)*(0.182-0.165); - if((y<0.3-0.1036-eps_)&&(y>0.3-0.1184-eps_)) values[switch2Idx] = 0.182+(((0.3-y)-0.1036)/0.0148)*(0.202-0.182); - if((y<0.3-0.1184-eps_)&&(y>0.3-0.1332-eps_)) values[switch2Idx] = 0.202+(((0.3-y)-0.1184)/0.0148)*(0.226-0.202); - if((y<0.3-0.1332-eps_)&&(y>0.3-0.1480-eps_)) values[switch2Idx] = 0.226+(((0.3-y)-0.1332)/0.0148)*(0.257-0.226); - if((y<0.3-0.1480-eps_)&&(y>0.3-0.1628-eps_)) values[switch2Idx] = 0.257+(((0.3-y)-0.1480)/0.0148)*(0.297-0.257); - if((y<0.3-0.1628-eps_)&&(y>0.3-0.1776-eps_)) values[switch2Idx] = 0.297+(((0.3-y)-0.1628)/0.0148)*(0.352-0.297); - if((y<0.3-0.1776-eps_)&&(y>0.3-0.1924-eps_)) values[switch2Idx] = 0.352+(((0.3-y)-0.1776)/0.0148)*(0.426-0.352); - if((y<0.3-0.1924-eps_)&&(y>0.3-0.2072-eps_)) values[switch2Idx] = 0.426+(((0.3-y)-0.1924)/0.0148)*(0.522-0.426); - if((y<0.3-0.2072-eps_)&&(y>0.3-0.2220-eps_)) values[switch2Idx] = 0.522+(((0.3-y)-0.2072)/0.0148)*(0.640-0.522); - if((y<0.3-0.2220-eps_)&&(y>0.3-0.2368-eps_)) values[switch2Idx] = 0.640+(((0.3-y)-0.2220)/0.0148)*(0.767-0.640); - if((y<0.3-0.2368-eps_)&&(y>0.3-0.2516-eps_)) values[switch2Idx] = 0.767+(((0.3-y)-0.2368)/0.0148)*(0.878-0.767); - if((y<0.3-0.2516-eps_)&&(y>0.3-0.2664-eps_)) values[switch2Idx] = 0.878+(((0.3-y)-0.2516)/0.0148)*(0.953-0.878); - if((y<0.3-0.2664-eps_)&&(y>0.3-0.2812-eps_)) values[switch2Idx] = 0.953+(((0.3-y)-0.2664)/0.0148)*(0.988-0.953); - if((y<0.3-0.2812-eps_)&&(y>0.3-0.3000-eps_)) values[switch2Idx] = 0.988; - } - - } - - const Scalar eps_; -}; -} //end namespace - -#endif diff --git a/lecture/mm/columnxylene/columnxylenespatialparams.hh b/lecture/mm/columnxylene/columnxylenespatialparams.hh index 0b4550e481dd58240ad08f542989103367e531f9..903d62cf2c82203855467e2ba7373175818ea595 100644 --- a/lecture/mm/columnxylene/columnxylenespatialparams.hh +++ b/lecture/mm/columnxylene/columnxylenespatialparams.hh @@ -24,128 +24,81 @@ #ifndef DUMUX_COLUMNXYLENE_SPATIAL_PARAMS_HH #define DUMUX_COLUMNXYLENE_SPATIAL_PARAMS_HH -#include -#include +#include +#include +#include #include #include #include -namespace Dumux -{ - -//forward declaration -template -class ColumnSpatialParams; - -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(ColumnSpatialParams); - -// Set the spatial parameters -SET_TYPE_PROP(ColumnSpatialParams, SpatialParams, Dumux::ColumnSpatialParams); - -// Set the material Law -SET_PROP(ColumnSpatialParams, MaterialLaw) -{ - private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef RegularizedParkerVanGen3P EffectiveLaw; - public: - // define the material law parameterized by absolute saturations - typedef EffToAbsLaw type; -}; -} +namespace Dumux { /*! * \ingroup ThreePThreeCNIModel * * \brief Definition of the spatial parameters for the column problem */ -template -class ColumnSpatialParams : public ImplicitSpatialParams +template +class ColumnSpatialParams +: public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx - }; - - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldVector Vector; + using GridView = typename FVGridGeometry::GridView; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using Element = typename GridView::template Codim<0>::Entity; + using ParentType = FVSpatialParams>; - 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, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; - + using GlobalPosition = typename SubControlVolume::GlobalPosition; + using EffectiveLaw = RegularizedParkerVanGen3P; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; + using PermeabilityType = Scalar; /*! * \brief The constructor * * \param gv The grid view */ - ColumnSpatialParams(const GridView &gv) - : ParentType(gv) + ColumnSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { // intrinsic permeabilities - fineK_ = GET_RUNTIME_PARAM(TypeTag, Scalar, permeability); - coarseK_ = GET_RUNTIME_PARAM(TypeTag, Scalar, permeabilityExtDomain); + fineK_ = getParam("SpatialParams.permeability"); + coarseK_ = getParam("SpatialParams.permeabilityExtDomain"); // porosities - finePorosity_ = GET_RUNTIME_PARAM(TypeTag, Scalar, porosity); - coarsePorosity_ = GET_RUNTIME_PARAM(TypeTag, Scalar, porosity); - - // specific heat capacities - fineHeatCap_ = 850.; - coarseHeatCap_ = GET_RUNTIME_PARAM(TypeTag, Scalar, heatCapExtDomain); - - // heat conductivity of granite - lambdaSolid_ = 2.8; + finePorosity_ = 0.46; + coarsePorosity_ = 0.46; // residual saturations - fineMaterialParams_.setSwr(GET_RUNTIME_PARAM(TypeTag, Scalar, swr)); - fineMaterialParams_.setSnr(GET_RUNTIME_PARAM(TypeTag, Scalar, snr)); - fineMaterialParams_.setSgr(GET_RUNTIME_PARAM(TypeTag, Scalar, Sgr)); - coarseMaterialParams_.setSwr(GET_RUNTIME_PARAM(TypeTag, Scalar, swr)); - coarseMaterialParams_.setSnr(GET_RUNTIME_PARAM(TypeTag, Scalar, snr)); - coarseMaterialParams_.setSgr(GET_RUNTIME_PARAM(TypeTag, Scalar, Sgr)); + fineMaterialParams_.setSwr(0.12); + fineMaterialParams_.setSnr(0.10); + fineMaterialParams_.setSgr(0.01); + coarseMaterialParams_.setSwr(0.12); + coarseMaterialParams_.setSnr(0.10); + coarseMaterialParams_.setSgr(0.01); // parameters for the 3phase van Genuchten law - fineMaterialParams_.setVgAlpha(GET_RUNTIME_PARAM(TypeTag, Scalar, vanGenuchtenAlpha)); + fineMaterialParams_.setVgAlpha(0.0005); coarseMaterialParams_.setVgAlpha(0.5); - fineMaterialParams_.setVgn(GET_RUNTIME_PARAM(TypeTag, Scalar, vanGenuchtenN)); + fineMaterialParams_.setVgn(4.0); coarseMaterialParams_.setVgn(4.0); coarseMaterialParams_.setKrRegardsSnr(false); fineMaterialParams_.setKrRegardsSnr(true); // parameters for the scaling of capillary pressure (GW = 1); - fineMaterialParams_.setBetaNw(GET_RUNTIME_PARAM(TypeTag, Scalar, betaNW)); - fineMaterialParams_.setBetaGn(GET_RUNTIME_PARAM(TypeTag, Scalar, betaGN)); - fineMaterialParams_.setBetaGw(GET_RUNTIME_PARAM(TypeTag, Scalar, betaGW)); - coarseMaterialParams_.setBetaNw(GET_RUNTIME_PARAM(TypeTag, Scalar, betaNW)); - coarseMaterialParams_.setBetaGn(GET_RUNTIME_PARAM(TypeTag, Scalar, betaGN)); - coarseMaterialParams_.setBetaGw(GET_RUNTIME_PARAM(TypeTag, Scalar, betaGW)); + fineMaterialParams_.setBetaNw(1.83); + fineMaterialParams_.setBetaGn(2.2); + fineMaterialParams_.setBetaGw(1.0); + coarseMaterialParams_.setBetaNw(1.83); + coarseMaterialParams_.setBetaGn(2.2); + coarseMaterialParams_.setBetaGw(1.0); // parameters for adsorption coarseMaterialParams_.setKdNAPL(0.); @@ -166,14 +119,15 @@ public: * \param fvElemGeom The current finite volume geometry of the element * \param scvIdx The index of the sub-control volume */ - const Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + template + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { - const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) + const auto& globalPos = scv.dofPosition(); + if (isFineMaterial_(globalPos)) return fineK_; - else return coarseK_; + return coarseK_; } /*! @@ -184,18 +138,27 @@ public: * \param scvIdx The local index of the sub-control volume where * the porosity needs to be defined */ - double porosity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + + template + Scalar inertVolumeFractionAtPos(const GlobalPosition& globalPos, + int compIdx) const { - const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return finePorosity_; - else - return coarsePorosity_; + if (compIdx == SolidSystem::comp0Idx) + { + if (isFineMaterial_(globalPos)) + return 1-finePorosity_; + else + return 0; + } + else + { + if (isFineMaterial_(globalPos)) + return 0; + else + return 1-coarsePorosity_; + } } - /*! * \brief return the parameter object for the Brooks-Corey material law which depends on the position * @@ -203,75 +166,28 @@ public: * \param fvElemGeom The current finite volume geometry of the element * \param scvIdx The index of the sub-control volume */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + template + const MaterialLawParams& materialLawParams(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { - const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) + const auto& globalPos = scv.dofPosition(); + if (isFineMaterial_(globalPos)) return fineMaterialParams_; - else - return coarseMaterialParams_; - } - /*! - * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidHeatCapacity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineHeatCap_; else - return coarseHeatCap_; - } - - /*! - * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidDensity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return 2650; // density of sand [kg/m^3] + return coarseMaterialParams_; } - /*! - * \brief Returns the thermal conductivity \f$[W/m^2]\f$ of the porous material. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the heat capacity needs to be defined - */ - Scalar solidThermalConductivity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return lambdaSolid_; - } private: bool isFineMaterial_(const GlobalPosition &pos) const { - if (0.90 < pos[1] + eps_) + if (0.90 - eps_ < pos[1] + eps_) { return true; } + else { return false; @@ -295,6 +211,6 @@ private: static constexpr Scalar eps_ = 1.5e-7; }; -} +} // end namespace Dumux -#endif +#endif // DUMUX_COLUMNXYLENE_SPATIAL_PARAMS_HH diff --git a/lecture/mm/columnxylene/columnxylenespatialparams2.hh b/lecture/mm/columnxylene/columnxylenespatialparams2.hh deleted file mode 100644 index 25b22939284f9cbaf11f9cf45ec6a09e6f2aebca..0000000000000000000000000000000000000000 --- a/lecture/mm/columnxylene/columnxylenespatialparams2.hh +++ /dev/null @@ -1,268 +0,0 @@ -// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -// vi: set et ts=4 sw=4 sts=4: -/***************************************************************************** - * Copyright (C) 2011- by Holger Class * - * Copyright (C) 2008-2010 by Andreas Lauser * - * Copyright (C) 2008-2009 by Klaus Mosthaf * - * Institute for Modelling Hydraulic and Environmental Systems * - * University of Stuttgart, Germany * - * email: .@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 . * - *****************************************************************************/ -/*! - * \file - * - * \brief Definition of the spatial parameters for the column problem. - */ -#ifndef DUMUX_COLUMNXYLENE_SPATIAL_PARAMS_HH -#define DUMUX_COLUMNXYLENE_SPATIAL_PARAMS_HH - -#include -#include -#include -#include -#include - -namespace Dumux -{ - -//forward declaration -template -class ColumnSpatialParams; - -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(ColumnSpatialParams); - -// Set the spatial parameters -SET_TYPE_PROP(ColumnSpatialParams, SpatialParams, Dumux::ColumnSpatialParams); - -// Set the material Law -SET_PROP(ColumnSpatialParams, MaterialLaw) -{ - private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef RegularizedParkerVanGen3P EffectiveLaw; - public: - // define the material law parameterized by absolute saturations - typedef EffToAbsLaw type; -}; -} - -/*! - * \ingroup ThreePThreeCNIModel - * - * \brief Definition of the spatial parameters for the column problem - */ -template -class ColumnSpatialParams : public ImplicitSpatialParams -{ - typedef ImplicitSpatialParams ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx - }; - - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldVector Vector; - - - 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, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; - - -public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; - - /*! - * \brief The constructor - * - * \param gv The grid view - */ - ColumnSpatialParams(const GridView &gv) - : ParentType(gv) - { - // intrinsic permeabilities - fineK_ = GET_RUNTIME_PARAM(TypeTag, Scalar, permeability); - - // porosities - finePorosity_ = GET_RUNTIME_PARAM(TypeTag, Scalar, porosity); - - // specific heat capacities - fineHeatCap_ = 850.; - - // heat conductivity of granite - lambdaSolid_ = 2.8; - - // residual saturations - fineMaterialParams_.setSwr(GET_RUNTIME_PARAM(TypeTag, Scalar, swr)); - fineMaterialParams_.setSnr(GET_RUNTIME_PARAM(TypeTag, Scalar, snr)); - fineMaterialParams_.setSgr(GET_RUNTIME_PARAM(TypeTag, Scalar, Sgr)); - - // parameters for the 3phase van Genuchten law - fineMaterialParams_.setVgAlpha(GET_RUNTIME_PARAM(TypeTag, Scalar, vanGenuchtenAlpha)); - fineMaterialParams_.setVgn(GET_RUNTIME_PARAM(TypeTag, Scalar, vanGenuchtenN)); - - fineMaterialParams_.setKrRegardsSnr(true); - - // parameters for the scaling of capillary pressure (GW = 1); - fineMaterialParams_.setBetaNw(GET_RUNTIME_PARAM(TypeTag, Scalar, betaNW)); - fineMaterialParams_.setBetaGn(GET_RUNTIME_PARAM(TypeTag, Scalar, betaGN)); - fineMaterialParams_.setBetaGw(GET_RUNTIME_PARAM(TypeTag, Scalar, betaGW)); - - // parameters for adsorption - fineMaterialParams_.setKdNAPL(0.); - fineMaterialParams_.setRhoBulk(1500.); - } - - ~ColumnSpatialParams() - {} - - - /*! - * \brief Update the spatial parameters with the flow solution - * after a timestep. - * - * \param globalSolution The global solution vector - */ - void update(const SolutionVector &globalSolution) - { - }; - - /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. - * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume - */ - const Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return fineK_; - } - - /*! - * \brief Define the porosity \f$[-]\f$ of the spatial parameters - * - * \param element The finite element - * \param fvElemGeom The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined - */ - double porosity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return finePorosity_; - } - - - /*! - * \brief return the parameter object for the Brooks-Corey material law which depends on the position - * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume - */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return fineMaterialParams_; - } - - /*! - * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidHeatCapacity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return fineHeatCap_; - } - - /*! - * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidDensity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return 2650; // density of sand [kg/m^3] - } - - /*! - * \brief Returns the thermal conductivity \f$[W/m^2]\f$ of the porous material. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the heat capacity needs to be defined - */ - Scalar solidThermalConductivity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return lambdaSolid_; - } - -private: - Scalar fineK_; - - Scalar finePorosity_; - - Scalar fineHeatCap_; - - MaterialLawParams fineMaterialParams_; - - Scalar lambdaSolid_; -}; - -} - -#endif diff --git a/lecture/mm/columnxylene/description/columnxyleneexercise.tex b/lecture/mm/columnxylene/description/columnxyleneexercise.tex index b17fb0b8ed1996a8cfb0a9680676c10a6e1895b6..8a0accaeedffc6edcde403fd90273d57c6442081 100644 --- a/lecture/mm/columnxylene/description/columnxyleneexercise.tex +++ b/lecture/mm/columnxylene/description/columnxyleneexercise.tex @@ -45,7 +45,6 @@ \setcounter{secnumdepth}{4} % allows numbers for sub-sub-section \input{problemdescription-columnxylene} -\input{paraview-columnxylene} \bibliography{./lit} \bibliographystyle{plain} diff --git a/lecture/mm/columnxylene/description/paraview-columnxylene.tex b/lecture/mm/columnxylene/description/paraview-columnxylene.tex deleted file mode 100644 index 56f5cf062c0916835966ca067eda09273cd9e872..0000000000000000000000000000000000000000 --- a/lecture/mm/columnxylene/description/paraview-columnxylene.tex +++ /dev/null @@ -1,49 +0,0 @@ -\section{Visualization - Column Model} -\subsection{Instructions} -\begin{enumerate} - \item Start {\small paraview \&}. - \item Open your results {\small columnxylol.pvd}. - \item Now you can see the file on the left in the {\small Pipeline-Browser}. Your simulation results will be shown by clicking the {\small Apply}-Button in the {\small Object Inspector} below. - \item As discussed in the description of this exercise on Page \pageref{extended-domain}, the simulation also contains an "extended domain"; however, the column we are interested in is only the top 30~cm of the simulated model domain. Therefore, we cut this part off, by {\small Filters / Common / Clip}. Set (0, 0.9, 0) as origin and (0, 1, 0) as normal vector. (The length of the normal vector is not important.) - \item You can now have, for example, a quick look at all different phase saturations how they develop over time by selecting the variable from \includegraphics[scale=0.5]{eps/variable.eps} and then click on run \includegraphics[scale=0.5]{eps/play.eps}. - \item Now we go into a more thorough analysis. First, open a second window by clicking \includegraphics[scale=0.5]{eps/horizontalSplit.eps}. - \item We want to create profiles, e.g., of temperature, pressure, or saturations with {\small Filters / Data Analysis / Plot Over Line}. Let the line go from point (0, 1.2, 0) to (0, 0.9, 0), then click the {\small Apply}-button. - \item Go to {\small Display} and unselect all variables by clicking \includegraphics[scale=0.5]{eps/selectAllVariables.eps} twice. Afterwards, reselect, e.g., the temperature and the gas pressure. The pressure can be shown on the {\small Bottom-right}-Axis, which can be selected in {\small Chart Axes}. - \item Now, we adjust the values of the temperature axis. Therefore click \includegraphics[scale=0.5]{eps/editPlotOverLine.eps} in the upper left of the plot-window. Take the following steps: \begin{itemize} - \item {\small General} $\rightarrow$ give a title - \item {\small Left Axis / Layout / Specify the Axis Range} $\rightarrow$ 273 - 378 [$^\circ$C] - \item {\small Bottom Axis / Layout / Specify the Axis Range} $\rightarrow$ 0.0 - 0.3 [m] - \item {\small Right Axis / Layout / Specify the Axis Range} $\rightarrow$ 100000 - 106000 [Pa] - \end{itemize} - \item We want to compare now the temperature plateaus obtained in the simulations with those of the measurements. Therefore, we need a plot of temperature at a certain location over time. \\ - We need {\small Filters / Data Analysis / Plot Selection Over Time}. But before this, it is necessary to select a grid point for which the plot over time is done. - \item Finally, save a screenshoot: select the plot-window, click {\small File / Save Screenshot} and set the resolution to $600 x 800$ so that all graphs match in size. -\end{enumerate} - -\subsection{Macros} - -\begin{enumerate} - \item Repeat steps 1 and 2. - \item Add the {\small col-xylol-macro.py} via {\small Macros / Add new Macro}. - \item Play the macro by clicking {\small Macros / col-xylol-macro}.\\ - \underline{Hint 1:} If the {\small Macros} tab remains empty, then start with {\it paraview3.12 \&} from the commandline.\\ - \underline{Hint 2:} To rearrange the plot just change the maximum for the x-axis to 3.1 {\small Apply} and change it back again (step 9). -\end{enumerate} - -\subsection{Results} - -\begin{figure}[h!] -\begin{minipage}[h!]{7cm} - \centering - \includegraphics[width=7.0cm]{./eps/col-xylol-sn.eps} - \caption{Saturation of Xylol in the column.} - \label{col-xylol-sn} -\end{minipage} -\hfill -\begin{minipage}[h!]{7cm} - \centering - \includegraphics[width=7.0cm]{./eps/col-xylol-graph.eps} - \caption{Temperature and gaspressure distribution over column height @950s.} - \label{col-xylol-graph} -\end{minipage} -\end{figure} diff --git a/lecture/mm/columnxylene/description/problemdescription-columnxylene.tex b/lecture/mm/columnxylene/description/problemdescription-columnxylene.tex index fa254890da33ea1911f76b95493691c4d564e2fb..da6b3ce9610a9611f2cd836ab7436e7c48db4dd9 100644 --- a/lecture/mm/columnxylene/description/problemdescription-columnxylene.tex +++ b/lecture/mm/columnxylene/description/problemdescription-columnxylene.tex @@ -357,18 +357,14 @@ influence of temperature on the system properties. The following (inclomplete) list of model parameters includes some parameters which could be varied to see how the model reacts on changes. \begin{verbatim} -Swr 0.12 -Snr 0.10 -Swrx 0.12 /* Swr+Snr */ -Sgr 0.01 -permeability 1.400E-11 +permeability 1.400E-11 # [m^2] vg_alpha 0.0005 vg_n 4.0 beta_gn 2.20 beta_nw 1.83 beta_gw 1.00 -enthalpy_st -17452.97 /* [J/(s m)] */ -q -0.395710 /* [mol/(s m)] */ +enthalpyFlux -17452.97 # [J/(s m)] +steamFlux -0.39571 # [mol/(s m)] \end{verbatim} \subsection{Questions} @@ -391,3 +387,7 @@ can be observed during the experiment (at 92$^\circ$C and at 100$^\circ$C)? {\it enthalpy\_st}. \\ Test your calculated values with the numerical model. \end{enumerate} + +Take care when you plot the results. There is a 'physically meaningful' part of the model domain +as well as the extended domain. You might want to 'clip' that away in paraview. +And then it makes sense to use a 'Plot-Over-Line' to visualize the results. diff --git a/lecture/mm/convectivemixing/CMakeLists.txt b/lecture/mm/convectivemixing/CMakeLists.txt index e80971ab2e8339b700fcad445a1b7329e7d48838..6c1f940c73e150c4fb4edf037b185aaa285a03a7 100644 --- a/lecture/mm/convectivemixing/CMakeLists.txt +++ b/lecture/mm/convectivemixing/CMakeLists.txt @@ -1,11 +1,17 @@ add_input_file_links() -add_dumux_test(convmixexercise convmixexercise convmixexercise.cc - ${CMAKE_CURRENT_BINARY_DIR}/convmixexercise) +dune_add_test(NAME convmixexercise + SOURCES convmixexercise.cc + COMMAND ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py + CMD_ARGS --script fuzzy + --files ${CMAKE_SOURCE_DIR}/lecture/references/convmix-reference.vtu + ${CMAKE_CURRENT_BINARY_DIR}/convmix-00019.vtu + --command "${CMAKE_CURRENT_BINARY_DIR}/convmixexercise") + # headers for installation and headercheck install(FILES - convmixco2tables.hh + convmixexercise.cc convmixproblem.hh convmixspatialparameters.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lecture/mm/convectivemixing) diff --git a/lecture/mm/convectivemixing/convmixexercise.cc b/lecture/mm/convectivemixing/convmixexercise.cc index 7555d6208e87cf44d35ae223c5e9ff6580494687..e177fd3eae0989eb9776faca892fce7f7eca99de 100644 --- a/lecture/mm/convectivemixing/convmixexercise.cc +++ b/lecture/mm/convectivemixing/convmixexercise.cc @@ -16,33 +16,191 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -#include "config.h" +/*! + * \file + * + * \brief test for the 1pnc model + */ +#include + #include "convmixproblem.hh" -#include -//! Prints a usage/help message if something goes wrong or the user asks for help -void usage(const char *progName, const std::string &errorMsg) /*@\label{tutorial-coupled:usage-function}@*/ +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +int main(int argc, char** argv) try { - std::cout << "\nUsage: " << progName << " [options]\n"; - if (errorMsg.size() > 0) - std::cout << errorMsg << "\n"; - std::cout << "\n" - << "The list of mandatory arguments for this program is:\n" - << "\t-TEnd The end of the simulation [s]\n" - << "\t-DtInitial The initial timestep size [s]\n" - << "\t-Grid.UpperRightX The x-coordinate value of the upper-right corner of the grid\n" - << "\t-Grid.UpperRightY The y-coordinate value of the upper-right corner of the grid\n" - << "\t-Grid.NumberOfCellsX The grid's x-resolution\n" - << "\t-Grid.NumberOfCellsY The grid's y-resolution\n" - << "\t-Problem.Name The name of the output files\n" - << "\t-Problem.DepthBOR The depth of the bottom of reservoir\n" - << "\t-Problem.Salinity The salinity of the brine phase [kg/kg]\n" - << "\t-SpatialParams.Permeability The intrinsic permeability\n" - << "\n"; + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(ConvmixBoxTypeTag); + + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // initialize parameter tree + Parameters::init(argc, argv); + + ////////////////////////////////////////////////////////////////////// + // try to create a grid (from the given grid file or the input file) + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + auto tEnd = getParam("TimeLoop.TEnd"); + auto dt = getParam("TimeLoop.DtInitial"); + auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength")); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = ILU0BiCGSTABBackend; + auto linearSolver = std::make_shared(); + + // the non-linear solver + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + // if episode length was specificied output only at the end of episodes + if (!hasParam("TimeLoop.EpisodeLength") || timeLoop->isCheckPoint() || timeLoop->finished() || timeLoop->timeStepIndex() == 1) + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/false); + + return 0; } -int main(int argc, char** argv) +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(ConvectiveMixingProblem) TypeTag; - return Dumux::start < TypeTag > (argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/convectivemixing/convmixexercise.input b/lecture/mm/convectivemixing/convmixexercise.input index 6d370a20e9ccacd2d5736689a2d59569e9ccdf52..cc456308bbdb0468695f05c16c8568d0db83e4a3 100644 --- a/lecture/mm/convectivemixing/convmixexercise.input +++ b/lecture/mm/convectivemixing/convmixexercise.input @@ -1,18 +1,27 @@ -tau = 0.706 - -[TimeManager] -TEnd = 1.e9 # duration of the simulation [s] -DtInitial = 10 # initial time step size [s] +[TimeLoop] +DtInitial = 10 # [s] +TEnd = 1.e9 # [s] EpisodeLength = 1.e7 [Grid] -UpperRight = 5 5 # upper right corner of the grid [m] -Cells = 10 10 # number of cells in (x, y) direction [-] +LowerLeft = 0 0 +UpperRight = 5 5 +Cells = 30 30 + +[SpatialParams] +Tortuosity = 0.706 +Permeability = 3e-13 [Problem] -Name = convmix # [-] the name of the output files +Name = convmix # name passed to the output routines +EnableGravity = 1 # enable gravity DepthBOR = 1400 # [m] depth below ground surface + +[FluidSystem] Salinity = 0.05 # [kg/kg] brine salinity -[SpatialParams] -Permeability = 3e-13 # [m^2] reservoir permeability +[Vtk] +AddVelocity = 1 # enable velocity output + +[BinaryCoefficients] +LiquidDiffCoeff = 2.e-9 diff --git a/lecture/mm/convectivemixing/convmixproblem.hh b/lecture/mm/convectivemixing/convmixproblem.hh index 657d48fce621d053b48285f10fcc223cfc71ef32..4f3f1ea2441144f576318f4d4b27f7bda266678c 100644 --- a/lecture/mm/convectivemixing/convmixproblem.hh +++ b/lecture/mm/convectivemixing/convmixproblem.hh @@ -16,108 +16,147 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -#ifndef DUMUX_CONVECTIVE_MIXING_PROBLEM_HH -#define DUMUX_CONVECTIVE_MIXING_PROBLEM_HH +/** + * \file + * \ingroup OnePNCTests + * \brief Definition of a problem, for the 1pnc problem: + * Component transport of nitrogen dissolved in the water phase. + */ +#ifndef DUMUX_CONVMIXING_PROBLEM_HH +#define DUMUX_CONVMIXING_PROBLEM_HH + +#if HAVE_UG +#include +#endif +#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include - #include +#include + +#include + #include "convmixspatialparams.hh" -namespace Dumux -{ +namespace Dumux { + template -class ConvectiveMixingProblem; +class ConvmixProblem; -namespace Properties -{ -NEW_TYPE_TAG(ConvectiveMixingProblem, INHERITS_FROM(BoxOnePTwoC, ConvMixSpatialParams)); +namespace Properties { -// Set the grid type -SET_TYPE_PROP(ConvectiveMixingProblem, Grid, Dune::YaspGrid<2>); +NEW_TYPE_TAG(ConvmixTypeTag, INHERITS_FROM(OnePNC)); +NEW_TYPE_TAG(ConvmixBoxTypeTag, INHERITS_FROM(BoxModel, ConvmixTypeTag)); -// Set the problem property -SET_TYPE_PROP(ConvectiveMixingProblem, Problem, ConvectiveMixingProblem); +SET_TYPE_PROP(ConvmixTypeTag, Grid, Dune::YaspGrid<2>); -// Set fluid configuration -SET_TYPE_PROP(ConvectiveMixingProblem, FluidSystem,BrineCO2FluidSystem); +// Set the problem property +SET_TYPE_PROP(ConvmixTypeTag, Problem, ConvmixProblem); -//! The constant tau function is used for the effective diffusivity -SET_PROP(ConvectiveMixingProblem, EffectiveDiffusivityModel) +SET_PROP(ConvmixTypeTag, FluidSystem) { -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef DiffusivityConstantTau type; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using BrineCO2 = FluidSystems::BrineCO2>, + FluidSystems::BrineCO2DefaultPolicy>; + using type = FluidSystems::OnePAdapter; }; -// Set the CO2 table to be used; in this case not the the default table -SET_TYPE_PROP(ConvectiveMixingProblem, CO2Table, CO2TablesBenchmarkThree::CO2Tables); +// Set the spatial parameters +SET_PROP(ConvmixTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = ConvmixSpatialParams; +}; -//Define whether mole(true) or mass (false) fractions are used -SET_BOOL_PROP(ConvectiveMixingProblem, UseMoles, false); +SET_PROP(ConvmixTypeTag, EffectiveDiffusivityModel) +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = DiffusivityConstantTortuosity; +}; -// Disable gravity -SET_BOOL_PROP(ConvectiveMixingProblem, ProblemEnableGravity, true); +// Define whether mole(true) or mass (false) fractions are used +SET_BOOL_PROP(ConvmixTypeTag, UseMoles, false); } + +/*! + * \ingroup OnePNCTests + * \brief Definition of a problem, for the 1pnc problem: + * + * This problem uses the \ref OnePNCModel model. + * + */ template -class ConvectiveMixingProblem : public ImplicitPorousMediaProblem +class ConvmixProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; - typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; - typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + using ParentType = PorousMediumFlowProblem; + + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using Element = typename GridView::template Codim<0>::Entity; + + using CO2 = Components::CO2; + using Brine_CO2 = BinaryCoeff::Brine_CO2; // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld, - + enum + { // indices of the primary variables - pressureIdx = Indices::pressureIdx, // index for the pressure primary variable - massFracIdx = Indices::massOrMoleFracIdx, // index for the CO2 massfraction primary variable + pressureIdx = Indices::pressureIdx, - // indices of the equations - conti0EqIdx = Indices::conti0EqIdx, // index for the continuity equation - transportEqIdx = Indices::transportEqIdx, // index for the transport equation - }; + // component indices + BrineIdx = FluidSystem::MultiPhaseFluidSystem::comp0Idx, + CO2Idx = FluidSystem::MultiPhaseFluidSystem::CO2Idx, + // equation indices + conti0EqIdx = Indices::conti0EqIdx, + contiCO2EqIdx = conti0EqIdx + CO2Idx + }; - typedef typename GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim<0>::Iterator ElementIterator; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; + //! property that defines whether mole or mass fractions are used + static constexpr bool useMoles = GET_PROP_VALUE(TypeTag, UseMoles); + static const bool isBox = GET_PROP_TYPE(TypeTag, FVGridGeometry)::discMethod == DiscretizationMethod::box; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef Dune::FieldVector GlobalPosition; - typedef typename GET_PROP_TYPE(TypeTag, PTAG(CO2Table)) CO2Table; - typedef Dumux::CO2 CO2; - typedef Dumux::BinaryCoeff::Brine_CO2 Brine_CO2; + static const int dimWorld = GridView::dimensionworld; + using GlobalPosition = typename SubControlVolumeFace::GlobalPosition; public: - ConvectiveMixingProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView) + ConvmixProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - depthBOR_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.DepthBOR); - name_ = GET_RUNTIME_PARAM(TypeTag, std::string, Problem.Name); + depthBOR_ = getParam("Problem.DepthBOR"); + name_ = getParam("Problem.Name"); + salinity_ = getParam("FluidSystem.Salinity"); + //initialize fluid system FluidSystem::init(); - episodeLength_ = GET_RUNTIME_PARAM(TypeTag, Scalar, TimeManager.EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); + + // stating in the console whether mole or mass fractions are used + if(useMoles) + std::cout<<"problem uses mole fractions"< this->bBoxMax()[1] - eps_) - { - values.setDirichlet(massFracIdx, transportEqIdx); // set top boundary to Dirichlet for transport equation - } - + if(globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_) + values.setDirichlet(CO2Idx, contiCO2EqIdx); if(globalPos[1] < eps_) - { - values.setAllDirichlet(); // set bottom boundary to Dirichlet for continuity and transport equation - } + values.setAllDirichlet(); + + return values; } /*! * \brief Evaluate the boundary conditions for a dirichlet * boundary segment. * - * For this method, the \a values parameter stores primary variables. + * \param globalPos The position for which the bc type should be evaluated */ - void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition& globalPos = vertex.geometry().center(); + PrimaryVariables values = initial_(globalPos); - initial_(values, globalPos); + return values; } /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * For this method, the \a values parameter stores the mass flux - * in normal direction of each component. Negative values mean - * influx. + * This is the method for the case where the Neumann condition is + * potentially solution dependent and requires some quantities that + * are specific to the fully-implicit method. + * + * \param values The neumann values for the conservation equations in units of + * \f$ [ \textnormal{unit of conserved quantity} / (m^2 \cdot s )] \f$ + * \param element The finite element + * \param fvGeometry The finite-volume geometry + * \param elemVolVars All volume variables for the element + * \param scvf The sub control volume face + * + * For this method, the \a values parameter stores the flux + * in normal direction of each phase. Negative values mean influx. + * E.g. for the mass balance that would the mass flux in \f$ [ kg / (m^2 \cdot s)] \f$. */ - using ParentType::neumann; - void neumann(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvElemGeom, - const Intersection &is, - int scvIdx, - int boundaryFaceIdx) const + NumEqVector neumann(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolumeFace& scvf) const { - values = 0; + NumEqVector flux(0.0); + + // no-flow everywhere + return flux; } // \} @@ -216,114 +257,61 @@ public: * \brief Evaluate the source term for all phases within a given * sub-control-volume. * - * For this method, the \a values parameter stores the rate mass + * For this method, the \a priVars parameter stores the rate mass * of a component is generated or annihilate per volume * unit. Positive values mean that mass is created, negative ones * mean that it vanishes. + * + * The units must be according to either using mole or mass fractions. (mole/(m^3*s) or kg/(m^3*s)) */ - void sourceAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - values = 0.0; - } + NumEqVector sourceAtPos(const GlobalPosition &globalPos) const + { return NumEqVector(0.0); } /*! * \brief Evaluate the initial value for a control volume. * + * \param globalPos The position for which the initial condition should be evaluated + * * For this method, the \a values parameter stores primary * variables. */ - void initial(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - const GlobalPosition &globalPos - = element.geometry().corner(scvIdx); - - initial_(values, globalPos); - } - - bool shouldWriteOutput() const - { - return this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } - - - // apply a constant timestep size - void preTimeStep() - { - // this->timeManager().setTimeStepSize(1e3); - } - - /*! - * \brief Called by Dune::TimeManager whenever a solution for a - * timestep has been computed and the simulation time has - * been updated. - * - * This is used to do some janitorial tasks like writing the - * current solution to disk. - */ - void postTimeStep() - { - PrimaryVariables mass(0); - this->model().globalStorage(mass); - const Scalar time = this->timeManager().time()+this->timeManager().timeStepSize(); - - // Write mass balance information for rank 0 - if (this->gridView().comm().rank() == 0) { - std::cout << "time (in s), mass CO2 (in kg): \n" - <timeManager().startNextEpisode(episodeLength_); - }; + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const + { return initial_(globalPos); } // \} private: - - // the internal method for the initial condition and dirichlet boundary conditions - // returns the values for the two primary variables pressure and CO2 massfraction - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + // the internal method for the initial condition + PrimaryVariables initial_(const GlobalPosition &globalPos) const { - const Scalar temp = temperatureAtPos(globalPos); - const Scalar salinity = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, Salinity); + PrimaryVariables priVars; - values[pressureIdx] = 1.013e5 + (depthBOR_ - globalPos[1]) * 1100 * 9.81; // hydrostatic pressure distribution - values[massFracIdx] = 0; // initial condition for the co2 mass fraction + const Scalar temp = temperatureAtPos(globalPos); + priVars[pressureIdx] = 1.013e5 + (depthBOR_ - globalPos[1]) * 1100 * 9.81; // hydrostatic pressure distribution + priVars[CO2Idx] = 0.; // initial condition for the CO2 massfraction Scalar moleFracCO2, xgH2O; Brine_CO2::calculateMoleFractions(temp, - values[pressureIdx], - salinity, + priVars[pressureIdx], + salinity_, /*knownPhaseIdx=*/-1, moleFracCO2, xgH2O); - const Scalar M1 = FluidSystem::molarMass(0); - const Scalar M2 = FluidSystem::molarMass(1); - const Scalar avgMolarMass = moleFracCO2 * M2 + (1-moleFracCO2) * M1; - const Scalar massFracCO2 = moleFracCO2 * FluidSystem::molarMass(1)/avgMolarMass; - - if(globalPos[1] > this->bBoxMax()[1] - eps_) + if(globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_) { - values[massFracIdx] = massFracCO2; // CO2 massfraction at top boundary [-] + priVars[CO2Idx] = moleFracCO2; // mole fraction at top boundary [-] } + + return priVars; } + static constexpr Scalar eps_ = 1e-6; + + Scalar depthBOR_; // bottom of reservoir [m] + std::string name_ ; + Scalar salinity_; + }; + +} //end namespace Dumux - static constexpr Scalar eps_ = 1e-6; - Scalar episodeLength_; - Scalar depthBOR_; // bottom of reservoir [m] - std::string name_ ; -}; //end namespace -} #endif diff --git a/lecture/mm/convectivemixing/convmixspatialparams.hh b/lecture/mm/convectivemixing/convmixspatialparams.hh index 58742f4052c8547118035bd91250f0a3a084a748..3dcc43177a66245cd28748dea965b74b728f279a 100644 --- a/lecture/mm/convectivemixing/convmixspatialparams.hh +++ b/lecture/mm/convectivemixing/convmixspatialparams.hh @@ -16,99 +16,83 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -#ifndef DUMUX_CONVMIX_SPATIAL_PARAMETERS_HH -#define DUMUX_CONVMIX_SPATIAL_PARAMETERS_HH - -#include -#include -#include -#include +/*! + * \file + * \ingroup OnePNCTests + * \brief Definition of the spatial parameters for the 1pnc + * problems. + */ +#ifndef DUMUX_CONVMIX_SPATIAL_PARAMS_HH +#define DUMUX_CONVMIX_SPATIAL_PARAMS_HH -namespace Dumux -{ -//forward declaration -template -class ConvMixSpatialParams; +#include +#include -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(ConvMixSpatialParams); +namespace Dumux { -// Set the spatial parameters -SET_TYPE_PROP(ConvMixSpatialParams, SpatialParams, Dumux::ConvMixSpatialParams); -} /*! - * \ingroup OnePTwoCBoxModel - * - * \brief Definition of the spatial parameters for the - * convective mixing problem + * \ingroup OnePNCTests + * \brief Definition of the spatial parameters for the 1pnc + * test problems. */ -template -class ConvMixSpatialParams : public ImplicitSpatialParamsOneP +template +class ConvmixSpatialParams +: public FVSpatialParamsOneP> { - typedef ImplicitSpatialParamsOneP ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; + using GridView = typename FVGridGeometry::GridView; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using Element = typename GridView::template Codim<0>::Entity; + using ParentType = FVSpatialParamsOneP>; + + static const int dimWorld = GridView::dimensionworld; + using GlobalPosition = typename Dune::FieldVector; public: - ConvMixSpatialParams(const GridView &gridView) - : ParentType(gridView) + // export permeability type + using PermeabilityType = Scalar; + + ConvmixSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - // intrinsic permeability [m^2] - permeability_ = GET_RUNTIME_PARAM(TypeTag, Scalar, SpatialParams.Permeability); + permeability_ = getParam("SpatialParams.Permeability", 3e-13); + porosity_ = 0.2; } - ~ConvMixSpatialParams() - {} - /*! - * \brief Define the intrinsic permeability \f$[m^2]\f$. + * \brief Define the intrinsic permeability \f$\mathrm{[m^2]}\f$. * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param globalPos The global position */ - const Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return permeability_; - } + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const + { return permeability_; } /*! - * \brief Define the porosity \f$[-]\f$. + * \brief Define the porosity \f$\mathrm{[-]}\f$. * - * \param element The finite element - * \param fvElemGeom The finite volume geometry - * \param scvIdx The local index of the sub-control volume where + * \param globalPos The global position */ - const Scalar porosity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return 0.2; - } + Scalar porosityAtPos(const GlobalPosition& globalPos) const + { return porosity_; } /*! - * \brief Define the dispersivity \f$[?]\f$. + * \brief Define the dispersivity. * * \param element The finite element - * \param fvElemGeom The finite volume geometry - * \param scvIdx The local index of the sub-control volume where + * \param scv The sub-control volume + * \param elemSol The solution for all dofs of the element */ - const Scalar dispersivity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return 0.0; - } + template + Scalar dispersivity(const Element &element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { return 0; } private: - Scalar permeability_; + Scalar porosity_; }; } // end namespace Dumux diff --git a/lecture/mm/fractures/CMakeLists.txt b/lecture/mm/fractures/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5ef3755a3d1bd0199dc5ece0fd246f2f34b06743 --- /dev/null +++ b/lecture/mm/fractures/CMakeLists.txt @@ -0,0 +1,19 @@ +dune_symlink_to_source_files(FILES "grids" "fracture_exercise.input") + +# test for the exercise +dune_add_test(NAME fracture_exercise + CMAKE_GUARD "( dune-foamgrid_FOUND AND dune-alugrid_FOUND )" + SOURCES fractures.cc + COMMAND ./fracture_exercise + CMD_ARGS fracture_exercise.input) + +set(CMAKE_BUILD_TYPE Release) + +#install sources +install(FILES +fractures.cc +matrixproblem.hh +fractureproblem.hh +matrixspatialparams.hh +fracturespatialparams.hh +DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lecture/mm/fractures) diff --git a/lecture/mm/fractures/fracture_exercise.input b/lecture/mm/fractures/fracture_exercise.input new file mode 100644 index 0000000000000000000000000000000000000000..a6b1e937082bb703880b3be0f816ea8891507d88 --- /dev/null +++ b/lecture/mm/fractures/fracture_exercise.input @@ -0,0 +1,42 @@ +[TimeLoop] +TEnd = 50000 # [s] +DtInitial = 1 # [s] +MaxTimeStepSize = 2500 # [s] + +[Problem] +EnableGravity = true + +[Grid] +File = ./grids/complex.msh + +[Problem] +InitialShaleMethaneSaturation = 0.5 +InjectionRate = -0.25 # [kg/s/m²] +InjectionDuration = 14400 # [s] The length of the injection period +InjectionPosition = 15 # [m] The x-position of the start of the well +InjectionLength = 10 # [m] The x-extent of the injection well + +[Matrix] +Problem.Name = matrix +SpatialParams.Permeability = 1e-13 +SpatialParams.Porosity = 0.1 +SpatialParams.VGAlpha = 1e-3 +SpatialParams.VGN = 2 +SpatialParams.Snr = 0.0 +SpatialParams.Swr = 0.0 +SpatialParams.OverburdenPermeability = 5e-10 +SpatialParams.OverburdenPorosity = 0.3 +SpatialParams.OverburdenVGAlpha = 5e-3 +SpatialParams.OverburdenVGN = 2 +SpatialParams.OverburdenSnr = 0.0 +SpatialParams.OverburdenSwr = 0.0 + +[Fracture] +Problem.Name = fractures +SpatialParams.Aperture = 1e-1 +SpatialParams.Permeability = 1e-7 +SpatialParams.Porosity = 0.85 +SpatialParams.VGAlpha = 1e-2 +SpatialParams.VGN = 2 +SpatialParams.Snr = 0.0 +SpatialParams.Swr = 0.0 diff --git a/lecture/mm/fractures/fractureproblem.hh b/lecture/mm/fractures/fractureproblem.hh new file mode 100644 index 0000000000000000000000000000000000000000..4970dad4aeff031697e3a541fb63637e6933ad6e --- /dev/null +++ b/lecture/mm/fractures/fractureproblem.hh @@ -0,0 +1,262 @@ +// -*- 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 . * + *****************************************************************************/ + /*! + * \file + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The sub-problem for the fracture domain in the exercise on two-phase flow in fractured porous media. + */ +#ifndef DUMUX_COURSE_FRACTURESEXERCISE_FRACTURE_PROBLEM_HH +#define DUMUX_COURSE_FRACTURESEXERCISE_FRACTURE_PROBLEM_HH + +// we use alu grid for the discretization of the fracture domain +// as this grid manager is able to represent network/surface grids +#include + +// we want to simulate methan gas transport in a water-saturated medium +#include +#include +#include +#include +#include +#include + +// we use a cell-centered finite volume scheme with tpfa here +#include + +// include the base problem and the model we inherit from +#include +#include + +// the spatial parameters (permeabilities, material parameters etc.) +#include "fracturespatialparams.hh" + +namespace Dumux { +// forward declarations +template class FractureSubProblem; + +namespace Properties { +// NEW_PROP_TAG(GridData); // forward declaration of property + +// create the type tag nodes +NEW_TYPE_TAG(FractureProblemTypeTag, INHERITS_FROM(CCTpfaModel, TwoP)); +// Set the grid type +SET_TYPE_PROP(FractureProblemTypeTag, Grid, Dune::FoamGrid<1, 2>); +// Set the problem type +SET_TYPE_PROP(FractureProblemTypeTag, Problem, FractureSubProblem); +// set the spatial params +SET_TYPE_PROP(FractureProblemTypeTag, + SpatialParams, + FractureSpatialParams< typename GET_PROP_TYPE(TypeTag, FVGridGeometry), + typename GET_PROP_TYPE(TypeTag, Scalar) >); +// the fluid system +SET_PROP(FractureProblemTypeTag, FluidSystem) +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using H2O = Dumux::Components::H2O; + using TabH2O = Dumux::Components::TabulatedComponent; + using CH4 = Dumux::Components::CH4; + + // turn components into gas/liquid phase + using LiquidPhase = Dumux::FluidSystems::OnePLiquid; + using GasPhase = Dumux::FluidSystems::OnePGas; +public: + using type = Dumux::FluidSystems::TwoPImmiscible; +}; +} // end namespace Properties + +/*! + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The sub-problem for the fracture domain in the exercise on two-phase flow in fractured porous media. + */ +template +class FractureSubProblem : public PorousMediumFlowProblem +{ + using ParentType = PorousMediumFlowProblem; + + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using CouplingManager = typename GET_PROP_TYPE(TypeTag, CouplingManager); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + using ElementVolumeVariables = typename GridVariables::GridVolumeVariables::LocalView; + using PrimaryVariables = typename GridVariables::PrimaryVariables; + using Scalar = typename GridVariables::Scalar; + + using FVGridGeometry = typename GridVariables::GridGeometry; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVGridGeometry::SubControlVolume; + using GridView = typename FVGridGeometry::GridView; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + // some indices for convenience + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + enum + { + pressureIdx = Indices::pressureIdx, + saturationIdx = Indices::saturationIdx, + contiH2OEqIdx = Indices::conti0EqIdx + }; + +public: + //! The constructor + FractureSubProblem(std::shared_ptr fvGridGeometry, + std::shared_ptr spatialParams, + const std::string& paramGroup) + : ParentType(fvGridGeometry, spatialParams, paramGroup) + , time_(0.0) + , aperture_(getParamFromGroup(paramGroup, "SpatialParams.Aperture")) + , initialShaleSaturation_(getParamFromGroup(paramGroup, "Problem.InitialShaleMethaneSaturation")) + , injectionRate_(getParam("Problem.InjectionRate")) + , injectionPosition_(getParamFromGroup(paramGroup, "Problem.InjectionPosition")) + , injectionLength_(getParamFromGroup(paramGroup, "Problem.InjectionLength")) + , injectionDuration_(getParamFromGroup(paramGroup, "Problem.InjectionDuration")) + { + // initialize the fluid system, i.e. the tabulation + // of water properties. Use the default p/T ranges. + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + FluidSystem::init(); + } + + //! Specifies the type of boundary condition at a given position + BoundaryTypes boundaryTypesAtPos(const GlobalPosition& globalPos) const + { + BoundaryTypes values; + + // We only use no-flow boundary conditions for all immersed fractures + // in the domain (fracture tips that do not touch the domain boundary) + // Otherwise, we would lose mass leaving across the fracture tips. + values.setAllNeumann(); + + return values; + } + + //! Evaluate the source term in a sub-control volume of an element + NumEqVector source(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolume& scv) const + { + // evaluate sources from bulk domain using the function in the coupling manager + auto source = couplingManagerPtr_->evalSourcesFromBulk(element, fvGeometry, elemVolVars, scv); + + // these sources are in kg/s, divide by volume and extrusion to have it in kg/s/m³ + source /= scv.volume()*elemVolVars[scv].extrusionFactor(); + return source; + } + + //! Set the aperture as extrusion factor. + Scalar extrusionFactorAtPos(const GlobalPosition& globalPos) const + { + // We treat the fractures as lower-dimensional in the grid, + // but we have to give it the aperture as extrusion factor + // such that the dimensions are correct in the end. + return aperture_; + } + + //! evaluates the Dirichlet boundary condition for a given position + PrimaryVariables dirichletAtPos(const GlobalPosition& globalPos) const + { return initialAtPos(globalPos); } + + //! evaluates the Neumann boundary condition for a given position + NumEqVector neumannAtPos(const GlobalPosition& globalPos) const + { + auto values = NumEqVector(0.0); + + // within the injection region + if (globalPos[1] < 1e-6 + && globalPos[0] > injectionPosition_ + && globalPos[0] < injectionPosition_ + injectionLength_ + && time_ < injectionDuration_ + 1e-6) + values[contiH2OEqIdx] = injectionRate_; + + return values; + } + + //! evaluate the initial conditions + PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const + { + // For the grid used here, the height of the domain is 52.5 m + const auto domainHeight = 52.5; + + // we assume a constant water density of 1000 for initial conditions! + const auto& g = this->gravityAtPos(globalPos); + PrimaryVariables values; + Scalar densityW = 1000.0; + values[pressureIdx] = 1e5 - (domainHeight - globalPos[1])*densityW*g[1]; + values[saturationIdx] = initialShaleSaturation_; + return values; + } + + //! computes the influx of water into the domain + Scalar computeInjectionFlux() const + { + Scalar flux = 0.0; + for (const auto& element : elements(this->fvGridGeometry().gridView())) + { + auto fvGeometry = localView(this->fvGridGeometry()); + fvGeometry.bind(element); + + for (const auto& scvf : scvfs(fvGeometry)) + if (scvf.boundary()) + flux -= neumannAtPos(scvf.ipGlobal())[contiH2OEqIdx] + *scvf.area() + *extrusionFactorAtPos(scvf.ipGlobal()); + } + + return flux; + } + + //! returns the temperature in \f$\mathrm{[K]}\f$ in the domain + Scalar temperature() const + { return 283.15; /*10°*/ } + + //! sets the pointer to the coupling manager. + void setCouplingManager(std::shared_ptr cm) + { couplingManagerPtr_ = cm; } + + //! returns reference to the coupling manager. + const CouplingManager& couplingManager() const + { return *couplingManagerPtr_; } + + //! sets the current time + void setTime(Scalar time) + { time_ = time; } + +private: + std::shared_ptr couplingManagerPtr_; + + Scalar time_; + Scalar aperture_; + Scalar initialShaleSaturation_; + + // injection specifics + Scalar injectionRate_; + Scalar injectionPosition_; + Scalar injectionLength_; + Scalar injectionDuration_; +}; + +} // end namespace Dumux + +#endif diff --git a/lecture/mm/fractures/fractures.cc b/lecture/mm/fractures/fractures.cc new file mode 100644 index 0000000000000000000000000000000000000000..087de6f37d59ecbc9c6f28c0f6a85c0b00023307 --- /dev/null +++ b/lecture/mm/fractures/fractures.cc @@ -0,0 +1,290 @@ +// -*- 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 . * + *****************************************************************************/ +/*! + * \file + * \brief Test for the exercise on two-phase flow in fractured porous media. + */ +#include +#include + +#include + +// include the headers of the two sub-problems +// i.e. the problems for fractures and matrix +#include "matrixproblem.hh" +#include "fractureproblem.hh" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +// Define some types for this test so that we can set them as properties below and +// reuse them again in the main function with only one single definition of them here +using MatrixTypeTag = TTAG(MatrixProblemTypeTag); +using FractureTypeTag = TTAG(FractureProblemTypeTag); +using MatrixFVGridGeometry = typename GET_PROP_TYPE(MatrixTypeTag, FVGridGeometry); +using FractureFVGridGeometry = typename GET_PROP_TYPE(FractureTypeTag, FVGridGeometry); +using TheMultiDomainTraits = Dumux::MultiDomainTraits; +using TheCouplingMapper = Dumux::FacetCouplingMapper; +using TheCouplingManager = Dumux::FacetCouplingManager; + +// set the coupling manager property in the sub-problems +namespace Dumux { +namespace Properties { +SET_TYPE_PROP(MatrixProblemTypeTag, CouplingManager, TheCouplingManager); +SET_TYPE_PROP(FractureProblemTypeTag, CouplingManager, TheCouplingManager); +} // end namespace Properties +} // end namespace Dumux + +// main program +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // initialize parameter tree + Parameters::init(argc, argv); + + // instantiate the grids with the grid manager + using MatrixGrid = typename GET_PROP_TYPE(MatrixTypeTag, Grid); + using FractureGrid = typename GET_PROP_TYPE(FractureTypeTag, Grid); + using GridManager = Dumux::FacetCouplingGridManager; + GridManager gridManager; + gridManager.init(); + gridManager.loadBalance(); + + // we compute on the leaf grid views (get them from grid manager) + // the grid ids correspond to the order of the grids passed to the manager (see above) + static constexpr std::size_t matrixGridId = 0; + static constexpr std::size_t fractureGridId = 1; + const auto& matrixGridView = gridManager.template grid().leafGridView(); + const auto& fractureGridView = gridManager.template grid().leafGridView(); + + // create the finite volume grid geometries + auto matrixFvGridGeometry = std::make_shared(matrixGridView); + auto fractureFvGridGeometry = std::make_shared(fractureGridView); + matrixFvGridGeometry->update(); + fractureFvGridGeometry->update(); + + // the problems (boundary/initial conditions etc) + using MatrixProblem = typename GET_PROP_TYPE(MatrixTypeTag, Problem); + using FractureProblem = typename GET_PROP_TYPE(FractureTypeTag, Problem); + + // make the spatial parameters and problems + auto matrixSpatialParams = std::make_shared(matrixFvGridGeometry, "Matrix"); + auto matrixProblem = std::make_shared(matrixFvGridGeometry, matrixSpatialParams, "Matrix"); + + auto fractureSpatialParams = std::make_shared(fractureFvGridGeometry, "Fracture"); + auto fractureProblem = std::make_shared(fractureFvGridGeometry, fractureSpatialParams, "Fracture"); + + // the solution vector + using SolutionVector = typename TheMultiDomainTraits::SolutionVector; + SolutionVector x, xOld; + + // The domain ids within the multi-domain framework. + // They do not necessarily have to be the same as the grid ids + // in case you have more subdomains involved. We domain ids + // correspond to the order of the type tags passed to the multidomain + // traits (see definition of the traits class at the beginning of this file) + static const auto matrixDomainId = typename TheMultiDomainTraits::template DomainIdx<0>(); + static const auto fractureDomainId = typename TheMultiDomainTraits::template DomainIdx<1>(); + + // resize the solution vector and write initial solution to it + x[matrixDomainId].resize(matrixFvGridGeometry->numDofs()); + x[fractureDomainId].resize(fractureFvGridGeometry->numDofs()); + matrixProblem->applyInitialSolution(x[matrixDomainId]); + fractureProblem->applyInitialSolution(x[fractureDomainId]); + + // instantiate the class holding the coupling maps between the domains + // this needs the information on embeddings (connectivity between matrix + // and fracture domain). This information is extracted directly from the + // grid during file read and can therefore be obtained from the grid manager. + const auto embeddings = gridManager.getEmbeddings(); + auto couplingMapper = std::make_shared(); + couplingMapper->update(*matrixFvGridGeometry, *fractureFvGridGeometry, embeddings); + + // the coupling manager (needs the coupling mapper) + auto couplingManager = std::make_shared(); + couplingManager->init(matrixProblem, fractureProblem, couplingMapper, x); + + // we have to set coupling manager pointer in sub-problems + // they also have to be made accessible in them (see e.g. matrixproblem.hh) + matrixProblem->setCouplingManager(couplingManager); + fractureProblem->setCouplingManager(couplingManager); + + // the grid variables + using MatrixGridVariables = typename GET_PROP_TYPE(MatrixTypeTag, GridVariables); + using FractureGridVariables = typename GET_PROP_TYPE(FractureTypeTag, GridVariables); + auto matrixGridVariables = std::make_shared(matrixProblem, matrixFvGridGeometry); + auto fractureGridVariables = std::make_shared(fractureProblem, fractureFvGridGeometry); + xOld = x; + matrixGridVariables->init(x[matrixDomainId], xOld[matrixDomainId]); + fractureGridVariables->init(x[fractureDomainId], xOld[fractureDomainId]); + + // intialize the vtk output modules + using MatrixOutputModule = VtkOutputModule; + using FractureOutputModule = VtkOutputModule; + MatrixOutputModule matrixVtkWriter(*matrixGridVariables, x[matrixDomainId], matrixProblem->name(), "Matrix"); + FractureOutputModule fractureVtkWriter(*fractureGridVariables, x[fractureDomainId], fractureProblem->name(), "Fracture"); + + // Add model specific output fields + using MatrixVtkOutputFields = typename GET_PROP_TYPE(MatrixTypeTag, VtkOutputFields); + using FractureVtkOutputFields = typename GET_PROP_TYPE(FractureTypeTag, VtkOutputFields); + MatrixVtkOutputFields::init(matrixVtkWriter); + FractureVtkOutputFields::init(fractureVtkWriter); + + // write out initial solution + matrixVtkWriter.write(0.0); + fractureVtkWriter.write(0.0); + + // get some time loop parameters + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // instantiate time loop + auto timeLoop = std::make_shared< CheckPointTimeLoop >(/*startTime*/0.0, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + timeLoop->setCheckPoint(getParam("Problem.InjectionDuration")); + timeLoop->setCheckPoint(tEnd); + + // the assembler for the coupled problem + using Assembler = MultiDomainFVAssembler; + auto assembler = std::make_shared( std::make_tuple(matrixProblem, fractureProblem), + std::make_tuple(matrixFvGridGeometry, fractureFvGridGeometry), + std::make_tuple(matrixGridVariables, fractureGridVariables), + couplingManager, + timeLoop); + + // the linear solver + using LinearSolver = ILU0BiCGSTABBackend; + auto linearSolver = std::make_shared(); + + // the non-linear solver + using NewtonSolver = Dumux::MultiDomainNewtonSolver; + auto newtonSolver = std::make_shared(assembler, linearSolver, couplingManager); + + // time loop + double injectedWater = 0.0; + double escapedGas = 0.0; + timeLoop->start(); do + { + // tell the problems the time for which it is solved + matrixProblem->setTime(timeLoop->time()+timeLoop->timeStepSize()); + fractureProblem->setTime(timeLoop->time()+timeLoop->timeStepSize()); + + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + newtonSolver->solve(x, *timeLoop); + + // print to the terminal the amount of gas escaped from the reservoir up to now + std::cout << std::endl; + const auto injectedInMatrix = matrixProblem->computeInjectionFlux()*timeLoop->timeStepSize(); + const auto injectedInFracture = fractureProblem->computeInjectionFlux()*timeLoop->timeStepSize(); + injectedWater += injectedInMatrix; + injectedWater += injectedInFracture; + std::cout << "Injected water in matrix in this time step: " << injectedInMatrix << " kg" <computeGasFluxToOverburden(*matrixGridVariables, x[matrixDomainId])*timeLoop->timeStepSize(); + escapedGas += escapedFromMatrix; + std::cout << "Escaped gas in this time step: " << escapedFromMatrix << " kg" << std::endl; + std::cout << "Escaped gas so far: " << escapedGas << " kg" << std::endl << std::endl; + + // make the new solution the old solution + xOld = x; + matrixGridVariables->advanceTimeStep(); + fractureGridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + matrixVtkWriter.write(timeLoop->time()); + fractureVtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the Newton solver + timeLoop->setTimeStepSize(newtonSolver->suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + // output some Newton statistics + newtonSolver->report(); + + // report time loop statistics + timeLoop->finalize(); + + // print dumux message to say goodbye + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/false); + + return 0; + +} +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) +{ + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; +} diff --git a/lecture/mm/fractures/fracturespatialparams.hh b/lecture/mm/fractures/fracturespatialparams.hh new file mode 100644 index 0000000000000000000000000000000000000000..d270ceb6bdb854b0e512f8f5e093a66802392f1f --- /dev/null +++ b/lecture/mm/fractures/fracturespatialparams.hh @@ -0,0 +1,120 @@ +// -*- 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 . * + *****************************************************************************/ +/*! + * \file + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The spatial parameters for the fracture sub-domain in the exercise + * on two-phase flow in fractured porous media. + */ +#ifndef DUMUX_COURSE_FRACTURESEXERCISE_FRACTURE_SPATIALPARAMS_HH +#define DUMUX_COURSE_FRACTURESEXERCISE_FRACTURE_SPATIALPARAMS_HH + +#include +#include +#include + +namespace Dumux { + +/*! + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The spatial params the two-phase facet coupling test + */ +template< class FVGridGeometry, class Scalar > +class FractureSpatialParams +: public FVSpatialParams< FVGridGeometry, Scalar, FractureSpatialParams > +{ + using ThisType = FractureSpatialParams< FVGridGeometry, Scalar >; + using ParentType = FVSpatialParams< FVGridGeometry, Scalar, ThisType >; + + using SubControlVolume = typename FVGridGeometry::SubControlVolume; + using GridView = typename FVGridGeometry::GridView; + using Grid = typename GridView::Grid; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + // use a regularized van-genuchten material law + using EffectiveLaw = RegularizedVanGenuchten; + + // we identify those fractures as barriers, that have a domain marker + // of 2. This is what is set in the grid file (see grids/complex.geo) + static constexpr int barriersDomainMarker = 2; + +public: + //! export the type used for permeabilities + using PermeabilityType = Scalar; + + //! export the material law and parameters used + using MaterialLaw = EffToAbsLaw< EffectiveLaw >; + using MaterialLawParams = typename MaterialLaw::Params; + + //! the constructor + FractureSpatialParams(std::shared_ptr fvGridGeometry, + const std::string& paramGroup) + : ParentType(fvGridGeometry) + { + porosity_ = getParamFromGroup(paramGroup, "SpatialParams.Porosity"); + permeability_ = getParamFromGroup(paramGroup, "SpatialParams.Permeability"); + + // set the material law parameters + materialLawParams_.setSnr(getParamFromGroup(paramGroup, "SpatialParams.Snr")); + materialLawParams_.setSnr(getParamFromGroup(paramGroup, "SpatialParams.Swr")); + materialLawParams_.setVgAlpha(getParamFromGroup(paramGroup, "SpatialParams.VGAlpha")); + materialLawParams_.setVgn(getParamFromGroup(paramGroup, "SpatialParams.VGN")); + } + + //! Function for defining the (intrinsic) permeability \f$[m^2]\f$. + template< class ElementSolution > + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { return permeability_; } + + //! Return the porosity + template< class ElementSolution > + Scalar porosity(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { return porosity_; } + + //! Return the material law parameters + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const + { return materialLawParams_; } + + //! Water is the wetting phase + template< class FluidSystem > + int wettingPhaseAtPos(const GlobalPosition& globalPos) const + { + // we set water as the wetting phase here + // which is phase0Idx in the H2oN2 fluid system + return FluidSystem::phase0Idx; + } + +private: + Scalar porosity_; + PermeabilityType permeability_; + MaterialLawParams materialLawParams_; +}; + +} // end namespace Dumux + +#endif diff --git a/lecture/mm/fractures/grids/complex.msh b/lecture/mm/fractures/grids/complex.msh new file mode 100644 index 0000000000000000000000000000000000000000..65ba6ba8be95bd99d48f7697b9d78579b4812b7d --- /dev/null +++ b/lecture/mm/fractures/grids/complex.msh @@ -0,0 +1,3419 @@ +$MeshFormat +2.2 0 8 +$EndMeshFormat +$Nodes +1128 +1 0 0 0 +2 0 35 0 +3 50 35 0 +4 50 0 0 +5 44.25815 20.772115 0 +6 23.1424 16.993095 0 +7 15.568 8.3741 0 +8 38.45485 15.88195 0 +9 14.77055 30.267545 0 +10 0 26.33946 0 +11 34.26665 6.80729 0 +12 50 8.339450000000001 0 +13 23.45195 28.22974 0 +14 39.2558 30.522135 0 +15 32.2713 24.555615 0 +16 50 24.69593 0 +17 15.4061 16.47702 0 +18 0 13.888245 0 +19 6.737799999999999 22.6583 0 +20 26.8811 22.748285 0 +21 43.96575 4.77554 0 +22 44.17025 15.294755 0 +23 5.2257 10.52751 0 +24 9.74225 0 0 +25 25.37775 29.26637 0 +26 28.09245 17.49622 0 +27 31.62675 29.415505 0 +28 33.7329 13.534535 0 +29 23.84075 13.46422 0 +30 18.44605 0 0 +31 6.750399999999999 19.89001 0 +32 6.6816 35 0 +33 14.3396 13.81121 0 +34 15.5313 24.75466 0 +35 28.00765 17.86379 0 +36 33.03935 18.764305 0 +37 22.74455 10.72834 0 +38 6.712899999999999 28.124705 0 +39 44.0237 7.757470000000001 0 +40 25.54675 28.533575 0 +41 14.61545 16.34416 0 +42 15.3072 22.69659 0 +43 33.63145 14.29967 0 +44 0 52.5 0 +45 50 52.5 0 +46 0 1.984034999996162 0 +47 0 3.968069999990362 0 +48 0 5.952104999984573 0 +49 0 7.93613999998452 0 +50 0 9.920174999989547 0 +51 0 11.90420999999477 0 +52 1.670399999995515 35 0 +53 3.340799999990534 35 0 +54 5.011199999997779 35 0 +55 50 33.28265500000254 0 +56 50 31.56531000000948 0 +57 50 29.84796500001318 0 +58 50 28.13062000000994 0 +59 50 26.41327500000416 0 +60 48.02787812500252 0 0 +61 46.05575625001058 0 0 +62 44.08363437501865 0 0 +63 42.11151250002393 0 0 +64 40.13939062502922 0 0 +65 38.16726875003451 0 0 +66 36.1951468750398 0 0 +67 34.2230250000451 0 0 +68 32.2509031250365 0 0 +69 30.27878125003278 0 0 +70 28.30665937502975 0 0 +71 26.33453750002185 0 0 +72 24.36241562501396 0 0 +73 22.39029375001093 0 0 +74 20.41817187500789 0 0 +75 42.388350000004 20.43748000000072 0 +76 40.51855000000938 20.10284500000168 0 +77 38.64875000001235 19.76821000000221 0 +78 36.77895000000971 19.43357500000174 0 +79 34.90915000000678 19.09894000000121 0 +80 17.36213749999583 8.962659999998632 0 +81 19.15627499998988 9.551219999996682 0 +82 20.95041249999542 10.1397799999985 0 +83 13.15902000000319 29.83897700000085 0 +84 11.54749000000746 29.41040900000198 0 +85 9.935960000007858 28.98184100000209 0 +86 8.324430000003961 28.55327300000106 0 +87 36.21806000000026 6.997326000000026 0 +88 38.1694699999909 7.187361999999114 0 +89 40.12087999999248 7.377397999999268 0 +90 42.07228999999417 7.567433999999433 0 +91 24.49934999999786 28.38165749999969 0 +92 34.24115555555247 24.57120555555553 0 +93 36.21101111110001 24.58679611111102 0 +94 38.18086666665 24.60238666666653 0 +95 40.15072222220278 24.61797722222207 0 +96 42.12057777775293 24.63356777777758 0 +97 44.09043333331158 24.64915833333316 0 +98 46.06028888887716 24.66474888888879 0 +99 48.0301444444389 24.6803394444444 0 +100 8.451679999998957 22.66595799999999 0 +101 10.16555999999348 22.67361599999997 0 +102 11.87943999999237 22.68127399999997 0 +103 13.59331999999633 22.68893199999998 0 +104 43.99472499999991 6.266504999995493 0 +105 5.978458333331313 8.77292500000471 0 +106 6.731216666662355 7.018340000010051 0 +107 7.483974999993471 5.263755000015218 0 +108 8.236733333329076 3.509170000009925 0 +109 8.989491666664877 1.754585000004171 0 +110 31.84159999999938 27.79554166667137 0 +111 32.05644999999906 26.17557833334044 0 +112 23.29265000000108 12.09628000000269 0 +113 6.744100000000018 21.27415499999584 0 +114 14.47752499999958 15.07768499999615 0 +115 0 28.07156799999556 0 +116 0 29.80367599998973 0 +117 0 31.5357839999907 0 +118 0 33.26789199999472 0 +119 0 15.66698999999593 0 +120 0 17.44573499999127 0 +121 0 19.22447999998552 0 +122 0 21.00322499998533 0 +123 0 22.78196999999068 0 +124 0 24.56071499999603 0 +125 8.650618181814268 35 0 +126 10.61963636362923 35 0 +127 12.5886545454428 35 0 +128 14.55767272725637 35 0 +129 16.5266909090678 35 0 +130 18.49570909087936 35 0 +131 20.46472727269104 35 0 +132 22.43374545450234 35 0 +133 24.40276363631358 35 0 +134 26.37178181812533 35 0 +135 28.34079999993752 35 0 +136 30.30981818176043 35 0 +137 32.27883636358429 35 0 +138 34.24785454540871 35 0 +139 36.21687272723313 35 0 +140 38.1858909090549 35 0 +141 40.15490909088198 35 0 +142 42.12392727270604 35 0 +143 44.09294545452808 35 0 +144 46.06196363635252 35 0 +145 48.03098181817695 35 0 +146 50 6.671560000003574 0 +147 50 5.00367000000888 0 +148 50 3.335780000008532 0 +149 50 1.667890000004265 0 +150 50 22.87854333333727 0 +151 50 21.06115666667515 0 +152 50 19.24377000001434 0 +153 50 17.42638333335177 0 +154 50 15.60899666668633 0 +155 50 13.79161000001388 0 +156 50 11.97422333334351 0 +157 50 10.15683666667175 0 +158 7.793800000003439 0 0 +159 5.845350000010174 0 0 +160 3.896900000010416 0 0 +161 1.948450000004931 0 0 +162 16.70528999999681 0 0 +163 14.96453000000083 0 0 +164 13.22376999999813 0 0 +165 11.48300999999715 0 0 +166 26.38590000000552 17.57355833333432 0 +167 24.76415000000573 17.28332666666769 0 +168 31.36211666667365 18.46413333333458 0 +169 29.68488333335728 18.16396166667095 0 +170 33.23671666666631 17.27609333333603 0 +171 33.43408333333274 15.78788166667115 0 +172 24.55903333332961 11.32356166666545 0 +173 26.37351666665725 11.91878333333024 0 +174 28.18799999998551 12.51400499999525 0 +175 30.0024833333242 13.10922666666367 0 +176 31.81696666666243 13.70444833333194 0 +177 22.02813333333426 8.940283333335639 0 +178 21.31171666666993 7.152226666674814 0 +179 20.59530000000561 5.364170000013996 0 +180 19.87888333333651 3.576113333341262 0 +181 19.16246666666801 1.788056666670009 0 +182 5.034675000003091 27.67839375000082 0 +183 3.35645000000821 27.23208250000219 0 +184 1.678225000004399 26.78577125000117 0 +185 6.705075000000031 29.84352874999307 0 +186 6.69725000000005 31.56235249998891 0 +187 6.689425000000026 33.28117624999446 0 +188 46.01579999999671 7.951463333333014 0 +189 48.00789999999206 8.145456666665893 0 +190 44.0603374999999 9.641791249994887 0 +191 44.0969749999998 11.52611249998977 0 +192 44.13361249999993 13.4104337499961 0 +193 27.0667499999967 28.75405749999952 0 +194 28.58674999999291 28.97453999999897 0 +195 30.10674999999748 29.19502249999964 0 +196 25.99153333333232 26.60514500000442 0 +197 26.4363166666657 24.67671500000419 0 +198 32.52731666666542 22.62517833334274 0 +199 32.78333333333286 20.69474166667025 0 +200 12.78851875000358 16.0371706250006 0 +201 10.96158750000865 15.73018125000145 0 +202 9.134656250014213 15.42319187500239 0 +203 7.307725000019555 15.11620250000329 0 +204 5.480793750013987 14.80921312500235 0 +205 3.653862500009664 14.50222375000162 0 +206 1.826931250005343 14.1952343750009 0 +207 14.78838749999959 17.93226749999623 0 +208 14.96132499999917 19.52037499999238 0 +209 15.13426249999966 21.10848249999684 0 +210 6.729500000000014 24.48043499999676 0 +211 6.721200000000017 26.30256999999607 0 +212 27.25661666666581 21.12012000000373 0 +213 27.63213333333276 19.49195500000248 0 +214 17.23618333332755 22.70520583333331 0 +215 19.16516666665581 22.71382166666662 0 +216 21.09414999998474 22.72243749999993 0 +217 23.02313333332339 22.73105333333329 0 +218 24.95211666666182 22.73966916666664 0 +219 15.41924999999993 23.72562499999935 0 +220 33.53401250000849 29.69216250000123 0 +221 35.44127499998709 29.96881999999813 0 +222 37.34853749998334 30.24547749999758 0 +223 35.23924999999583 14.8270966666653 0 +224 36.84704999999875 15.35452333333292 0 +225 0 50.55555555555556 0 +226 0 48.61111111111111 0 +227 0 46.66666666666666 0 +228 0 44.72222222222222 0 +229 0 42.77777777777778 0 +230 0 40.83333333333333 0 +231 0 38.88888888888889 0 +232 0 36.94444444444444 0 +233 1.999999999992026 52.5 0 +234 3.999999999984199 52.5 0 +235 5.999999999977311 52.5 0 +236 7.99999999996744 52.5 0 +237 9.99999999995706 52.5 0 +238 11.99999999994668 52.5 0 +239 13.99999999993629 52.5 0 +240 15.99999999992658 52.5 0 +241 17.99999999992327 52.5 0 +242 19.99999999991999 52.5 0 +243 21.99999999991671 52.5 0 +244 23.99999999991343 52.5 0 +245 25.99999999991016 52.5 0 +246 27.99999999990688 52.5 0 +247 29.9999999999036 52.5 0 +248 31.9999999999016 52.5 0 +249 33.99999999991243 52.5 0 +250 35.99999999992338 52.5 0 +251 37.99999999993432 52.5 0 +252 39.99999999994527 52.5 0 +253 41.99999999995622 52.5 0 +254 43.99999999996716 52.5 0 +255 45.9999999999781 52.5 0 +256 47.99999999998904 52.5 0 +257 50 50.55555555555556 0 +258 50 48.61111111111111 0 +259 50 46.66666666666666 0 +260 50 44.72222222222222 0 +261 50 42.77777777777778 0 +262 50 40.83333333333333 0 +263 50 38.88888888888889 0 +264 50 36.94444444444444 0 +265 18.64670141672912 28.06341597710413 0 +266 27.23844952263252 5.942708970528625 0 +267 19.32989319319844 14.44369382493224 0 +268 19.12698064044404 18.48024614215868 0 +269 10.84558052014704 10.18619143086724 0 +270 37.25507592733883 11.0532709004272 0 +271 44.45779398453828 29.82754765217709 0 +272 22.87701485840443 31.60496915035716 0 +273 12.82736461775832 4.90577345591459 0 +274 32.33564643696627 10.0962693041247 0 +275 12.30955237955386 26.36284811211084 0 +276 16.90272634420711 14.47612329263843 0 +277 3.038826997107249 22.10739032001548 0 +278 3.329260841454581 18.37196685581549 0 +279 21.24092695601067 25.93084378508187 0 +280 19.34566735314533 31.68531536035624 0 +281 27.26232524364469 31.81374328025289 0 +282 16.11757792020266 27.50021201948136 0 +283 31.1624704463176 4.127053454229562 0 +284 9.998551228047621 19.13328712254481 0 +285 20.86393607261744 29.61050167386068 0 +286 40.46319481182088 12.73673303115774 0 +287 26.91338097847315 15.0341651858022 0 +288 41.20609455757427 3.886185048283311 0 +289 17.67219665726686 25.7874794093465 0 +290 3.447489661772743 30.16176915544965 0 +291 16.3725162642632 11.64539356130785 0 +292 3.819451367002752 3.861714734507798 0 +293 17.54478630338697 16.75293076342928 0 +294 24.13518607776848 3.575211059834689 0 +295 16.52773667284296 4.916310732351844 0 +296 46.91359424671136 4.078853255075433 0 +297 46.53084732076888 17.99993763368508 0 +298 29.74856637857286 15.85040062907323 0 +299 25.12202418135594 8.304259248944868 0 +300 13.77594597612766 10.98970363837397 0 +301 28.96571475820416 9.03191535364714 0 +302 36.99142939265846 3.463140863849647 0 +303 10.23340531155484 6.842553000015316 0 +304 21.6768370366252 19.7191687538661 0 +305 41.75520029095458 28.09695059212905 0 +306 8.445508691532355 11.89657735129295 0 +307 20.70156746636036 16.40419376421243 0 +308 41.46039726150852 17.02740807510819 0 +309 30.32043504632028 32.10492636642905 0 +310 21.74080395395566 14.50795622123973 0 +311 29.36047529115177 26.41646349262434 0 +312 9.795692364953489 32.07007057008639 0 +313 17.04678554410612 29.65483019785734 0 +314 11.66814990003693 13.11322425857036 0 +315 9.595195366624321 25.74693840941798 0 +316 3.343038769064087 6.944432541176302 0 +317 20.6820787870305 12.44392920069907 0 +318 42.07398341447296 31.50499705507399 0 +319 47.09948238151702 11.04473583777493 0 +320 46.90209441465957 32.01584522211704 0 +321 18.39004148382744 12.25247940612284 0 +322 47.06916757641818 27.54169144709295 0 +323 47.06636252184263 14.5928723161971 0 +324 29.94885737661442 20.91014148637868 0 +325 34.56887212612936 11.32120203389268 0 +326 23.68252456284879 25.79292781383391 0 +327 23.57954829081439 30.14096520330602 0 +328 5.898945244860654 17.398222395013 0 +329 47.08362546986954 21.81949661317423 0 +330 4.023930317529036 24.63021271111203 0 +331 16.59723653441685 32.31534453140867 0 +332 27.91242180078795 2.976493458023876 0 +333 24.59033303966204 20.06539102660047 0 +334 25.58171771476024 31.40839733140303 0 +335 33.86402439217194 3.023549653642475 0 +336 45.22289748039987 2.331941034810379 0 +337 38.55331608666934 27.44640796175813 0 +338 14.43142195137176 26.40513049479295 0 +339 31.2983928422025 7.103333205704891 0 +340 12.76650073894054 8.338560751608197 0 +341 29.84008360211071 23.56323514060695 0 +342 12.5087555882477 20.21198581678859 0 +343 17.22836732776285 20.25245321625287 0 +344 13.32233256667851 32.46905352271105 0 +345 14.60538744124782 2.66979955184231 0 +346 39.53414097530521 10.02834192202645 0 +347 35.73518020263307 21.98643223492792 0 +348 8.460906759445816 17.97820360819092 0 +349 21.5863424402874 27.66938507704372 0 +350 33.18606495732008 32.41220952058436 0 +351 34.30768914165381 27.29838127219588 0 +352 13.64216727341296 24.32854106791213 0 +353 8.612919584146006 8.855247340480819 0 +354 2.705127208495084 11.74982471154661 0 +355 42.48890693401737 2.688182406659852 0 +356 4.613898800674547 20.19757481038227 0 +357 43.84849766742676 18.16069054666152 0 +358 35.47519541711029 12.78090188193245 0 +359 19.47105978910333 26.43610974714225 0 +360 16.71707109320879 17.60211599287372 0 +361 11.7328741955099 2.422981308002585 0 +362 8.644609520255569 20.47945506744437 0 +363 2.338445684698211 32.7327398104869 0 +364 1.68822651305492 20.03443189708176 0 +365 18.14455407139138 6.406520588196999 0 +366 44.2555695418026 26.77835985008649 0 +367 38.08330736192481 13.29402029734002 0 +368 23.75271206829438 6.364496669179411 0 +369 1.850382623100708 16.81546371792227 0 +370 38.80575653077317 22.26597804919369 0 +371 36.07236467822808 32.52419719312381 0 +372 21.28122106775418 33.49799962150425 0 +373 2.03108999618397 23.72830297198344 0 +374 19.3136088909623 29.98214649863952 0 +375 14.1232743477924 28.55478791018204 0 +376 5.890049104008871 2.526611555494611 0 +377 33.18034909930795 11.47626600692638 0 +378 41.50755509758118 14.89746600997455 0 +379 24.98771073961044 15.05764795560392 0 +380 5.047313361265373 32.5002450563014 0 +381 47.65714626089652 29.70208344369917 0 +382 35.48207569126736 16.51013898042722 0 +383 44.42411155845512 32.77193198172615 0 +384 19.80616244542339 20.60491940149215 0 +385 35.55129743143213 9.160972886929152 0 +386 14.4875866732845 6.042283687453629 0 +387 26.62248457135886 30.27709401248774 0 +388 2.193631378513078 29.10115456567678 0 +389 22.27056316013793 2.50594066524105 0 +390 10.05075518406139 4.40982272234539 0 +391 42.52288734219594 10.50423522845774 0 +392 47.76433632464092 2.662465327094254 0 +393 16.92926147885555 2.274589218109645 0 +394 16.55682723604295 15.9276459489815 0 +395 16.02147179048859 26.34951501869952 0 +396 19.0870333854536 16.03439036770158 0 +397 48.24744564494623 5.830268535121865 0 +398 26.86386272692193 9.657446234092458 0 +399 12.07438672711412 18.0860964889097 0 +400 41.84586074179298 5.375851446126093 0 +401 45.59167901248588 5.40922553374727 0 +402 5.483032345034156 22.08590410151536 0 +403 19.79845597243818 24.1263794551938 0 +404 2.07377150015731 8.915524251640409 0 +405 25.8303600917045 2.485814003581266 0 +406 15.69732957740142 12.91749294439205 0 +407 10.67235890710652 27.1297253146736 0 +408 6.013634130585121 12.68818690285019 0 +409 39.16932106132716 32.91641174807599 0 +410 24.29015837565842 32.73263649936641 0 +411 31.78857907704126 16.20894340312642 0 +412 4.467334323043952 16.27863440941293 0 +413 47.75423295981699 19.70822511258636 0 +414 22.45466772384307 29.33534939864492 0 +415 39.55173968133398 17.69849067856991 0 +416 29.00047954838642 31.25666050559221 0 +417 30.20767040036353 10.47145802398361 0 +418 17.43292018672124 27.69486045696407 0 +419 28.20934878145219 15.70580666992482 0 +420 11.16676150133519 24.45275431237375 0 +421 29.47364899135285 17.16709396475379 0 +422 41.60449750403822 22.44910011344941 0 +423 36.41683345440973 28.13416915495829 0 +424 17.21956866322731 24.17697117891971 0 +425 2.262535645154834 2.257221428637616 0 +426 29.66367594900018 5.733942655252996 0 +427 38.73509648784231 1.850103460738277 0 +428 9.658064298090521 13.40290634539902 0 +429 38.39782647176716 5.009889200278886 0 +430 20.59366404667967 28.17277796510897 0 +431 48.1004245379029 16.38633062988729 0 +432 23.31049189501959 15.10459361844944 0 +433 48.13191076013261 12.8829166666787 0 +434 11.91201833381857 31.25545005893374 0 +435 27.27676903135899 16.61627604653913 0 +436 12.44293404136823 27.86361134685346 0 +437 18.17321993502058 14.86224501517571 0 +438 2.03734186067372 5.176793025695699 0 +439 4.928442607685393 5.783818551030331 0 +440 8.549193243648693 30.49669138092381 0 +441 21.16588764716175 30.9980883617831 0 +442 20.79575519961925 18.45202739791783 0 +443 14.52517944835049 12.30786859985112 0 +444 45.14028128047666 22.72719958166227 0 +445 31.26484218753464 1.955420392523632 0 +446 35.16541949207756 4.833437524331952 0 +447 24.08355066224761 9.676097462917413 0 +448 37.0778167180332 17.36623250786168 0 +449 15.01702409638606 10.08714830357283 0 +450 32.98744264800958 4.998785685521643 0 +451 46.26124102454273 12.94963416527131 0 +452 1.806366683474419 18.30721938040037 0 +453 37.68809583383148 9.488794538054663 0 +454 17.77517233119673 10.93924601134033 0 +455 4.983099600884902 29.40567825850048 0 +456 28.74989663867422 32.91909169818992 0 +457 11.15498926046654 20.99958707128943 0 +458 39.92027775944598 14.6038712111053 0 +459 22.58171633837151 24.32922243787187 0 +460 15.57761228723622 15.35544653221218 0 +461 9.737591568841648 17.02595366249903 0 +462 21.8195756952795 13.08492788606924 0 +463 27.44069225784112 17.38746109496836 0 +464 30.29566050180238 27.63055701948768 0 +465 25.66078674374385 13.59380271902416 0 +466 18.16281621596653 13.63651504781161 0 +467 1.584243143130819 21.80678939030506 0 +468 20.52807360781486 15.19302347787753 0 +469 19.6108381280922 11.16021260621862 0 +470 28.74654445628053 17.49268006686703 0 +471 37.16717651546629 21.18468354371588 0 +472 7.359122700322271 10.05800667097799 0 +473 23.41727809081838 18.82295669014732 0 +474 30.61302431118866 25.15991283195935 0 +475 24.11136365791416 27.03206039018697 0 +476 22.80115801602735 27.09181684606009 0 +477 28.04304495433472 27.37316756908164 0 +478 5.467243416306494 19.13429940995312 0 +479 11.62086258213809 33.33509197134493 0 +480 5.38621490219175 23.56322980366313 0 +481 18.45569954865408 33.1620712353407 0 +482 26.21209413323043 4.463951350198116 0 +483 41.14224083657525 30.03143482177961 0 +484 28.79616351259407 22.16867806943896 0 +485 28.62493832994268 14.29461620950922 0 +486 28.13436752146841 23.96296538895009 0 +487 35.50064277926596 1.901426473997253 0 +488 26.95458995347541 7.795800894759407 0 +489 15.23691581177901 33.45045408328299 0 +490 45.99439699674797 15.91930293779274 0 +491 1.797646268859261 31.10256832778322 0 +492 33.79215690180128 8.515899907194482 0 +493 20.60151832530546 13.79071028651431 0 +494 12.22621132967214 6.541104452097501 0 +495 46.08193056815487 20.03715531641888 0 +496 40.93188443657165 1.794018789606235 0 +497 42.33954153247677 12.67616312190111 0 +498 40.45072500722697 26.58909173577215 0 +499 13.15282493228026 1.803607058606198 0 +500 25.16105371629476 27.5061452710261 0 +501 13.55004505946231 17.45550599912985 0 +502 21.87042691235338 15.92688223944559 0 +503 12.75314628078181 14.58224755578472 0 +504 31.78719341160695 31.48815724041567 0 +505 13.16955172492029 12.68219551649645 0 +506 25.65420429640994 6.340884300524166 0 +507 19.4759558867083 12.98777321811214 0 +508 22.69210992027525 33.25510581979924 0 +509 15.69049370937519 29.01263090062776 0 +510 22.62495634298454 4.706216159875856 0 +511 8.106409304637953 27.00036002544775 0 +512 18.70104383187166 4.877524767821366 0 +513 10.28132751998715 11.7330649328321 0 +514 16.36783814197717 7.192136955385479 0 +515 23.18080102528988 8.061091122996773 0 +516 24.01204296579859 1.679041946285091 0 +517 18.54905429666512 24.73779877545221 0 +518 2.483652319640748 25.30480288037287 0 +519 23.05389622144284 20.67897019153724 0 +520 13.36989415887299 18.91687450124621 0 +521 9.244909670138696 10.31396745648292 0 +522 48.41541751547999 4.113683910900569 0 +523 41.16612707388487 33.25134916762075 0 +524 4.122454436986526 1.750073105599431 0 +525 8.498205501569721 7.047769767626381 0 +526 5.040012370313074 25.96332873903871 0 +527 28.71047027375213 18.94224707161582 0 +528 30.91662588212604 8.954688849884583 0 +529 43.26704831593057 22.61143680103719 0 +530 4.090125561684907 8.72636337253282 0 +531 17.87632349994334 31.05103070327579 0 +532 4.213718787846844 12.5266936892572 0 +533 45.37447633565449 3.788104795917312 0 +534 38.79045455566218 11.66515494278058 0 +535 11.14128477560814 8.399736020773441 0 +536 12.46105369662246 9.823439359439318 0 +537 42.66028916294155 26.44147043782217 0 +538 32.34488091013561 15.00603058314243 0 +539 37.30608021926136 26.14532637373119 0 +540 8.452618849421405 33.27106840465029 0 +541 18.70476229967276 17.34907103771349 0 +542 28.33361810537988 10.78303092644417 0 +543 34.02194691826274 12.32474692182176 0 +544 14.6263173794957 4.203123274796374 0 +545 25.9874598252269 33.29413248093569 0 +546 42.14236881862374 18.67312293347926 0 +547 48.27326194963469 33.13737989347793 0 +548 41.08619603908885 9.158770382632341 0 +549 17.75453141522947 17.98515410332787 0 +550 29.1583238669102 1.603765332925283 0 +551 48.34674319601453 26.44703820618595 0 +552 42.84566398276662 29.63240243887593 0 +553 16.15868906538643 30.84757282740862 0 +554 19.59380270512118 7.770291678965391 0 +555 8.372418864648711 24.3152560764621 0 +556 45.86277738693805 9.750694722641425 0 +557 20.56849367457208 26.92903259374784 0 +558 21.81188933521624 11.70464081354766 0 +559 5.957234433313215 4.300355393930391 0 +560 17.17547552136509 12.65966780923596 0 +561 24.48943305695871 29.32239024619829 0 +562 34.81415315719687 13.82069097704827 0 +563 17.59513987688902 28.69986557579601 0 +564 26.11331477829536 19.23787089265349 0 +565 23.85950540827801 24.16485570838161 0 +566 18.05647804988673 21.32182890619091 0 +567 31.60781979609062 33.34575345867641 0 +568 27.050926435703 18.51223499992809 0 +569 24.81323880246396 30.22741716633683 0 +570 28.56614726378251 7.261342570040073 0 +571 42.95058874994962 33.30280070935925 0 +572 40.14064144907464 16.08012548539705 0 +573 7.218436503415316 16.97718499233003 0 +574 10.32812357728709 30.47239074037575 0 +575 12.03360155630692 11.42130318942991 0 +576 42.98389411220206 16.47053386543601 0 +577 21.44491939356887 17.30839051907941 0 +578 48.47710096369776 18.15292934190497 0 +579 48.46577079290171 9.715529919102856 0 +580 36.8289147945212 5.323848287639525 0 +581 39.89732231615523 28.78527266275687 0 +582 7.718742651179139 13.42137864253537 0 +583 31.35943665828357 12.04990956554916 0 +584 48.39365070632861 31.29417223499296 0 +585 6.969903966416809 18.46417537757671 0 +586 13.65731851272435 21.25095294339201 0 +587 36.93670071994605 22.99125367663019 0 +588 48.35206661309022 23.04796456112177 0 +589 48.47391327229644 28.35847241545178 0 +590 16.36887750898971 16.8168185208643 0 +591 40.15046241003588 5.658804700645466 0 +592 30.92256378371359 22.03726675740378 0 +593 5.152980730039767 30.97029294783483 0 +594 37.63718084380464 31.87327543340216 0 +595 25.40039751410247 10.17602958919008 0 +596 27.49432085559511 25.87320957291589 0 +597 25.67509324458693 21.31906990351628 0 +598 30.34514920607331 30.55913963537474 0 +599 18.4329542812822 26.87736995072221 0 +600 15.38034584599273 14.24522013133273 0 +601 34.63128911754708 31.4384735478201 0 +602 3.803069128236405 28.71581564592599 0 +603 46.96531617858921 6.649944794953539 0 +604 1.490830425979023 6.984998963696301 0 +605 27.14846319949536 1.349035321715489 0 +606 45.81776243948885 26.35161083204325 0 +607 46.47501100551668 2.792671839508623 0 +608 22.23957179399298 30.54876819023728 0 +609 40.56149447725204 31.64526555851007 0 +610 16.37003221593302 18.76094036026776 0 +611 45.52517260862665 14.43339963385145 0 +612 33.23554842523597 1.483343692907383 0 +613 26.08378291551999 29.36921300726588 0 +614 16.50472104114759 21.40257042349888 0 +615 13.96809726467414 9.220360954499078 0 +616 15.18142104978578 25.74379493238432 0 +617 31.32734716834253 20.06534350286195 0 +618 37.1179168569682 1.442934159717035 0 +619 8.023491874472583 19.40713185997319 0 +620 34.59907774478166 33.37648817229557 0 +621 43.9683028450081 3.28973135064217 0 +622 15.78790813136638 1.489560564380436 0 +623 36.47112616831976 13.88862100735176 0 +624 14.63982017830862 24.30589159628254 0 +625 14.5065206892869 23.48027072862176 0 +626 46.22642139748346 30.57196570788666 0 +627 48.65973956397563 14.65254525589266 0 +628 20.25639594325495 30.6382152327704 0 +629 3.882341407418838 33.55838436050283 0 +630 18.63938338619636 19.86240252349778 0 +631 37.45632604392959 33.46277687492039 0 +632 43.86168603185477 1.782319947326388 0 +633 34.56993703077564 13.09509879385656 0 +634 35.5026900692157 26.11339441293261 0 +635 43.67149365307968 31.31862059663922 0 +636 7.351084820803287 1.558073311101741 0 +637 25.82150331797212 16.29919196008679 0 +638 4.031905324061854 23.10922705445876 0 +639 9.741714480602647 24.05356112983805 0 +640 24.1761836477305 31.21960124818749 0 +641 46.05687113710758 33.41576675467544 0 +642 21.24008793024047 21.18319981447026 0 +643 14.94198844454805 31.73356185701085 0 +644 10.62062837706559 1.322610675175142 0 +645 5.014256387685611 7.449175892950842 0 +646 3.141053708503728 20.5791513240913 0 +647 33.05318515230624 13.59606206084782 0 +648 10.19711278916114 2.890442628373055 0 +649 26.82847982865177 27.4278309284003 0 +650 28.21204663353694 4.567799835047124 0 +651 5.652238854507146 20.6093987160808 0 +652 12.47260438828776 23.90601872875219 0 +653 45.52478322511952 28.40557523932204 0 +654 9.04001800508915 5.519946483400444 0 +655 1.451498923341196 12.62067836893285 0 +656 3.505341538624323 10.36301707999753 0 +657 28.63323084701288 20.49330396254163 0 +658 42.69498301567854 4.003098050342285 0 +659 34.07568137359442 22.85955035184491 0 +660 15.3400541735371 26.77567779322564 0 +661 34.39494015327579 20.6535056203377 0 +662 1.142504016988822 32.40183799999268 0 +663 9.827885668391298 33.73524618921632 0 +664 48.62084457824962 11.35668787448231 0 +665 33.53808161240419 25.96985370789218 0 +666 48.46588723747571 21.14833729134903 0 +667 18.04514528101253 3.110032535983013 0 +668 32.21774189891493 8.251638930637442 0 +669 16.46215120779013 10.3052734499173 0 +670 4.443832633857355 17.945791446016 0 +671 22.50266843612388 28.32632911070313 0 +672 32.86079741580382 12.78099464807951 0 +673 22.79579274452923 13.74277411548217 0 +674 1.530648491574144 10.78048361919534 0 +675 16.42752017086223 25.30289359908669 0 +676 11.04636904911644 25.8431863969553 0 +677 24.26515110153316 16.13860273432576 0 +678 13.58561816235901 27.14534241558328 0 +679 20.60806804102214 8.710760335794205 0 +680 9.389927269270506 27.62494343782666 0 +681 45.61373722188477 11.39376643171894 0 +682 27.09245180647612 13.5072052046635 0 +683 5.393180938320377 33.73293715966112 0 +684 38.94960990348193 25.93341314775111 0 +685 13.6821583547191 31.11601257658108 0 +686 8.068308410870674 32.00094635523216 0 +687 42.75308217625577 8.925940172216881 0 +688 20.79486798352109 1.691182661655768 0 +689 24.75691027124614 12.58079463474509 0 +690 45.26504479425289 6.797380318680801 0 +691 46.76229585434601 1.421353853316712 0 +692 31.09441702383108 15.03003125096756 0 +693 30.68101477164675 17.21614067682015 0 +694 39.23243089682423 8.625836930244221 0 +695 32.34224258042972 3.154675598882983 0 +696 13.15194792641798 3.309614234729007 0 +697 19.73817132389322 33.3846698349045 0 +698 29.56229681401458 3.494079188000413 0 +699 10.8169570845492 14.28249601915561 0 +700 27.96134609891805 30.1617769163572 0 +701 1.238593463165824 25.3438104204707 0 +702 14.23070569142475 7.618091133507314 0 +703 23.53570496317037 29.13296472974679 0 +704 15.7578926510652 17.44624941354567 0 +705 15.1419575377662 17.04992422838547 0 +706 1.549131398202915 3.665255250011823 0 +707 30.13237016908941 33.53093231106063 0 +708 46.80682552403835 23.32659658516722 0 +709 24.88491628742793 25.5025026214434 0 +710 38.29436187144353 29.02669245589418 0 +711 27.41680439371498 33.75547269198315 0 +712 24.80527444694759 18.64921685642361 0 +713 44.72751961840864 17.01652528660901 0 +714 31.23485767273568 23.58824161266256 0 +715 6.140771703283807 16.02534918706497 0 +716 40.66339272141173 18.63035738570481 0 +717 33.22016817110187 28.26880933680422 0 +718 29.94448427762548 19.50652183723393 0 +719 45.64099682962527 21.33899162781384 0 +720 16.0115991970922 3.328044285977408 0 +721 20.01939430204594 19.35024960018911 0 +722 31.54157109892288 5.617266084030393 0 +723 34.3606943143914 15.47918121048515 0 +724 16.28215005997233 23.49250235379355 0 +725 3.692884411236053 25.90171777974172 0 +726 32.68399256497518 6.882368968848141 0 +727 24.52426639394711 5.114133839811624 0 +728 16.70840540549643 28.49546939185059 0 +729 1.295837078374954 33.7402739246527 0 +730 17.58767320539592 15.67614931289232 0 +731 22.37507621900846 25.83639517664702 0 +732 7.956436365085563 21.52637802217613 0 +733 19.98935175291559 17.52167948505724 0 +734 36.92732000891881 12.35608273310946 0 +735 40.7894025427583 11.12823310482599 0 +736 17.15945636165177 33.73285719354536 0 +737 11.82131539254979 19.46956620015575 0 +738 38.08596153321476 14.52358736413913 0 +739 19.53484352976432 27.47517843241135 0 +740 35.85048950660015 10.68850848116497 0 +741 13.65866000769624 15.51031579519537 0 +742 35.8316460509995 18.10083710771874 0 +743 9.625827205146775 21.4947338990012 0 +744 17.5756699999984 1.062617967335966 0 +745 18.21190562510302 29.6084710617609 0 +746 9.924999004697353 9.154717012446408 0 +747 7.85864691119819 25.56801247980361 0 +748 16.03974161696846 20.20896430000174 0 +749 16.17595777881236 24.29053042635987 0 +750 14.42302525318008 21.93623936084721 0 +751 13.43962265272856 25.73339225283379 0 +752 12.43120648999009 21.51408146964316 0 +753 13.57316363634959 33.91357873513269 0 +754 11.49660575207914 3.943377281856149 0 +755 20.74308066136827 11.3041993596217 0 +756 21.34567256012027 3.725764185312924 0 +757 44.93332544200229 18.83307967219709 0 +758 34.43407732635747 17.80459457865849 0 +759 14.34688410097868 1.192593434965789 0 +760 35.131808303225 7.966262479628608 0 +761 34.64726772543037 28.73005181696966 0 +762 29.91816699521657 7.669987512364869 0 +763 6.632220228097695 13.97228744146608 0 +764 2.788410626417025 13.13394569618567 0 +765 43.51298475258362 19.46152927176656 0 +766 3.533342742131626 5.30707338072596 0 +767 29.08845431630293 24.99515728541132 0 +768 1.329546506270209 15.44760497448538 0 +769 32.02829418288264 17.44231106396194 0 +770 21.19025318214966 24.69977366058143 0 +771 8.582415313492813 16.75170163113576 0 +772 30.82720504600124 26.51935935911538 0 +773 48.60562735829531 1.375590696736938 0 +774 11.1815265669095 28.30801131480736 0 +775 33.24002044295293 33.88404877224514 0 +776 21.6002557439103 28.62286864507228 0 +777 3.611036185675699 31.83766660972647 0 +778 45.03562479399572 1.021770887119149 0 +779 17.01744016780396 26.58538840905006 0 +780 34.21313525037285 10.20985310337206 0 +781 19.52744326364264 28.81874860148092 0 +782 35.79707937427833 20.47142727979689 0 +783 2.714793507753197 15.7347402010575 0 +784 42.81148277659415 6.384383133784877 0 +785 7.336507604034385 8.314446138323344 0 +786 11.11212715480312 17.20253782979112 0 +787 36.73776157870389 8.24767111366425 0 +788 6.834399097043907 11.54144270956298 0 +789 18.20673681438599 23.62581787224407 0 +790 34.21650224430609 16.61124531273492 0 +791 35.3308906332838 3.330558872961289 0 +792 3.864294316046249 19.29908282548283 0 +793 14.38693421352887 25.24855769849408 0 +794 15.10381750458783 15.85689395715528 0 +795 38.21303460155734 18.30395031709043 0 +796 48.72605125217162 7.032903918137312 0 +797 22.13196549647165 18.40280320965907 0 +798 40.3787381341469 21.51606168805053 0 +799 35.43994579256521 23.39904758601392 0 +800 10.99890644946564 5.537343741856946 0 +801 39.40213229274161 3.61035701056697 0 +802 4.239856867835567 21.25769943053493 0 +803 43.58321669802052 28.1970510350688 0 +804 11.91998991863085 1.232168245486684 0 +805 28.55775518274756 16.73596198382653 0 +806 23.00925875521794 9.351452979812455 0 +807 46.98747569219835 5.339568722902387 0 +808 3.160703844089307 17.16927997952191 0 +809 48.95707623968324 2.631081986948912 0 +810 33.05886526729704 30.93449991515094 0 +811 1.333531023321834 1.148360314795101 0 +812 24.97832076423914 28.87599818654949 0 +813 19.92077118869964 25.51249697613234 0 +814 47.06482130904753 25.93708576373107 0 +815 42.53645134065238 1.30648289451847 0 +816 13.89767149094632 20.110079495207 0 +817 22.23392030965874 6.119955211407033 0 +818 19.69439651446697 6.420278567277254 0 +819 44.99065372173366 8.775354826492332 0 +820 17.80633572417778 19.08759532260297 0 +821 25.3484765625179 1.02813913576251 0 +822 40.21031364598486 23.20862869883518 0 +823 7.623807551962746 20.51542598991791 0 +824 1.251987035814695 27.96755445875513 0 +825 9.639042833260191 8.017599345603228 0 +826 7.572849716161921 23.52998726911471 0 +827 43.07127420666004 14.49889380250216 0 +828 2.922675000007674 1.014902245957631 0 +829 18.2647929963574 7.933870005697102 0 +830 5.536583093506741 24.85464080190178 0 +831 29.55699325410495 11.80988480212056 0 +832 21.7807258705378 26.71103977541376 0 +833 32.99279778503166 27.10163286338081 0 +834 29.12233517751488 27.91826248523981 0 +835 25.69579480082061 30.10969830349869 0 +836 20.54630351876473 32.0254881483115 0 +837 7.618565507808925 34.13806116366118 0 +838 18.80920724260343 25.87025097175911 0 +839 36.23937624431731 31.14842645508794 0 +840 12.34617954073912 25.104456811823 0 +841 24.2589145011354 21.50683072433078 0 +842 14.90801066155899 27.59952302660998 0 +843 45.14589445033346 31.417480937101 0 +844 30.07497541206007 14.38918543740038 0 +845 2.76584041250678 19.31837045657435 0 +846 15.20451981340622 11.29930052210273 0 +847 21.87519755784951 32.09949333315618 0 +848 30.96769012544981 28.50915654653967 0 +849 2.563413618326583 34.02239102401667 0 +850 39.31308167367926 13.42026214489828 0 +851 47.1016494347617 9.230947955311393 0 +852 29.20009497067363 30.02942791146455 0 +853 49.07606094195291 34.10500872337012 0 +854 8.699514594777188 43.51789213135419 0 +855 41.23567001887317 43.82863395295706 0 +856 31.29432727267234 43.73653473851049 0 +857 21.44923636359668 43.73097378380047 0 +858 15.06951517577916 41.68131446987376 0 +859 36.17399911618855 40.80480126175749 0 +860 36.59577378672099 46.41190796752353 0 +861 26.36837159447826 40.82622781059069 0 +862 26.32037866150903 46.4925170101728 0 +863 17.1370134182747 46.70407381341717 0 +864 5.299731556302916 39.82765709626381 0 +865 44.81062683723471 40.0994196230718 0 +866 45.05553880418831 47.02470158357667 0 +867 5.09322856747725 47.43029823535323 0 +868 11.94144943704854 47.52329049895355 0 +869 19.46503849214654 39.54094716553757 0 +870 10.85703235516901 39.39404035702919 0 +871 31.29432727267234 39.31279582481006 0 +872 40.51675766497855 39.55208359702203 0 +873 30.9999999999026 48.17037982872466 0 +874 40.52892792725118 48.10927153895523 0 +875 21.09046360781091 48.19805254675985 0 +876 3.688270394442978 43.74075034436972 0 +877 46.22916528921837 43.74999999999999 0 +878 23.10915272195053 38.5103257820299 0 +879 8.432273754133961 49.09893895322421 0 +880 12.2701636404003 44.22332043961181 0 +881 7.666109090907136 38.12252991806989 0 +882 17.89265697154811 43.99785688804621 0 +883 15.75187069189905 38.19568204709837 0 +884 24.76936279329152 43.71129544592237 0 +885 34.68451641944075 43.69088981865124 0 +886 37.94639496479859 43.31096836641385 0 +887 2.798653888862909 38.23359559198028 0 +888 34.13084592017734 49.20517444711999 0 +889 24.17846998184304 49.57383493996294 0 +890 28.05736795563795 43.52655555965996 0 +891 34.29867213487012 38.07495626769809 0 +892 14.65490199834892 49.41028958074838 0 +893 28.52172913177401 38.25076893826741 0 +894 28.02997953570542 49.56481474920712 0 +895 46.85959282384462 37.96259973857322 0 +896 37.74697778213292 38.06460176550614 0 +897 46.85192728024087 49.53154127418695 0 +898 2.902622888024911 49.59464219069595 0 +899 37.64241784861261 49.52195801593583 0 +900 18.40051965002123 49.5454088108828 0 +901 43.17557049490763 37.89097635144746 0 +902 8.025998308266539 40.90153870060654 0 +903 43.02593764343852 49.69504416650136 0 +904 33.29806362197495 46.25239077468559 0 +905 15.10724161067906 44.84329942840607 0 +906 23.43144285605656 46.26398422907666 0 +907 33.46187731601907 41.16343781646042 0 +908 25.53300577317989 37.61153960259107 0 +909 29.09646485687793 46.12235011557384 0 +910 2.707303805223199 41.06987974242092 0 +911 6.217850825140451 44.92768366635954 0 +912 19.69892607489682 45.90663836075748 0 +913 12.83457156957623 37.61498280133418 0 +914 43.81595870868688 42.64133632675207 0 +915 29.11335791968895 40.98757790503097 0 +916 23.14583179630118 41.5643720253829 0 +917 2.3335674573138 46.37615954244032 0 +918 47.27065669618678 41.15286247464685 0 +919 46.9873533486648 46.47046942201269 0 +920 42.33358431967594 45.98392626302031 0 +921 9.65584577133211 46.30285717464162 0 +922 5.352225417838895 37.39343531300831 0 +923 10.92217462532688 49.62777044540235 0 +924 6.07588189018744 49.87048383597651 0 +925 38.5824157967636 46.12856705042861 0 +926 20.70003396452642 37.33745614346689 0 +927 5.840491734192067 42.46471200327206 0 +928 38.88565332505407 41.225794189638 0 +929 17.76577934353839 37.47153990465556 0 +930 12.22813473521922 41.0456008508766 0 +931 9.928785380269685 37.61924071415488 0 +932 17.06086591224351 41.24099388194661 0 +933 14.21087027077039 46.94170302718894 0 +934 20.08709478161374 42.14860071675555 0 +935 10.2657354021144 42.27318724741983 0 +936 42.15970660861264 40.55529440341888 0 +937 31.29987780423057 36.95716781812368 0 +938 32.06397999863938 50.20492906550577 0 +939 22.07303003010849 50.0970599897915 0 +940 41.22339073701299 37.19491306786558 0 +941 7.264026230351908 46.86366177021998 0 +942 41.05765765510272 50.64004388914849 0 +943 44.49298256255972 45.19706268437352 0 +944 45.03164637566358 36.88720686374668 0 +945 31.28919278609799 41.18828357920165 0 +946 20.89204622999094 40.50980292305891 0 +947 13.87197698107471 39.88057863087512 0 +948 44.93139291834097 50.71339050685967 0 +949 26.07817627252363 50.32673354070436 0 +950 31.28315314203061 45.9952267215149 0 +951 16.70980000995651 50.67411646619561 0 +952 12.92318733265563 50.77016599006164 0 +953 40.77202499691521 41.7773324804956 0 +954 30.05378463276474 50.00140271788001 0 +955 35.62171133976401 50.5314922472675 0 +956 23.75849287376128 36.77969332410002 0 +957 36.07271722490808 37.15241022909525 0 +958 48.26701249423032 43.75188578599886 0 +959 1.709840856183999 43.70983213832596 0 +960 1.640699796378152 39.79741791487666 0 +961 9.282298119257188 51.02228380547334 0 +962 17.69432768109026 39.35604242628145 0 +963 24.50180872690958 40.13215329221614 0 +964 26.41270860157866 44.48263115601944 0 +965 20.19188772384958 50.71614494038587 0 +966 48.30142319626061 39.75267732943926 0 +967 21.57789318869564 45.61514758453349 0 +968 48.26010913388354 47.76177647700193 0 +969 18.81684729631082 47.59260023919129 0 +970 1.746870999209071 48.03981462343886 0 +971 36.34572607857386 44.44816848255341 0 +972 10.21829699392566 44.33229320546209 0 +973 8.936185994799644 39.45122493870378 0 +974 33.19631489428989 36.61462073686106 0 +975 4.358778073997111 45.68248550565444 0 +976 45.55796613977881 41.90212262832885 0 +977 36.27770979467504 42.69350742361635 0 +978 40.44192288230767 46.22501522636441 0 +979 29.36478270437435 36.66336361217253 0 +980 12.64176250235822 45.88372069855347 0 +981 26.35582982574511 42.67922595585597 0 +982 39.06227862225358 36.63720839702343 0 +983 38.90704944819819 50.90634071843982 0 +984 1.721669027433259 36.73375483769755 0 +985 36.07743621508108 38.92762461978401 0 +986 27.17592902737003 36.77980363422914 0 +987 4.067496898788193 36.75288014733636 0 +988 32.97652201739178 44.54135160077892 0 +989 36.09077503286122 48.43423535103748 0 +990 4.454685402555254 50.81952576034462 0 +991 23.16156704257558 44.63742136626128 0 +992 13.73317408826938 43.16406142859964 0 +993 29.62644507193036 42.66838835217254 0 +994 10.14375283855525 47.86683048109336 0 +995 6.756679586554817 36.77634605203988 0 +996 14.4546519913165 37.03008536038701 0 +997 16.31039893198819 43.19735298301363 0 +998 16.89578321170607 45.14203065650732 0 +999 43.1681116257331 47.81583838757038 0 +1000 26.11275123994976 48.51753834085241 0 +1001 7.086595545957538 39.61038253460386 0 +1002 27.8271245958712 47.35423264720178 0 +1003 37.92246789248512 47.87397009032782 0 +1004 43.2364385302063 44.33023617641176 0 +1005 11.73728825317954 36.61887358856972 0 +1006 3.570527784919181 39.88881169858791 0 +1007 39.64260503367725 42.94903669407294 0 +1008 26.38179251674049 38.86704882457877 0 +1009 4.417311640465131 41.82030651837793 0 +1010 19.60700860061176 44.25349243223649 0 +1011 33.0553465093618 39.44489979960284 0 +1012 22.51377720679884 47.73575508443014 0 +1013 32.76086970347328 47.95593914040001 0 +1014 16.5067105095234 48.13680431058945 0 +1015 32.90230165221493 42.74922998127737 0 +1016 38.81505102585629 39.34383996853821 0 +1017 19.15607100733482 36.49673281427505 0 +1018 16.34196397788853 36.7345907365551 0 +1019 15.62465833641309 39.93750567407056 0 +1020 29.71241120764187 44.48450679828556 0 +1021 8.468570532419109 36.65907075025068 0 +1022 7.934199298147655 45.34065847499524 0 +1023 48.30152948572562 36.69520663442201 0 +1024 48.29368336198063 50.80128066438812 0 +1025 24.6566246677685 41.99221190846156 0 +1026 34.67387889034568 40.02830378329773 0 +1027 1.695216049383562 50.81269841269634 0 +1028 29.5792175506538 39.33677472164023 0 +1029 24.8806929043299 45.48839293328265 0 +1030 34.76979392386011 45.44297030865256 0 +1031 14.85757786816479 51.17091440740113 0 +1032 21.75438131894 41.90991487746587 0 +1033 46.41217060128264 39.59633544092316 0 +1034 12.97118130422291 48.87646657401552 0 +1035 3.518151638710929 47.70512224457966 0 +1036 46.32588084915916 47.95530175066431 0 +1037 33.64439468092398 50.83586711751649 0 +1038 24.60091031685755 51.08611188207261 0 +1039 7.490535298537315 42.33124029178681 0 +1040 34.63925117557238 47.2921535342802 0 +1041 7.558090752704668 50.99834131893481 0 +1042 24.55041633896956 47.51155109037663 0 +1043 44.95282955647745 38.64080589677166 0 +1044 39.50947744987693 49.14267649566472 0 +1045 11.14521186393668 46.02205208305265 0 +1046 34.97594205055342 41.75410293448357 0 +1047 44.89313152018349 48.78930294489322 0 +1048 6.527785252237425 48.15662140103863 0 +1049 29.37935050214299 48.02576436552406 0 +1050 27.67239067828774 41.70430733412353 0 +1051 37.27348948896154 51.13853334494243 0 +1052 27.58616064372171 51.00990835708971 0 +1053 39.27061279128985 44.58991433467249 0 +1054 37.45694745311652 36.37084407832497 0 +1055 4.736854294068744 48.98027306615918 0 +1056 43.12347900886463 36.3484475596054 0 +1057 1.368149159926473 45.25815259764398 0 +1058 45.99246189175857 45.27091774776638 0 +1059 1.389640463253745 41.81458333878567 0 +1060 48.48002768103544 45.70111608172556 0 +1061 48.64863912753782 41.81850100622432 0 +1062 42.10055914228867 38.92031295652433 0 +1063 27.97901970636165 39.94081599529731 0 +1064 46.77106840324905 36.39015710304117 0 +1065 46.78213785384942 51.16132813672874 0 +1066 11.54646435240942 38.10655278339774 0 +1067 18.72806838330339 51.09314726146103 0 +1068 39.53607447138162 38.15143440700236 0 +1069 1.302727594219108 38.1643363914232 0 +1070 42.27691813116296 42.45640779324589 0 +1071 42.9999999999617 51.28291107745569 0 +1072 21.90235131817017 36.45983148628784 0 +1073 6.31343630121655 41.09617713981151 0 +1074 19.34420087004673 38.06948377496797 0 +1075 15.87023726670231 46.2781125298733 0 +1076 4.610197393446239 38.56828584150639 0 +1077 12.47897795690075 39.16952507388702 0 +1078 32.62890772308494 38.08088808941914 0 +1079 18.23769488519782 45.82664881284549 0 +1080 25.38727272721946 36.15545818006084 0 +1081 27.93576939339169 45.30739552284801 0 +1082 48.70403368767149 38.11239776105978 0 +1083 5.225714845088521 43.81929774469813 0 +1084 1.275150551653015 49.47933077680749 0 +1085 23.15616733041224 42.92436490121572 0 +1086 48.70825962148135 49.36450550850154 0 +1087 19.98923650678524 49.10202589480875 0 +1088 44.77050036658675 43.75019766172744 0 +1089 11.70137665015464 42.89340321202319 0 +1090 41.64385822079078 49.1115651073256 0 +1091 34.72285139156528 36.39738825059503 0 +1092 37.51784621453653 40.02068980906959 0 +1093 30.05063108871109 38.06692875147328 0 +1094 13.61143394689121 44.90096025886208 0 +1095 37.40795982290625 41.75747704919375 0 +1096 2.770805550666386 42.51388624196338 0 +1097 3.01050486798799 51.24537327274739 0 +1098 21.46124663871784 38.76631953854658 0 +1099 24.53626247611435 38.59057055284278 0 +1100 30.85323363095319 51.3287280221881 0 +1101 37.74818468362938 44.97790524031838 0 +1102 5.573840380635938 36.18453230247691 0 +1103 47.2200167859701 42.62885701503312 0 +1104 23.2646300469242 51.22918713169454 0 +1105 8.810486645673127 42.05087339214558 0 +1106 43.7414214953973 41.16666183735583 0 +1107 2.743451619929621 44.88608323852502 0 +1108 11.01823715386388 51.32925918947442 0 +1109 13.23456781735429 36.25278835005818 0 +1110 6.003790460176551 46.33548750655709 0 +1111 29.0370162824341 51.29669685232218 0 +1112 30.24818315201888 46.97707463811731 0 +1113 20.70762723622062 46.79534901742104 0 +1114 7.097556964727241 43.87485113160259 0 +1115 17.59669411182105 36.08813125777063 0 +1116 43.71427485173265 46.18209194661033 0 +1117 39.30538400866968 47.48330367655868 0 +1118 41.78675265333738 47.44617233036567 0 +1119 6.275153336192899 38.34303015436144 0 +1120 22.77481678138392 40.20240949976977 0 +1121 8.620717043499146 47.43553558489783 0 +1122 18.3715863664474 42.42797663519534 0 +1123 0.8480172568571936 35.9195498205355 0 +1124 14.13665016551299 38.30665105941044 0 +1125 9.855166659485484 40.66235971246385 0 +1126 47.24105756053936 44.94453441005792 0 +1127 43.47219078918501 39.50635532612768 0 +1128 18.83232742535076 40.94404098743881 0 +$EndNodes +$Elements +2282 +1 1 2 1 5 5 75 +2 1 2 1 5 75 76 +3 1 2 1 5 76 77 +4 1 2 1 5 77 78 +5 1 2 1 5 78 79 +6 1 2 1 5 79 36 +7 1 2 2 6 7 80 +8 1 2 2 6 80 81 +9 1 2 2 6 81 82 +10 1 2 2 6 82 37 +11 1 2 3 7 9 83 +12 1 2 3 7 83 84 +13 1 2 3 7 84 85 +14 1 2 3 7 85 86 +15 1 2 3 7 86 38 +16 1 2 4 8 11 87 +17 1 2 4 8 87 88 +18 1 2 4 8 88 89 +19 1 2 4 8 89 90 +20 1 2 4 8 90 39 +21 1 2 5 9 13 91 +22 1 2 5 9 91 40 +23 1 2 6 10 15 92 +24 1 2 6 10 92 93 +25 1 2 6 10 93 94 +26 1 2 6 10 94 95 +27 1 2 6 10 95 96 +28 1 2 6 10 96 97 +29 1 2 6 10 97 98 +30 1 2 6 10 98 99 +31 1 2 6 10 99 16 +32 1 2 7 11 17 41 +33 1 2 8 12 19 100 +34 1 2 8 12 100 101 +35 1 2 8 12 101 102 +36 1 2 8 12 102 103 +37 1 2 8 12 103 42 +38 1 2 9 13 21 104 +39 1 2 9 13 104 39 +40 1 2 10 14 23 105 +41 1 2 10 14 105 106 +42 1 2 10 14 106 107 +43 1 2 10 14 107 108 +44 1 2 10 14 108 109 +45 1 2 10 14 109 24 +46 1 2 11 15 25 40 +47 1 2 12 16 27 110 +48 1 2 12 16 110 111 +49 1 2 12 16 111 15 +50 1 2 13 17 29 112 +51 1 2 13 17 112 37 +52 1 2 14 18 31 113 +53 1 2 14 18 113 19 +54 1 2 15 19 33 114 +55 1 2 15 19 114 41 +56 1 2 1 27 35 166 +57 1 2 1 27 166 167 +58 1 2 1 27 167 6 +59 1 2 11 28 35 26 +60 1 2 1 29 36 168 +61 1 2 1 29 168 169 +62 1 2 1 29 169 35 +63 1 2 12 30 36 170 +64 1 2 12 30 170 171 +65 1 2 12 30 171 43 +66 1 2 2 31 37 172 +67 1 2 2 31 172 173 +68 1 2 2 31 173 174 +69 1 2 2 31 174 175 +70 1 2 2 31 175 176 +71 1 2 2 31 176 43 +72 1 2 13 32 37 177 +73 1 2 13 32 177 178 +74 1 2 13 32 178 179 +75 1 2 13 32 179 180 +76 1 2 13 32 180 181 +77 1 2 13 32 181 30 +78 1 2 3 33 38 182 +79 1 2 3 33 182 183 +80 1 2 3 33 183 184 +81 1 2 3 33 184 10 +82 1 2 14 34 38 185 +83 1 2 14 34 185 186 +84 1 2 14 34 186 187 +85 1 2 14 34 187 32 +86 1 2 4 35 39 188 +87 1 2 4 35 188 189 +88 1 2 4 35 189 12 +89 1 2 9 36 39 190 +90 1 2 9 36 190 191 +91 1 2 9 36 191 192 +92 1 2 9 36 192 22 +93 1 2 5 37 40 193 +94 1 2 5 37 193 194 +95 1 2 5 37 194 195 +96 1 2 5 37 195 27 +97 1 2 11 38 40 196 +98 1 2 11 38 196 197 +99 1 2 11 38 197 20 +100 1 2 12 39 15 198 +101 1 2 12 39 198 199 +102 1 2 12 39 199 36 +103 1 2 7 40 41 200 +104 1 2 7 40 200 201 +105 1 2 7 40 201 202 +106 1 2 7 40 202 203 +107 1 2 7 40 203 204 +108 1 2 7 40 204 205 +109 1 2 7 40 205 206 +110 1 2 7 40 206 18 +111 1 2 15 41 41 207 +112 1 2 15 41 207 208 +113 1 2 15 41 208 209 +114 1 2 15 41 209 42 +115 1 2 14 42 19 210 +116 1 2 14 42 210 211 +117 1 2 14 42 211 38 +118 1 2 11 43 20 212 +119 1 2 11 43 212 213 +120 1 2 11 43 213 35 +121 1 2 8 44 42 214 +122 1 2 8 44 214 215 +123 1 2 8 44 215 216 +124 1 2 8 44 216 217 +125 1 2 8 44 217 218 +126 1 2 8 44 218 20 +127 1 2 15 45 42 219 +128 1 2 15 45 219 34 +129 1 2 5 46 27 220 +130 1 2 5 46 220 221 +131 1 2 5 46 221 222 +132 1 2 5 46 222 14 +133 1 2 12 47 43 28 +134 1 2 2 48 43 223 +135 1 2 2 48 223 224 +136 1 2 2 48 224 8 +137 2 2 1 1 475 500 709 +138 2 2 1 1 448 795 8 +139 2 2 1 1 795 415 8 +140 2 2 1 1 743 284 362 +141 2 2 1 1 400 591 90 +142 2 2 1 1 457 284 743 +143 2 2 1 1 321 454 560 +144 2 2 1 1 29 432 379 +145 2 2 1 1 121 122 364 +146 2 2 1 1 83 375 436 +147 2 2 1 1 363 777 491 +148 2 2 1 1 284 348 619 +149 2 2 1 1 299 368 515 +150 2 2 1 1 274 583 377 +151 2 2 1 1 384 215 642 +152 2 2 1 1 270 534 453 +153 2 2 1 1 267 396 468 +154 2 2 1 1 296 522 392 +155 2 2 1 1 322 653 381 +156 2 2 1 1 159 524 376 +157 2 2 1 1 212 564 597 +158 2 2 1 1 224 382 448 +159 2 2 1 1 190 391 191 +160 2 2 1 1 16 551 59 +161 2 2 1 1 374 745 531 +162 2 2 1 1 381 653 626 +163 2 2 1 1 90 591 89 +164 2 2 1 1 276 560 406 +165 2 2 1 1 276 460 394 +166 2 2 1 1 801 288 496 +167 2 2 1 1 308 378 572 +168 2 2 1 1 132 508 372 +169 2 2 1 1 49 50 404 +170 2 2 1 1 186 380 187 +171 2 2 1 1 38 455 185 +172 2 2 1 1 120 369 768 +173 2 2 1 1 131 132 372 +174 2 2 1 1 346 453 534 +175 2 2 1 1 49 404 604 +176 2 2 1 1 273 494 386 +177 2 2 1 1 38 182 455 +178 2 2 1 1 846 443 406 +179 2 2 1 1 277 467 373 +180 2 2 1 1 58 59 551 +181 2 2 1 1 145 547 641 +182 2 2 1 1 17 394 460 +183 2 2 1 1 280 374 531 +184 2 2 1 1 106 107 439 +185 2 2 1 1 285 781 374 +186 2 2 1 1 382 723 790 +187 2 2 1 1 277 373 638 +188 2 2 1 1 55 56 547 +189 2 2 1 1 332 405 482 +190 2 2 1 1 267 437 396 +191 2 2 1 1 23 656 532 +192 2 2 1 1 96 97 529 +193 2 2 1 1 35 568 213 +194 2 2 1 1 64 427 496 +195 2 2 1 1 97 366 606 +196 2 2 1 1 291 560 454 +197 2 2 1 1 284 461 348 +198 2 2 1 1 363 491 662 +199 2 2 1 1 123 124 373 +200 2 2 1 1 108 376 559 +201 2 2 1 1 301 488 398 +202 2 2 1 1 276 600 460 +203 2 2 1 1 299 506 368 +204 2 2 1 1 58 551 589 +205 2 2 1 1 360 704 610 +206 2 2 1 1 204 408 532 +207 2 2 1 1 306 428 513 +208 2 2 1 1 9 509 375 +209 2 2 1 1 223 382 224 +210 2 2 1 1 64 65 427 +211 2 2 1 1 275 407 436 +212 2 2 1 1 37 172 447 +213 2 2 1 1 285 430 781 +214 2 2 1 1 212 213 564 +215 2 2 1 1 267 507 466 +216 2 2 1 1 97 537 366 +217 2 2 1 1 334 410 545 +218 2 2 1 1 623 562 223 +219 2 2 1 1 56 584 547 +220 2 2 1 1 218 565 709 +221 2 2 1 1 177 515 178 +222 2 2 1 1 119 120 768 +223 2 2 1 1 199 659 661 +224 2 2 1 1 29 379 465 +225 2 2 1 1 37 806 177 +226 2 2 1 1 308 576 378 +227 2 2 1 1 35 166 568 +228 2 2 1 1 504 810 27 +229 2 2 1 1 9 375 83 +230 2 2 1 1 215 216 642 +231 2 2 1 1 43 723 223 +232 2 2 1 1 427 801 496 +233 2 2 1 1 267 466 437 +234 2 2 1 1 295 514 365 +235 2 2 1 1 121 364 452 +236 2 2 1 1 77 471 370 +237 2 2 1 1 77 370 798 +238 2 2 1 1 291 846 406 +239 2 2 1 1 276 466 560 +240 2 2 1 1 306 513 521 +241 2 2 1 1 295 365 512 +242 2 2 1 1 325 358 740 +243 2 2 1 1 284 619 362 +244 2 2 1 1 190 687 391 +245 2 2 1 1 23 532 408 +246 2 2 1 1 295 667 720 +247 2 2 1 1 107 559 439 +248 2 2 1 1 26 805 435 +249 2 2 1 1 709 500 196 +250 2 2 1 1 120 452 369 +251 2 2 1 1 57 589 381 +252 2 2 1 1 143 641 383 +253 2 2 1 1 35 527 169 +254 2 2 1 1 330 638 373 +255 2 2 1 1 123 373 467 +256 2 2 1 1 133 410 508 +257 2 2 1 1 386 494 702 +258 2 2 1 1 358 562 623 +259 2 2 1 1 19 113 402 +260 2 2 1 1 73 389 516 +261 2 2 1 1 57 381 584 +262 2 2 1 1 294 389 510 +263 2 2 1 1 287 465 379 +264 2 2 1 1 286 458 378 +265 2 2 1 1 33 406 443 +266 2 2 1 1 223 723 382 +267 2 2 1 1 152 413 666 +268 2 2 1 1 96 529 422 +269 2 2 1 1 281 334 545 +270 2 2 1 1 673 112 462 +271 2 2 1 1 358 734 740 +272 2 2 1 1 83 84 434 +273 2 2 1 1 16 99 551 +274 2 2 1 1 122 467 364 +275 2 2 1 1 86 185 440 +276 2 2 1 1 788 582 306 +277 2 2 1 1 198 659 199 +278 2 2 1 1 288 658 355 +279 2 2 1 1 197 20 218 +280 2 2 1 1 96 498 537 +281 2 2 1 1 178 515 817 +282 2 2 1 1 35 213 527 +283 2 2 1 1 37 177 82 +284 2 2 1 1 197 218 709 +285 2 2 1 1 170 171 411 +286 2 2 1 1 144 145 641 +287 2 2 1 1 284 399 786 +288 2 2 1 1 274 417 583 +289 2 2 1 1 221 222 423 +290 2 2 1 1 286 378 497 +291 2 2 1 1 94 822 370 +292 2 2 1 1 151 152 666 +293 2 2 1 1 309 416 456 +294 2 2 1 1 199 617 592 +295 2 2 1 1 681 192 451 +296 2 2 1 1 281 456 416 +297 2 2 1 1 283 698 426 +298 2 2 1 1 293 396 730 +299 2 2 1 1 139 371 620 +300 2 2 1 1 378 458 572 +301 2 2 1 1 15 659 198 +302 2 2 1 1 29 112 673 +303 2 2 1 1 8 224 448 +304 2 2 1 1 207 610 704 +305 2 2 1 1 191 192 681 +306 2 2 1 1 290 491 777 +307 2 2 1 1 315 680 407 +308 2 2 1 1 316 604 404 +309 2 2 1 1 426 698 650 +310 2 2 1 1 15 92 659 +311 2 2 1 1 21 104 401 +312 2 2 1 1 17 590 394 +313 2 2 1 1 33 503 114 +314 2 2 1 1 21 400 784 +315 2 2 1 1 329 413 495 +316 2 2 1 1 276 406 600 +317 2 2 1 1 295 512 667 +318 2 2 1 1 16 150 588 +319 2 2 1 1 183 824 388 +320 2 2 1 1 332 482 650 +321 2 2 1 1 116 388 824 +322 2 2 1 1 42 103 625 +323 2 2 1 1 317 469 507 +324 2 2 1 1 95 498 96 +325 2 2 1 1 204 532 205 +326 2 2 1 1 83 436 84 +327 2 2 1 1 88 591 429 +328 2 2 1 1 312 479 434 +329 2 2 1 1 146 147 397 +330 2 2 1 1 299 595 398 +331 2 2 1 1 324 484 592 +332 2 2 1 1 173 542 398 +333 2 2 1 1 94 370 587 +334 2 2 1 1 408 582 788 +335 2 2 1 1 187 380 683 +336 2 2 1 1 284 786 461 +337 2 2 1 1 159 160 524 +338 2 2 1 1 101 420 102 +339 2 2 1 1 354 532 656 +340 2 2 1 1 272 327 608 +341 2 2 1 1 140 141 409 +342 2 2 1 1 292 559 376 +343 2 2 1 1 288 355 496 +344 2 2 1 1 321 507 469 +345 2 2 1 1 133 545 410 +346 2 2 1 1 277 646 467 +347 2 2 1 1 396 437 730 +348 2 2 1 1 21 784 104 +349 2 2 1 1 280 531 481 +350 2 2 1 1 7 80 514 +351 2 2 1 1 83 434 685 +352 2 2 1 1 321 469 454 +353 2 2 1 1 21 658 400 +354 2 2 1 1 341 592 484 +355 2 2 1 1 343 614 566 +356 2 2 1 1 191 391 497 +357 2 2 1 1 11 87 446 +358 2 2 1 1 385 740 453 +359 2 2 1 1 270 453 740 +360 2 2 1 1 280 697 836 +361 2 2 1 1 112 172 37 +362 2 2 1 1 37 82 558 +363 2 2 1 1 108 636 376 +364 2 2 1 1 340 702 494 +365 2 2 1 1 185 186 440 +366 2 2 1 1 139 631 371 +367 2 2 1 1 159 376 636 +368 2 2 1 1 810 220 27 +369 2 2 1 1 292 376 524 +370 2 2 1 1 307 468 396 +371 2 2 1 1 330 373 518 +372 2 2 1 1 299 398 488 +373 2 2 1 1 36 661 79 +374 2 2 1 1 116 491 388 +375 2 2 1 1 284 737 399 +376 2 2 1 1 323 490 431 +377 2 2 1 1 43 176 538 +378 2 2 1 1 296 392 607 +379 2 2 1 1 285 374 628 +380 2 2 1 1 80 829 514 +381 2 2 1 1 16 588 99 +382 2 2 1 1 290 388 491 +383 2 2 1 1 85 86 440 +384 2 2 1 1 368 817 515 +385 2 2 1 1 297 431 490 +386 2 2 1 1 132 133 508 +387 2 2 1 1 36 199 661 +388 2 2 1 1 40 91 812 +389 2 2 1 1 323 433 451 +390 2 2 1 1 217 519 642 +391 2 2 1 1 97 444 529 +392 2 2 1 1 271 653 803 +393 2 2 1 1 478 670 792 +394 2 2 1 1 201 503 699 +395 2 2 1 1 290 602 388 +396 2 2 1 1 67 612 487 +397 2 2 1 1 386 702 514 +398 2 2 1 1 295 386 514 +399 2 2 1 1 325 377 543 +400 2 2 1 1 153 154 431 +401 2 2 1 1 186 593 380 +402 2 2 1 1 90 784 400 +403 2 2 1 1 364 467 646 +404 2 2 1 1 76 77 798 +405 2 2 1 1 807 522 296 +406 2 2 1 1 7 514 702 +407 2 2 1 1 47 48 438 +408 2 2 1 1 335 450 446 +409 2 2 1 1 315 511 680 +410 2 2 1 1 38 211 182 +411 2 2 1 1 88 89 591 +412 2 2 1 1 272 410 640 +413 2 2 1 1 295 544 386 +414 2 2 1 1 310 462 493 +415 2 2 1 1 124 701 373 +416 2 2 1 1 36 168 617 +417 2 2 1 1 303 535 494 +418 2 2 1 1 38 86 511 +419 2 2 1 1 73 688 389 +420 2 2 1 1 183 184 824 +421 2 2 1 1 41 200 501 +422 2 2 1 1 378 576 827 +423 2 2 1 1 340 494 535 +424 2 2 1 1 200 503 201 +425 2 2 1 1 115 116 824 +426 2 2 1 1 307 396 733 +427 2 2 1 1 26 435 463 +428 2 2 1 1 294 516 389 +429 2 2 1 1 273 386 544 +430 2 2 1 1 304 721 384 +431 2 2 1 1 301 570 488 +432 2 2 1 1 108 654 390 +433 2 2 1 1 526 725 182 +434 2 2 1 1 222 710 423 +435 2 2 1 1 19 402 480 +436 2 2 1 1 725 183 182 +437 2 2 1 1 12 579 157 +438 2 2 1 1 6 473 167 +439 2 2 1 1 265 745 781 +440 2 2 1 1 310 468 502 +441 2 2 1 1 319 451 433 +442 2 2 1 1 288 400 658 +443 2 2 1 1 360 610 549 +444 2 2 1 1 294 482 405 +445 2 2 1 1 143 144 641 +446 2 2 1 1 334 640 410 +447 2 2 1 1 101 102 457 +448 2 2 1 1 103 352 625 +449 2 2 1 1 403 789 517 +450 2 2 1 1 280 481 697 +451 2 2 1 1 204 205 412 +452 2 2 1 1 77 716 415 +453 2 2 1 1 11 446 450 +454 2 2 1 1 198 199 592 +455 2 2 1 1 293 590 360 +456 2 2 1 1 331 481 531 +457 2 2 1 1 725 518 184 +458 2 2 1 1 287 379 637 +459 2 2 1 1 143 383 571 +460 2 2 1 1 358 543 633 +461 2 2 1 1 317 493 462 +462 2 2 1 1 107 108 559 +463 2 2 1 1 294 405 516 +464 2 2 1 1 38 511 211 +465 2 2 1 1 285 608 414 +466 2 2 1 1 318 552 483 +467 2 2 1 1 285 441 608 +468 2 2 1 1 305 498 581 +469 2 2 1 1 173 398 595 +470 2 2 1 1 6 432 502 +471 2 2 1 1 325 543 358 +472 2 2 1 1 179 180 512 +473 2 2 1 1 286 497 735 +474 2 2 1 1 310 502 432 +475 2 2 1 1 267 468 493 +476 2 2 1 1 396 541 733 +477 2 2 1 1 185 86 38 +478 2 2 1 1 327 414 608 +479 2 2 1 1 325 780 377 +480 2 2 1 1 272 640 327 +481 2 2 1 1 75 422 529 +482 2 2 1 1 183 725 184 +483 2 2 1 1 408 763 582 +484 2 2 1 1 114 503 741 +485 2 2 1 1 12 189 579 +486 2 2 1 1 36 617 199 +487 2 2 1 1 304 519 473 +488 2 2 1 1 328 715 412 +489 2 2 1 1 179 512 818 +490 2 2 1 1 150 151 666 +491 2 2 1 1 155 156 433 +492 2 2 1 1 391 687 548 +493 2 2 1 1 276 437 466 +494 2 2 1 1 322 381 589 +495 2 2 1 1 309 567 504 +496 2 2 1 1 57 58 589 +497 2 2 1 1 88 429 580 +498 2 2 1 1 150 666 588 +499 2 2 1 1 307 502 468 +500 2 2 1 1 265 563 745 +501 2 2 1 1 72 73 516 +502 2 2 1 1 726 668 492 +503 2 2 1 1 41 501 207 +504 2 2 1 1 183 388 602 +505 2 2 1 1 333 473 519 +506 2 2 1 1 276 394 730 +507 2 2 1 1 147 522 397 +508 2 2 1 1 397 522 807 +509 2 2 1 1 129 736 489 +510 2 2 1 1 391 735 497 +511 2 2 1 1 281 416 700 +512 2 2 1 1 298 419 805 +513 2 2 1 1 610 820 549 +514 2 2 1 1 392 522 809 +515 2 2 1 1 333 597 564 +516 2 2 1 1 128 129 489 +517 2 2 1 1 389 756 510 +518 2 2 1 1 66 67 487 +519 2 2 1 1 297 495 413 +520 2 2 1 1 300 575 505 +521 2 2 1 1 182 211 526 +522 2 2 1 1 56 57 584 +523 2 2 1 1 94 95 822 +524 2 2 1 1 133 134 545 +525 2 2 1 1 33 600 406 +526 2 2 1 1 5 495 757 +527 2 2 1 1 5 757 765 +528 2 2 1 1 292 425 706 +529 2 2 1 1 11 726 492 +530 2 2 1 1 517 838 813 +531 2 2 1 1 14 594 409 +532 2 2 1 1 96 422 822 +533 2 2 1 1 87 580 446 +534 2 2 1 1 402 802 638 +535 2 2 1 1 281 387 334 +536 2 2 1 1 272 508 410 +537 2 2 1 1 41 794 114 +538 2 2 1 1 297 413 578 +539 2 2 1 1 181 74 30 +540 2 2 1 1 202 428 582 +541 2 2 1 1 383 635 571 +542 2 2 1 1 313 509 553 +543 2 2 1 1 97 606 98 +544 2 2 1 1 316 404 530 +545 2 2 1 1 216 217 642 +546 2 2 1 1 190 191 681 +547 2 2 1 1 477 596 649 +548 2 2 1 1 314 505 575 +549 2 2 1 1 377 583 672 +550 2 2 1 1 113 651 402 +551 2 2 1 1 190 681 556 +552 2 2 1 1 13 91 475 +553 2 2 1 1 75 798 422 +554 2 2 1 1 293 541 396 +555 2 2 1 1 282 779 395 +556 2 2 1 1 356 478 792 +557 2 2 1 1 68 69 445 +558 2 2 1 1 318 571 635 +559 2 2 1 1 393 720 667 +560 2 2 1 1 318 635 552 +561 2 2 1 1 216 459 217 +562 2 2 1 1 335 487 612 +563 2 2 1 1 301 417 528 +564 2 2 1 1 216 770 459 +565 2 2 1 1 97 98 444 +566 2 2 1 1 301 542 417 +567 2 2 1 1 173 174 542 +568 2 2 1 1 101 639 420 +569 2 2 1 1 270 740 734 +570 2 2 1 1 344 434 479 +571 2 2 1 1 274 528 417 +572 2 2 1 1 802 277 638 +573 2 2 1 1 292 706 438 +574 2 2 1 1 108 390 648 +575 2 2 1 1 381 626 584 +576 2 2 1 1 135 707 456 +577 2 2 1 1 141 523 409 +578 2 2 1 1 172 173 595 +579 2 2 1 1 103 652 352 +580 2 2 1 1 337 581 498 +581 2 2 1 1 296 533 401 +582 2 2 1 1 109 24 158 +583 2 2 1 1 204 412 715 +584 2 2 1 1 154 627 431 +585 2 2 1 1 304 384 642 +586 2 2 1 1 46 706 425 +587 2 2 1 1 337 539 423 +588 2 2 1 1 308 546 576 +589 2 2 1 1 324 592 617 +590 2 2 1 1 385 780 740 +591 2 2 1 1 203 573 771 +592 2 2 1 1 170 411 769 +593 2 2 1 1 215 403 216 +594 2 2 1 1 275 676 407 +595 2 2 1 1 292 524 425 +596 2 2 1 1 85 440 574 +597 2 2 1 1 301 398 542 +598 2 2 1 1 366 803 653 +599 2 2 1 1 329 495 719 +600 2 2 1 1 21 401 533 +601 2 2 1 1 314 513 428 +602 2 2 1 1 278 792 670 +603 2 2 1 1 299 447 595 +604 2 2 1 1 8 415 572 +605 2 2 1 1 76 716 77 +606 2 2 1 1 320 584 626 +607 2 2 1 1 186 686 440 +608 2 2 1 1 289 517 424 +609 2 2 1 1 204 763 408 +610 2 2 1 1 33 443 505 +611 2 2 1 1 357 576 546 +612 2 2 1 1 42 214 614 +613 2 2 1 1 207 208 610 +614 2 2 1 1 292 439 559 +615 2 2 1 1 297 578 431 +616 2 2 1 1 152 578 413 +617 2 2 1 1 293 394 590 +618 2 2 1 1 344 685 434 +619 2 2 1 1 375 509 842 +620 2 2 1 1 801 591 288 +621 2 2 1 1 421 298 805 +622 2 2 1 1 311 767 596 +623 2 2 1 1 395 779 675 +624 2 2 1 1 429 591 801 +625 2 2 1 1 486 596 767 +626 2 2 1 1 120 121 452 +627 2 2 1 1 33 505 503 +628 2 2 1 1 281 700 387 +629 2 2 1 1 328 412 670 +630 2 2 1 1 266 482 506 +631 2 2 1 1 215 384 566 +632 2 2 1 1 7 449 669 +633 2 2 1 1 337 423 710 +634 2 2 1 1 47 438 706 +635 2 2 1 1 299 488 506 +636 2 2 1 1 280 628 374 +637 2 2 1 1 140 409 631 +638 2 2 1 1 180 756 688 +639 2 2 1 1 282 418 779 +640 2 2 1 1 271 803 552 +641 2 2 1 1 96 537 97 +642 2 2 1 1 312 663 479 +643 2 2 1 1 288 591 400 +644 2 2 1 1 43 538 171 +645 2 2 1 1 350 504 567 +646 2 2 1 1 293 360 549 +647 2 2 1 1 200 399 501 +648 2 2 1 1 321 466 507 +649 2 2 1 1 290 777 593 +650 2 2 1 1 17 460 794 +651 2 2 1 1 14 409 609 +652 2 2 1 1 309 598 416 +653 2 2 1 1 266 570 426 +654 2 2 1 1 302 427 618 +655 2 2 1 1 102 752 457 +656 2 2 1 1 201 202 461 +657 2 2 1 1 95 96 822 +658 2 2 1 1 131 372 697 +659 2 2 1 1 351 423 634 +660 2 2 1 1 266 650 482 +661 2 2 1 1 300 449 615 +662 2 2 1 1 202 203 771 +663 2 2 1 1 39 104 784 +664 2 2 1 1 77 78 471 +665 2 2 1 1 276 730 437 +666 2 2 1 1 332 605 405 +667 2 2 1 1 577 307 733 +668 2 2 1 1 50 674 404 +669 2 2 1 1 331 489 736 +670 2 2 1 1 19 480 210 +671 2 2 1 1 323 431 627 +672 2 2 1 1 7 669 80 +673 2 2 1 1 326 475 709 +674 2 2 1 1 265 418 563 +675 2 2 1 1 87 88 580 +676 2 2 1 1 368 727 510 +677 2 2 1 1 290 455 602 +678 2 2 1 1 202 699 428 +679 2 2 1 1 2 729 118 +680 2 2 1 1 305 537 498 +681 2 2 1 1 22 827 576 +682 2 2 1 1 269 521 513 +683 2 2 1 1 347 587 471 +684 2 2 1 1 200 786 399 +685 2 2 1 1 216 403 770 +686 2 2 1 1 297 490 713 +687 2 2 1 1 135 136 707 +688 2 2 1 1 191 497 192 +689 2 2 1 1 167 473 712 +690 2 2 1 1 290 593 455 +691 2 2 1 1 289 424 675 +692 2 2 1 1 9 553 509 +693 2 2 1 1 289 675 779 +694 2 2 1 1 31 478 651 +695 2 2 1 1 349 557 430 +696 2 2 1 1 347 661 659 +697 2 2 1 1 42 750 103 +698 2 2 1 1 6 677 432 +699 2 2 1 1 320 641 547 +700 2 2 1 1 34 616 675 +701 2 2 1 1 289 779 599 +702 2 2 1 1 774 407 85 +703 2 2 1 1 320 843 641 +704 2 2 1 1 81 454 469 +705 2 2 1 1 23 472 105 +706 2 2 1 1 75 546 76 +707 2 2 1 1 193 194 477 +708 2 2 1 1 43 171 723 +709 2 2 1 1 84 574 434 +710 2 2 1 1 138 139 620 +711 2 2 1 1 407 774 436 +712 2 2 1 1 171 538 411 +713 2 2 1 1 343 748 614 +714 2 2 1 1 40 500 91 +715 2 2 1 1 285 628 441 +716 2 2 1 1 5 75 529 +717 2 2 1 1 418 599 779 +718 2 2 1 1 15 474 111 +719 2 2 1 1 215 789 403 +720 2 2 1 1 310 493 468 +721 2 2 1 1 27 717 110 +722 2 2 1 1 75 76 798 +723 2 2 1 1 63 64 496 +724 2 2 1 1 271 626 653 +725 2 2 1 1 339 426 762 +726 2 2 1 1 274 377 780 +727 2 2 1 1 23 408 788 +728 2 2 1 1 308 572 415 +729 2 2 1 1 325 740 780 +730 2 2 1 1 407 680 85 +731 2 2 1 1 46 425 811 +732 2 2 1 1 300 505 443 +733 2 2 1 1 315 407 676 +734 2 2 1 1 23 105 530 +735 2 2 1 1 297 713 757 +736 2 2 1 1 267 493 507 +737 2 2 1 1 185 455 593 +738 2 2 1 1 8 458 738 +739 2 2 1 1 339 722 426 +740 2 2 1 1 202 582 203 +741 2 2 1 1 294 510 727 +742 2 2 1 1 302 801 427 +743 2 2 1 1 77 415 795 +744 2 2 1 1 122 123 467 +745 2 2 1 1 298 485 419 +746 2 2 1 1 283 426 722 +747 2 2 1 1 342 520 737 +748 2 2 1 1 28 543 672 +749 2 2 1 1 287 419 485 +750 2 2 1 1 391 548 735 +751 2 2 1 1 155 433 627 +752 2 2 1 1 2 52 729 +753 2 2 1 1 367 534 734 +754 2 2 1 1 370 471 587 +755 2 2 1 1 302 429 801 +756 2 2 1 1 346 735 548 +757 2 2 1 1 5 529 444 +758 2 2 1 1 10 115 824 +759 2 2 1 1 265 599 418 +760 2 2 1 1 176 672 583 +761 2 2 1 1 146 397 796 +762 2 2 1 1 20 484 212 +763 2 2 1 1 10 824 184 +764 2 2 1 1 27 220 717 +765 2 2 1 1 221 423 761 +766 2 2 1 1 275 436 678 +767 2 2 1 1 81 469 82 +768 2 2 1 1 39 784 90 +769 2 2 1 1 269 536 535 +770 2 2 1 1 329 666 413 +771 2 2 1 1 310 432 673 +772 2 2 1 1 172 689 173 +773 2 2 1 1 82 755 558 +774 2 2 1 1 331 643 489 +775 2 2 1 1 306 521 472 +776 2 2 1 1 278 808 452 +777 2 2 1 1 353 472 521 +778 2 2 1 1 323 627 433 +779 2 2 1 1 351 761 423 +780 2 2 1 1 116 117 491 +781 2 2 1 1 136 137 567 +782 2 2 1 1 373 701 518 +783 2 2 1 1 285 776 430 +784 2 2 1 1 40 193 649 +785 2 2 1 1 13 671 703 +786 2 2 1 1 68 445 612 +787 2 2 1 1 302 580 429 +788 2 2 1 1 282 395 660 +789 2 2 1 1 385 492 780 +790 2 2 1 1 73 74 688 +791 2 2 1 1 308 415 716 +792 2 2 1 1 182 602 455 +793 2 2 1 1 100 639 101 +794 2 2 1 1 380 593 777 +795 2 2 1 1 13 475 476 +796 2 2 1 1 287 682 465 +797 2 2 1 1 282 842 509 +798 2 2 1 1 327 703 414 +799 2 2 1 1 186 187 686 +800 2 2 1 1 266 506 488 +801 2 2 1 1 46 47 706 +802 2 2 1 1 106 525 107 +803 2 2 1 1 335 695 450 +804 2 2 1 1 107 654 108 +805 2 2 1 1 153 431 578 +806 2 2 1 1 112 558 462 +807 2 2 1 1 349 430 776 +808 2 2 1 1 403 517 813 +809 2 2 1 1 375 678 436 +810 2 2 1 1 20 197 486 +811 2 2 1 1 365 818 512 +812 2 2 1 1 306 582 428 +813 2 2 1 1 352 624 625 +814 2 2 1 1 442 577 733 +815 2 2 1 1 102 420 652 +816 2 2 1 1 69 550 445 +817 2 2 1 1 365 514 829 +818 2 2 1 1 356 651 478 +819 2 2 1 1 316 438 604 +820 2 2 1 1 20 486 484 +821 2 2 1 1 166 167 712 +822 2 2 1 1 315 676 420 +823 2 2 1 1 158 159 636 +824 2 2 1 1 331 553 643 +825 2 2 1 1 361 696 499 +826 2 2 1 1 316 645 439 +827 2 2 1 1 314 428 699 +828 2 2 1 1 266 426 650 +829 2 2 1 1 53 54 629 +830 2 2 1 1 389 688 756 +831 2 2 1 1 270 734 534 +832 2 2 1 1 214 566 614 +833 2 2 1 1 304 442 721 +834 2 2 1 1 196 649 596 +835 2 2 1 1 399 737 520 +836 2 2 1 1 312 574 440 +837 2 2 1 1 296 401 807 +838 2 2 1 1 18 119 768 +839 2 2 1 1 282 728 418 +840 2 2 1 1 180 688 181 +841 2 2 1 1 300 615 536 +842 2 2 1 1 300 846 449 +843 2 2 1 1 100 555 639 +844 2 2 1 1 20 597 218 +845 2 2 1 1 89 548 90 +846 2 2 1 1 48 604 438 +847 2 2 1 1 104 690 401 +848 2 2 1 1 20 212 597 +849 2 2 1 1 328 585 573 +850 2 2 1 1 298 693 411 +851 2 2 1 1 314 699 503 +852 2 2 1 1 65 618 427 +853 2 2 1 1 15 111 665 +854 2 2 1 1 281 711 456 +855 2 2 1 1 361 754 696 +856 2 2 1 1 8 572 458 +857 2 2 1 1 18 768 206 +858 2 2 1 1 80 454 81 +859 2 2 1 1 187 540 686 +860 2 2 1 1 32 683 54 +861 2 2 1 1 15 665 92 +862 2 2 1 1 185 593 186 +863 2 2 1 1 340 535 536 +864 2 2 1 1 291 406 560 +865 2 2 1 1 268 442 733 +866 2 2 1 1 314 575 513 +867 2 2 1 1 126 127 479 +868 2 2 1 1 283 450 695 +869 2 2 1 1 181 744 667 +870 2 2 1 1 313 553 531 +871 2 2 1 1 32 187 683 +872 2 2 1 1 302 618 487 +873 2 2 1 1 28 43 562 +874 2 2 1 1 383 641 843 +875 2 2 1 1 323 451 611 +876 2 2 1 1 108 109 636 +877 2 2 1 1 141 142 523 +878 2 2 1 1 369 452 808 +879 2 2 1 1 162 622 744 +880 2 2 1 1 324 657 484 +881 2 2 1 1 29 673 432 +882 2 2 1 1 220 601 221 +883 2 2 1 1 14 222 594 +884 2 2 1 1 312 434 574 +885 2 2 1 1 76 546 716 +886 2 2 1 1 7 615 449 +887 2 2 1 1 89 694 548 +888 2 2 1 1 19 100 732 +889 2 2 1 1 19 732 113 +890 2 2 1 1 6 797 473 +891 2 2 1 1 302 446 580 +892 2 2 1 1 393 667 744 +893 2 2 1 1 283 445 698 +894 2 2 1 1 315 420 639 +895 2 2 1 1 359 813 838 +896 2 2 1 1 305 581 483 +897 2 2 1 1 392 773 691 +898 2 2 1 1 299 515 447 +899 2 2 1 1 296 607 533 +900 2 2 1 1 344 479 753 +901 2 2 1 1 402 651 802 +902 2 2 1 1 39 188 690 +903 2 2 1 1 395 675 616 +904 2 2 1 1 304 642 519 +905 2 2 1 1 293 730 394 +906 2 2 1 1 338 678 842 +907 2 2 1 1 25 561 569 +908 2 2 1 1 106 439 645 +909 2 2 1 1 37 558 112 +910 2 2 1 1 341 484 486 +911 2 2 1 1 354 656 674 +912 2 2 1 1 205 783 412 +913 2 2 1 1 13 703 91 +914 2 2 1 1 13 476 671 +915 2 2 1 1 404 674 656 +916 2 2 1 1 287 637 435 +917 2 2 1 1 172 595 447 +918 2 2 1 1 342 737 457 +919 2 2 1 1 166 712 564 +920 2 2 1 1 458 850 738 +921 2 2 1 1 201 699 202 +922 2 2 1 1 273 754 800 +923 2 2 1 1 19 210 826 +924 2 2 1 1 311 834 464 +925 2 2 1 1 317 462 558 +926 2 2 1 1 265 781 739 +927 2 2 1 1 207 520 208 +928 2 2 1 1 62 815 632 +929 2 2 1 1 27 598 504 +930 2 2 1 1 292 438 766 +931 2 2 1 1 182 183 602 +932 2 2 1 1 69 70 550 +933 2 2 1 1 273 800 494 +934 2 2 1 1 62 63 815 +935 2 2 1 1 316 766 438 +936 2 2 1 1 54 683 629 +937 2 2 1 1 348 573 585 +938 2 2 1 1 347 799 587 +939 2 2 1 1 174 485 175 +940 2 2 1 1 147 148 522 +941 2 2 1 1 266 488 570 +942 2 2 1 1 156 664 433 +943 2 2 1 1 402 638 480 +944 2 2 1 1 42 614 209 +945 2 2 1 1 300 443 846 +946 2 2 1 1 177 178 679 +947 2 2 1 1 319 433 664 +948 2 2 1 1 136 567 707 +949 2 2 1 1 326 565 459 +950 2 2 1 1 311 477 834 +951 2 2 1 1 414 703 671 +952 2 2 1 1 430 739 781 +953 2 2 1 1 42 209 750 +954 2 2 1 1 316 439 766 +955 2 2 1 1 18 655 51 +956 2 2 1 1 482 727 506 +957 2 2 1 1 329 444 708 +958 2 2 1 1 39 819 188 +959 2 2 1 1 37 447 806 +960 2 2 1 1 84 85 574 +961 2 2 1 1 342 457 752 +962 2 2 1 1 284 457 737 +963 2 2 1 1 305 483 552 +964 2 2 1 1 23 788 472 +965 2 2 1 1 285 414 776 +966 2 2 1 1 36 79 758 +967 2 2 1 1 292 766 439 +968 2 2 1 1 223 224 623 +969 2 2 1 1 193 613 387 +970 2 2 1 1 18 206 655 +971 2 2 1 1 142 571 523 +972 2 2 1 1 60 691 773 +973 2 2 1 1 320 547 584 +974 2 2 1 1 27 195 598 +975 2 2 1 1 195 464 834 +976 2 2 1 1 303 800 654 +977 2 2 1 1 110 772 464 +978 2 2 1 1 70 605 550 +979 2 2 1 1 91 703 561 +980 2 2 1 1 353 785 472 +981 2 2 1 1 321 560 466 +982 2 2 1 1 423 539 634 +983 2 2 1 1 378 827 497 +984 2 2 1 1 322 589 551 +985 2 2 1 1 286 534 850 +986 2 2 1 1 93 539 94 +987 2 2 1 1 192 611 451 +988 2 2 1 1 12 796 189 +989 2 2 1 1 162 163 622 +990 2 2 1 1 312 440 686 +991 2 2 1 1 88 453 694 +992 2 2 1 1 36 769 168 +993 2 2 1 1 6 577 797 +994 2 2 1 1 275 678 751 +995 2 2 1 1 278 452 845 +996 2 2 1 1 117 662 491 +997 2 2 1 1 127 753 479 +998 2 2 1 1 329 719 444 +999 2 2 1 1 397 603 796 +1000 2 2 1 1 142 143 571 +1001 2 2 1 1 336 607 691 +1002 2 2 1 1 317 507 493 +1003 2 2 1 1 74 181 688 +1004 2 2 1 1 189 796 603 +1005 2 2 1 1 323 611 490 +1006 2 2 1 1 5 444 719 +1007 2 2 1 1 335 446 791 +1008 2 2 1 1 152 153 578 +1009 2 2 1 1 36 170 769 +1010 2 2 1 1 212 484 657 +1011 2 2 1 1 156 157 664 +1012 2 2 1 1 268 721 442 +1013 2 2 1 1 346 548 694 +1014 2 2 1 1 283 695 445 +1015 2 2 1 1 202 771 461 +1016 2 2 1 1 445 695 612 +1017 2 2 1 1 98 708 444 +1018 2 2 1 1 109 158 636 +1019 2 2 1 1 23 530 656 +1020 2 2 1 1 41 114 741 +1021 2 2 1 1 188 189 603 +1022 2 2 1 1 420 676 840 +1023 2 2 1 1 28 633 543 +1024 2 2 1 1 81 82 679 +1025 2 2 1 1 28 647 43 +1026 2 2 1 1 15 198 714 +1027 2 2 1 1 206 768 783 +1028 2 2 1 1 63 496 815 +1029 2 2 1 1 217 459 565 +1030 2 2 1 1 344 489 643 +1031 2 2 1 1 22 490 611 +1032 2 2 1 1 313 531 745 +1033 2 2 1 1 205 206 783 +1034 2 2 1 1 88 787 453 +1035 2 2 1 1 197 596 486 +1036 2 2 1 1 92 634 93 +1037 2 2 1 1 318 523 571 +1038 2 2 1 1 31 585 478 +1039 2 2 1 1 15 714 474 +1040 2 2 1 1 311 464 772 +1041 2 2 1 1 302 791 446 +1042 2 2 1 1 356 802 651 +1043 2 2 1 1 275 840 676 +1044 2 2 1 1 210 830 211 +1045 2 2 1 1 303 654 525 +1046 2 2 1 1 29 465 689 +1047 2 2 1 1 102 652 103 +1048 2 2 1 1 364 845 452 +1049 2 2 1 1 314 503 505 +1050 2 2 1 1 14 483 581 +1051 2 2 1 1 105 472 785 +1052 2 2 1 1 211 830 526 +1053 2 2 1 1 281 545 711 +1054 2 2 1 1 78 795 448 +1055 2 2 1 1 168 169 718 +1056 2 2 1 1 345 720 622 +1057 2 2 1 1 283 722 450 +1058 2 2 1 1 322 606 653 +1059 2 2 1 1 92 665 634 +1060 2 2 1 1 311 772 474 +1061 2 2 1 1 327 640 569 +1062 2 2 1 1 426 570 762 +1063 2 2 1 1 32 125 837 +1064 2 2 1 1 12 146 796 +1065 2 2 1 1 332 550 605 +1066 2 2 1 1 214 215 566 +1067 2 2 1 1 11 450 726 +1068 2 2 1 1 19 826 100 +1069 2 2 1 1 112 689 172 +1070 2 2 1 1 29 689 112 +1071 2 2 1 1 110 111 772 +1072 2 2 1 1 286 735 534 +1073 2 2 1 1 331 531 553 +1074 2 2 1 1 24 109 644 +1075 2 2 1 1 4 60 773 +1076 2 2 1 1 220 810 601 +1077 2 2 1 1 107 525 654 +1078 2 2 1 1 298 411 692 +1079 2 2 1 1 393 744 622 +1080 2 2 1 1 71 821 605 +1081 2 2 1 1 287 435 419 +1082 2 2 1 1 336 691 778 +1083 2 2 1 1 14 609 483 +1084 2 2 1 1 319 681 451 +1085 2 2 1 1 33 114 600 +1086 2 2 1 1 91 500 475 +1087 2 2 1 1 34 793 616 +1088 2 2 1 1 382 742 448 +1089 2 2 1 1 193 477 649 +1090 2 2 1 1 309 456 707 +1091 2 2 1 1 307 577 502 +1092 2 2 1 1 157 579 664 +1093 2 2 1 1 109 648 644 +1094 2 2 1 1 36 758 170 +1095 2 2 1 1 318 483 609 +1096 2 2 1 1 279 731 770 +1097 2 2 1 1 39 90 687 +1098 2 2 1 1 195 848 464 +1099 2 2 1 1 193 387 700 +1100 2 2 1 1 310 673 462 +1101 2 2 1 1 25 613 40 +1102 2 2 1 1 175 844 176 +1103 2 2 1 1 269 513 575 +1104 2 2 1 1 334 387 835 +1105 2 2 1 1 300 536 575 +1106 2 2 1 1 317 755 469 +1107 2 2 1 1 194 195 834 +1108 2 2 1 1 393 622 720 +1109 2 2 1 1 341 474 714 +1110 2 2 1 1 293 549 541 +1111 2 2 1 1 405 605 821 +1112 2 2 1 1 164 804 499 +1113 2 2 1 1 125 126 663 +1114 2 2 1 1 51 655 674 +1115 2 2 1 1 101 457 743 +1116 2 2 1 1 291 454 669 +1117 2 2 1 1 295 720 544 +1118 2 2 1 1 84 436 774 +1119 2 2 1 1 217 565 218 +1120 2 2 1 1 441 847 608 +1121 2 2 1 1 30 744 181 +1122 2 2 1 1 130 697 481 +1123 2 2 1 1 316 530 645 +1124 2 2 1 1 167 637 677 +1125 2 2 1 1 286 850 458 +1126 2 2 1 1 366 653 606 +1127 2 2 1 1 399 520 501 +1128 2 2 1 1 359 557 813 +1129 2 2 1 1 39 690 104 +1130 2 2 1 1 70 71 605 +1131 2 2 1 1 193 700 194 +1132 2 2 1 1 326 459 731 +1133 2 2 1 1 337 684 539 +1134 2 2 1 1 287 485 682 +1135 2 2 1 1 272 608 847 +1136 2 2 1 1 34 624 793 +1137 2 2 1 1 3 853 145 +1138 2 2 1 1 208 816 209 +1139 2 2 1 1 100 743 732 +1140 2 2 1 1 200 201 786 +1141 2 2 1 1 332 650 698 +1142 2 2 1 1 130 131 697 +1143 2 2 1 1 174 682 485 +1144 2 2 1 1 361 644 648 +1145 2 2 1 1 416 598 852 +1146 2 2 1 1 336 533 607 +1147 2 2 1 1 379 677 637 +1148 2 2 1 1 164 165 804 +1149 2 2 1 1 367 850 534 +1150 2 2 1 1 66 487 618 +1151 2 2 1 1 379 432 677 +1152 2 2 1 1 348 461 771 +1153 2 2 1 1 148 809 522 +1154 2 2 1 1 50 51 674 +1155 2 2 1 1 207 501 520 +1156 2 2 1 1 173 689 465 +1157 2 2 1 1 114 460 600 +1158 2 2 1 1 168 718 617 +1159 2 2 1 1 175 176 583 +1160 2 2 1 1 93 94 587 +1161 2 2 1 1 269 575 536 +1162 2 2 1 1 78 782 471 +1163 2 2 1 1 60 61 691 +1164 2 2 1 1 39 687 190 +1165 2 2 1 1 78 448 742 +1166 2 2 1 1 390 654 800 +1167 2 2 1 1 336 632 621 +1168 2 2 1 1 385 453 787 +1169 2 2 1 1 102 103 752 +1170 2 2 1 1 424 724 749 +1171 2 2 1 1 192 497 827 +1172 2 2 1 1 125 663 540 +1173 2 2 1 1 309 504 598 +1174 2 2 1 1 409 594 631 +1175 2 2 1 1 311 596 477 +1176 2 2 1 1 346 694 453 +1177 2 2 1 1 110 464 848 +1178 2 2 1 1 176 844 692 +1179 2 2 1 1 409 523 609 +1180 2 2 1 1 100 101 743 +1181 2 2 1 1 374 781 745 +1182 2 2 1 1 34 219 624 +1183 2 2 1 1 139 140 631 +1184 2 2 1 1 327 569 561 +1185 2 2 1 1 22 611 192 +1186 2 2 1 1 282 509 728 +1187 2 2 1 1 349 671 476 +1188 2 2 1 1 347 471 782 +1189 2 2 1 1 274 492 668 +1190 2 2 1 1 39 190 819 +1191 2 2 1 1 445 550 698 +1192 2 2 1 1 95 684 498 +1193 2 2 1 1 7 702 615 +1194 2 2 1 1 527 657 718 +1195 2 2 1 1 48 49 604 +1196 2 2 1 1 355 621 632 +1197 2 2 1 1 4 773 149 +1198 2 2 1 1 315 747 511 +1199 2 2 1 1 338 751 678 +1200 2 2 1 1 108 648 109 +1201 2 2 1 1 344 753 489 +1202 2 2 1 1 330 480 638 +1203 2 2 1 1 200 741 503 +1204 2 2 1 1 130 481 736 +1205 2 2 1 1 328 478 585 +1206 2 2 1 1 25 812 561 +1207 2 2 1 1 405 821 516 +1208 2 2 1 1 67 68 612 +1209 2 2 1 1 65 66 618 +1210 2 2 1 1 129 130 736 +1211 2 2 1 1 322 551 814 +1212 2 2 1 1 173 682 174 +1213 2 2 1 1 345 544 720 +1214 2 2 1 1 349 832 557 +1215 2 2 1 1 31 619 585 +1216 2 2 1 1 418 728 563 +1217 2 2 1 1 196 596 197 +1218 2 2 1 1 268 541 549 +1219 2 2 1 1 105 106 645 +1220 2 2 1 1 166 637 167 +1221 2 2 1 1 98 99 708 +1222 2 2 1 1 279 813 557 +1223 2 2 1 1 313 728 509 +1224 2 2 1 1 94 684 95 +1225 2 2 1 1 80 669 454 +1226 2 2 1 1 80 81 829 +1227 2 2 1 1 304 473 797 +1228 2 2 1 1 173 465 682 +1229 2 2 1 1 417 542 831 +1230 2 2 1 1 154 155 627 +1231 2 2 1 1 318 609 523 +1232 2 2 1 1 289 838 517 +1233 2 2 1 1 294 727 482 +1234 2 2 1 1 304 797 442 +1235 2 2 1 1 271 552 635 +1236 2 2 1 1 308 716 546 +1237 2 2 1 1 111 474 772 +1238 2 2 1 1 412 808 670 +1239 2 2 1 1 164 499 759 +1240 2 2 1 1 22 713 490 +1241 2 2 1 1 88 694 89 +1242 2 2 1 1 82 469 755 +1243 2 2 1 1 335 612 695 +1244 2 2 1 1 280 836 628 +1245 2 2 1 1 77 795 78 +1246 2 2 1 1 353 525 785 +1247 2 2 1 1 333 712 473 +1248 2 2 1 1 306 472 788 +1249 2 2 1 1 135 456 711 +1250 2 2 1 1 194 834 477 +1251 2 2 1 1 21 533 621 +1252 2 2 1 1 367 738 850 +1253 2 2 1 1 179 756 180 +1254 2 2 1 1 329 588 666 +1255 2 2 1 1 354 674 655 +1256 2 2 1 1 338 842 660 +1257 2 2 1 1 278 670 808 +1258 2 2 1 1 459 770 731 +1259 2 2 1 1 320 626 843 +1260 2 2 1 1 274 668 528 +1261 2 2 1 1 209 816 586 +1262 2 2 1 1 326 476 475 +1263 2 2 1 1 179 510 756 +1264 2 2 1 1 375 842 678 +1265 2 2 1 1 343 566 630 +1266 2 2 1 1 42 625 219 +1267 2 2 1 1 126 479 663 +1268 2 2 1 1 24 644 165 +1269 2 2 1 1 180 181 667 +1270 2 2 1 1 6 502 577 +1271 2 2 1 1 217 218 841 +1272 2 2 1 1 168 693 169 +1273 2 2 1 1 372 836 697 +1274 2 2 1 1 21 621 658 +1275 2 2 1 1 447 515 806 +1276 2 2 1 1 86 680 511 +1277 2 2 1 1 319 851 556 +1278 2 2 1 1 351 634 665 +1279 2 2 1 1 175 583 831 +1280 2 2 1 1 31 651 113 +1281 2 2 1 1 178 817 179 +1282 2 2 1 1 84 774 85 +1283 2 2 1 1 345 759 499 +1284 2 2 1 1 14 710 222 +1285 2 2 1 1 362 732 743 +1286 2 2 1 1 180 667 512 +1287 2 2 1 1 330 830 480 +1288 2 2 1 1 417 831 583 +1289 2 2 1 1 212 657 213 +1290 2 2 1 1 179 817 510 +1291 2 2 1 1 337 498 684 +1292 2 2 1 1 269 746 521 +1293 2 2 1 1 201 461 786 +1294 2 2 1 1 385 760 492 +1295 2 2 1 1 345 696 544 +1296 2 2 1 1 177 806 515 +1297 2 2 1 1 371 601 620 +1298 2 2 1 1 350 620 601 +1299 2 2 1 1 312 686 540 +1300 2 2 1 1 646 792 845 +1301 2 2 1 1 315 555 747 +1302 2 2 1 1 298 421 693 +1303 2 2 1 1 94 539 684 +1304 2 2 1 1 178 179 818 +1305 2 2 1 1 178 554 679 +1306 2 2 1 1 105 645 530 +1307 2 2 1 1 196 197 709 +1308 2 2 1 1 322 814 606 +1309 2 2 1 1 103 586 752 +1310 2 2 1 1 117 118 662 +1311 2 2 1 1 302 487 791 +1312 2 2 1 1 92 93 799 +1313 2 2 1 1 87 787 88 +1314 2 2 1 1 331 736 481 +1315 2 2 1 1 128 489 753 +1316 2 2 1 1 1 46 811 +1317 2 2 1 1 106 785 525 +1318 2 2 1 1 163 164 759 +1319 2 2 1 1 355 815 496 +1320 2 2 1 1 81 554 829 +1321 2 2 1 1 10 184 701 +1322 2 2 1 1 210 211 747 +1323 2 2 1 1 404 656 530 +1324 2 2 1 1 357 765 757 +1325 2 2 1 1 273 544 696 +1326 2 2 1 1 10 701 124 +1327 2 2 1 1 289 599 838 +1328 2 2 1 1 272 847 508 +1329 2 2 1 1 274 780 492 +1330 2 2 1 1 345 499 696 +1331 2 2 1 1 361 648 754 +1332 2 2 1 1 319 579 851 +1333 2 2 1 1 134 135 711 +1334 2 2 1 1 93 634 539 +1335 2 2 1 1 78 79 782 +1336 2 2 1 1 1 811 161 +1337 2 2 1 1 341 767 474 +1338 2 2 1 1 85 680 86 +1339 2 2 1 1 273 696 754 +1340 2 2 1 1 335 791 487 +1341 2 2 1 1 390 754 648 +1342 2 2 1 1 214 789 215 +1343 2 2 1 1 430 557 739 +1344 2 2 1 1 27 110 848 +1345 2 2 1 1 217 841 519 +1346 2 2 1 1 82 177 679 +1347 2 2 1 1 346 534 735 +1348 2 2 1 1 319 556 681 +1349 2 2 1 1 208 209 748 +1350 2 2 1 1 81 679 554 +1351 2 2 1 1 205 764 206 +1352 2 2 1 1 5 719 495 +1353 2 2 1 1 328 670 478 +1354 2 2 1 1 364 646 845 +1355 2 2 1 1 203 204 715 +1356 2 2 1 1 297 757 495 +1357 2 2 1 1 330 526 830 +1358 2 2 1 1 214 724 424 +1359 2 2 1 1 303 494 800 +1360 2 2 1 1 340 536 615 +1361 2 2 1 1 441 628 836 +1362 2 2 1 1 6 167 677 +1363 2 2 1 1 315 639 555 +1364 2 2 1 1 353 521 746 +1365 2 2 1 1 184 518 701 +1366 2 2 1 1 9 83 685 +1367 2 2 1 1 268 733 541 +1368 2 2 1 1 312 540 663 +1369 2 2 1 1 419 435 805 +1370 2 2 1 1 339 528 668 +1371 2 2 1 1 42 219 724 +1372 2 2 1 1 358 633 562 +1373 2 2 1 1 309 707 567 +1374 2 2 1 1 90 548 687 +1375 2 2 1 1 390 800 754 +1376 2 2 1 1 9 643 553 +1377 2 2 1 1 205 532 764 +1378 2 2 1 1 11 492 760 +1379 2 2 1 1 384 630 566 +1380 2 2 1 1 344 643 685 +1381 2 2 1 1 188 851 189 +1382 2 2 1 1 333 519 841 +1383 2 2 1 1 213 657 527 +1384 2 2 1 1 61 62 778 +1385 2 2 1 1 78 742 79 +1386 2 2 1 1 343 820 610 +1387 2 2 1 1 311 474 767 +1388 2 2 1 1 99 588 708 +1389 2 2 1 1 361 499 804 +1390 2 2 1 1 195 852 598 +1391 2 2 1 1 369 783 768 +1392 2 2 1 1 127 128 753 +1393 2 2 1 1 330 725 526 +1394 2 2 1 1 25 835 613 +1395 2 2 1 1 303 825 535 +1396 2 2 1 1 203 763 204 +1397 2 2 1 1 11 760 87 +1398 2 2 1 1 178 818 554 +1399 2 2 1 1 72 516 821 +1400 2 2 1 1 8 738 224 +1401 2 2 1 1 401 690 603 +1402 2 2 1 1 368 506 727 +1403 2 2 1 1 313 563 728 +1404 2 2 1 1 28 672 647 +1405 2 2 1 1 357 757 713 +1406 2 2 1 1 268 630 721 +1407 2 2 1 1 380 629 683 +1408 2 2 1 1 330 518 725 +1409 2 2 1 1 5 765 75 +1410 2 2 1 1 291 669 846 +1411 2 2 1 1 17 704 590 +1412 2 2 1 1 305 803 537 +1413 2 2 1 1 105 785 106 +1414 2 2 1 1 368 510 817 +1415 2 2 1 1 137 138 775 +1416 2 2 1 1 363 629 777 +1417 2 2 1 1 319 664 579 +1418 2 2 1 1 301 528 762 +1419 2 2 1 1 220 221 761 +1420 2 2 1 1 366 537 803 +1421 2 2 1 1 52 53 849 +1422 2 2 1 1 30 162 744 +1423 2 2 1 1 279 557 832 +1424 2 2 1 1 357 546 765 +1425 2 2 1 1 336 621 533 +1426 2 2 1 1 380 777 629 +1427 2 2 1 1 98 814 99 +1428 2 2 1 1 371 631 594 +1429 2 2 1 1 32 837 187 +1430 2 2 1 1 342 816 520 +1431 2 2 1 1 411 538 692 +1432 2 2 1 1 22 192 827 +1433 2 2 1 1 176 647 672 +1434 2 2 1 1 71 72 821 +1435 2 2 1 1 14 581 710 +1436 2 2 1 1 341 486 767 +1437 2 2 1 1 332 698 550 +1438 2 2 1 1 34 749 219 +1439 2 2 1 1 269 535 746 +1440 2 2 1 1 268 549 820 +1441 2 2 1 1 75 765 546 +1442 2 2 1 1 17 41 705 +1443 2 2 1 1 210 747 555 +1444 2 2 1 1 160 161 828 +1445 2 2 1 1 355 658 621 +1446 2 2 1 1 174 175 831 +1447 2 2 1 1 221 839 222 +1448 2 2 1 1 148 149 809 +1449 2 2 1 1 348 585 619 +1450 2 2 1 1 372 508 847 +1451 2 2 1 1 211 511 747 +1452 2 2 1 1 187 837 540 +1453 2 2 1 1 324 718 657 +1454 2 2 1 1 22 576 713 +1455 2 2 1 1 114 794 460 +1456 2 2 1 1 354 764 532 +1457 2 2 1 1 339 762 528 +1458 2 2 1 1 170 790 171 +1459 2 2 1 1 134 711 545 +1460 2 2 1 1 353 825 525 +1461 2 2 1 1 27 848 195 +1462 2 2 1 1 55 547 853 +1463 2 2 1 1 334 569 640 +1464 2 2 1 1 171 790 723 +1465 2 2 1 1 265 739 599 +1466 2 2 1 1 303 525 825 +1467 2 2 1 1 160 828 524 +1468 2 2 1 1 25 569 835 +1469 2 2 1 1 194 852 195 +1470 2 2 1 1 425 524 828 +1471 2 2 1 1 219 625 624 +1472 2 2 1 1 3 55 853 +1473 2 2 1 1 326 709 565 +1474 2 2 1 1 328 573 715 +1475 2 2 1 1 100 826 555 +1476 2 2 1 1 208 520 816 +1477 2 2 1 1 31 113 823 +1478 2 2 1 1 198 592 714 +1479 2 2 1 1 28 562 633 +1480 2 2 1 1 340 615 702 +1481 2 2 1 1 298 844 485 +1482 2 2 1 1 174 831 542 +1483 2 2 1 1 449 846 669 +1484 2 2 1 1 110 833 111 +1485 2 2 1 1 421 805 470 +1486 2 2 1 1 350 567 775 +1487 2 2 1 1 377 672 543 +1488 2 2 1 1 17 794 41 +1489 2 2 1 1 359 739 557 +1490 2 2 1 1 168 769 693 +1491 2 2 1 1 25 40 812 +1492 2 2 1 1 382 758 742 +1493 2 2 1 1 137 775 567 +1494 2 2 1 1 301 762 570 +1495 2 2 1 1 420 840 652 +1496 2 2 1 1 210 555 826 +1497 2 2 1 1 345 622 759 +1498 2 2 1 1 395 616 660 +1499 2 2 1 1 357 713 576 +1500 2 2 1 1 93 587 799 +1501 2 2 1 1 339 668 726 +1502 2 2 1 1 326 731 476 +1503 2 2 1 1 118 729 662 +1504 2 2 1 1 188 556 851 +1505 2 2 1 1 341 714 592 +1506 2 2 1 1 334 835 569 +1507 2 2 1 1 329 708 588 +1508 2 2 1 1 337 710 581 +1509 2 2 1 1 442 797 577 +1510 2 2 1 1 79 742 758 +1511 2 2 1 1 92 799 659 +1512 2 2 1 1 224 738 623 +1513 2 2 1 1 268 820 630 +1514 2 2 1 1 333 564 712 +1515 2 2 1 1 222 839 594 +1516 2 2 1 1 208 748 610 +1517 2 2 1 1 324 617 718 +1518 2 2 1 1 31 823 619 +1519 2 2 1 1 166 435 637 +1520 2 2 1 1 392 691 607 +1521 2 2 1 1 317 558 755 +1522 2 2 1 1 333 841 597 +1523 2 2 1 1 349 476 832 +1524 2 2 1 1 166 564 568 +1525 2 2 1 1 371 594 839 +1526 2 2 1 1 359 599 739 +1527 2 2 1 1 305 552 803 +1528 2 2 1 1 203 715 573 +1529 2 2 1 1 384 721 630 +1530 2 2 1 1 441 836 847 +1531 2 2 1 1 359 838 599 +1532 2 2 1 1 424 517 789 +1533 2 2 1 1 190 556 819 +1534 2 2 1 1 91 561 812 +1535 2 2 1 1 221 601 839 +1536 2 2 1 1 213 568 564 +1537 2 2 1 1 365 554 818 +1538 2 2 1 1 367 734 623 +1539 2 2 1 1 365 829 554 +1540 2 2 1 1 79 661 782 +1541 2 2 1 1 175 485 844 +1542 2 2 1 1 125 540 837 +1543 2 2 1 1 350 810 504 +1544 2 2 1 1 371 839 601 +1545 2 2 1 1 214 424 789 +1546 2 2 1 1 387 613 835 +1547 2 2 1 1 34 675 749 +1548 2 2 1 1 414 671 776 +1549 2 2 1 1 411 693 769 +1550 2 2 1 1 342 586 816 +1551 2 2 1 1 367 623 738 +1552 2 2 1 1 145 853 547 +1553 2 2 1 1 535 825 746 +1554 2 2 1 1 210 480 830 +1555 2 2 1 1 209 614 748 +1556 2 2 1 1 9 685 643 +1557 2 2 1 1 385 787 760 +1558 2 2 1 1 348 771 573 +1559 2 2 1 1 342 752 586 +1560 2 2 1 1 61 778 691 +1561 2 2 1 1 203 582 763 +1562 2 2 1 1 188 819 556 +1563 2 2 1 1 358 623 734 +1564 2 2 1 1 99 814 551 +1565 2 2 1 1 188 603 690 +1566 2 2 1 1 362 619 823 +1567 2 2 1 1 163 759 622 +1568 2 2 1 1 401 603 807 +1569 2 2 1 1 338 660 616 +1570 2 2 1 1 397 807 603 +1571 2 2 1 1 336 778 632 +1572 2 2 1 1 209 586 750 +1573 2 2 1 1 219 749 724 +1574 2 2 1 1 313 745 563 +1575 2 2 1 1 169 693 421 +1576 2 2 1 1 343 610 748 +1577 2 2 1 1 87 760 787 +1578 2 2 1 1 194 700 852 +1579 2 2 1 1 338 616 793 +1580 2 2 1 1 363 662 729 +1581 2 2 1 1 169 421 470 +1582 2 2 1 1 354 655 764 +1583 2 2 1 1 138 620 775 +1584 2 2 1 1 350 775 620 +1585 2 2 1 1 372 847 836 +1586 2 2 1 1 206 764 655 +1587 2 2 1 1 271 635 843 +1588 2 2 1 1 339 726 722 +1589 2 2 1 1 271 843 626 +1590 2 2 1 1 111 833 665 +1591 2 2 1 1 327 561 703 +1592 2 2 1 1 450 722 726 +1593 2 2 1 1 370 822 798 +1594 2 2 1 1 422 798 822 +1595 2 2 1 1 176 692 538 +1596 2 2 1 1 356 792 646 +1597 2 2 1 1 369 808 783 +1598 2 2 1 1 412 783 808 +1599 2 2 1 1 17 705 704 +1600 2 2 1 1 103 750 586 +1601 2 2 1 1 356 646 802 +1602 2 2 1 1 98 606 814 +1603 2 2 1 1 351 665 833 +1604 2 2 1 1 161 811 828 +1605 2 2 1 1 350 601 810 +1606 2 2 1 1 425 828 811 +1607 2 2 1 1 189 851 579 +1608 2 2 1 1 352 652 840 +1609 2 2 1 1 165 644 804 +1610 2 2 1 1 361 804 644 +1611 2 2 1 1 343 630 820 +1612 2 2 1 1 355 632 815 +1613 2 2 1 1 279 770 813 +1614 2 2 1 1 347 782 661 +1615 2 2 1 1 53 629 849 +1616 2 2 1 1 166 463 435 +1617 2 2 1 1 416 852 700 +1618 2 2 1 1 338 793 751 +1619 2 2 1 1 347 659 799 +1620 2 2 1 1 220 761 717 +1621 2 2 1 1 277 802 646 +1622 2 2 1 1 363 849 629 +1623 2 2 1 1 149 773 809 +1624 2 2 1 1 169 527 718 +1625 2 2 1 1 351 717 761 +1626 2 2 1 1 383 843 635 +1627 2 2 1 1 170 758 790 +1628 2 2 1 1 62 632 778 +1629 2 2 1 1 218 597 841 +1630 2 2 1 1 52 849 729 +1631 2 2 1 1 349 776 671 +1632 2 2 1 1 392 809 773 +1633 2 2 1 1 110 717 833 +1634 2 2 1 1 351 833 717 +1635 2 2 1 1 113 732 823 +1636 2 2 1 1 403 813 770 +1637 2 2 1 1 282 660 842 +1638 2 2 1 1 279 832 731 +1639 2 2 1 1 476 731 832 +1640 2 2 1 1 362 823 732 +1641 2 2 1 1 363 729 849 +1642 2 2 1 1 353 746 825 +1643 2 2 1 1 298 692 844 +1644 2 2 1 1 352 793 624 +1645 2 2 1 1 382 790 758 +1646 2 2 1 1 424 749 675 +1647 2 2 1 1 360 590 704 +1648 2 2 1 1 352 751 793 +1649 2 2 1 1 352 840 751 +1650 2 2 1 1 278 845 792 +1651 2 2 1 1 275 751 840 +1652 2 2 1 1 207 704 705 +1653 2 2 1 1 35 463 166 +1654 2 2 1 1 35 169 470 +1655 2 2 1 1 40 613 193 +1656 2 2 1 1 40 196 500 +1657 2 2 1 1 40 649 196 +1658 2 2 1 1 41 741 200 +1659 2 2 1 1 41 207 705 +1660 2 2 1 1 42 724 214 +1661 2 2 1 1 43 647 176 +1662 2 2 1 1 43 223 562 +1663 2 2 1 1 26 463 35 +1664 2 2 1 1 26 35 470 +1665 2 2 1 1 26 470 805 +1666 2 2 2 2 1125 930 935 +1667 2 2 2 2 870 930 1125 +1668 2 2 2 2 249 955 250 +1669 2 2 2 2 249 1037 955 +1670 2 2 2 2 126 931 1021 +1671 2 2 2 2 227 917 970 +1672 2 2 2 2 227 1057 917 +1673 2 2 2 2 53 987 984 +1674 2 2 2 2 854 935 972 +1675 2 2 2 2 871 945 1028 +1676 2 2 2 2 915 1028 945 +1677 2 2 2 2 907 945 1011 +1678 2 2 2 2 125 126 1021 +1679 2 2 2 2 871 1011 945 +1680 2 2 2 2 900 1014 969 +1681 2 2 2 2 866 1058 919 +1682 2 2 2 2 933 1075 1014 +1683 2 2 2 2 52 53 984 +1684 2 2 2 2 866 919 1036 +1685 2 2 2 2 967 1012 1113 +1686 2 2 2 2 243 965 939 +1687 2 2 2 2 1004 920 855 +1688 2 2 2 2 863 1014 1075 +1689 2 2 2 2 858 930 947 +1690 2 2 2 2 855 920 978 +1691 2 2 2 2 235 990 924 +1692 2 2 2 2 892 933 1014 +1693 2 2 2 2 872 953 928 +1694 2 2 2 2 895 1033 1043 +1695 2 2 2 2 917 975 1035 +1696 2 2 2 2 897 1047 1036 +1697 2 2 2 2 868 1034 923 +1698 2 2 2 2 895 1043 944 +1699 2 2 2 2 935 1089 972 +1700 2 2 2 2 906 1012 967 +1701 2 2 2 2 873 950 1013 +1702 2 2 2 2 879 923 961 +1703 2 2 2 2 879 1041 924 +1704 2 2 2 2 918 976 1033 +1705 2 2 2 2 863 969 1014 +1706 2 2 2 2 867 1035 975 +1707 2 2 2 2 32 1021 995 +1708 2 2 2 2 897 948 1047 +1709 2 2 2 2 865 1033 976 +1710 2 2 2 2 879 994 923 +1711 2 2 2 2 868 923 994 +1712 2 2 2 2 242 965 243 +1713 2 2 2 2 882 1010 1079 +1714 2 2 2 2 904 1013 950 +1715 2 2 2 2 881 931 973 +1716 2 2 2 2 878 1098 1072 +1717 2 2 2 2 141 142 940 +1718 2 2 2 2 870 973 931 +1719 2 2 2 2 873 938 954 +1720 2 2 2 2 934 1128 946 +1721 2 2 2 2 921 1022 972 +1722 2 2 2 2 869 946 1128 +1723 2 2 2 2 878 1072 956 +1724 2 2 2 2 879 924 1048 +1725 2 2 2 2 875 1113 1012 +1726 2 2 2 2 240 951 241 +1727 2 2 2 2 854 972 1022 +1728 2 2 2 2 872 936 953 +1729 2 2 2 2 142 1056 940 +1730 2 2 2 2 862 1081 1002 +1731 2 2 2 2 235 924 1041 +1732 2 2 2 2 925 1003 860 +1733 2 2 2 2 1101 925 860 +1734 2 2 2 2 32 125 1021 +1735 2 2 2 2 241 951 1067 +1736 2 2 2 2 926 1017 131 +1737 2 2 2 2 1072 926 131 +1738 2 2 2 2 252 942 253 +1739 2 2 2 2 912 1079 1010 +1740 2 2 2 2 874 1117 978 +1741 2 2 2 2 858 992 930 +1742 2 2 2 2 883 929 962 +1743 2 2 2 2 868 933 1034 +1744 2 2 2 2 892 1034 933 +1745 2 2 2 2 875 939 1087 +1746 2 2 2 2 858 932 997 +1747 2 2 2 2 885 1015 1046 +1748 2 2 2 2 886 928 1007 +1749 2 2 2 2 872 928 1016 +1750 2 2 2 2 886 1095 928 +1751 2 2 2 2 874 978 1118 +1752 2 2 2 2 869 1074 1098 +1753 2 2 2 2 883 1018 929 +1754 2 2 2 2 887 987 1076 +1755 2 2 2 2 889 1104 939 +1756 2 2 2 2 136 137 937 +1757 2 2 2 2 857 967 1010 +1758 2 2 2 2 912 1010 967 +1759 2 2 2 2 939 965 1087 +1760 2 2 2 2 889 1038 1104 +1761 2 2 2 2 870 931 1066 +1762 2 2 2 2 870 1077 930 +1763 2 2 2 2 858 1019 932 +1764 2 2 2 2 963 1099 1008 +1765 2 2 2 2 905 933 1094 +1766 2 2 2 2 901 940 1056 +1767 2 2 2 2 128 129 1018 +1768 2 2 2 2 128 1018 996 +1769 2 2 2 2 909 1002 1081 +1770 2 2 2 2 884 1085 1025 +1771 2 2 2 2 253 942 1071 +1772 2 2 2 2 861 963 1008 +1773 2 2 2 2 905 1075 933 +1774 2 2 2 2 866 943 1058 +1775 2 2 2 2 881 1021 931 +1776 2 2 2 2 894 1000 1002 +1777 2 2 2 2 143 144 944 +1778 2 2 2 2 262 1061 966 +1779 2 2 2 2 254 948 255 +1780 2 2 2 2 922 1076 987 +1781 2 2 2 2 259 968 1060 +1782 2 2 2 2 869 1098 946 +1783 2 2 2 2 857 1010 934 +1784 2 2 2 2 875 1012 939 +1785 2 2 2 2 930 992 1089 +1786 2 2 2 2 889 939 1012 +1787 2 2 2 2 230 960 1059 +1788 2 2 2 2 248 938 1037 +1789 2 2 2 2 868 980 933 +1790 2 2 2 2 126 1005 931 +1791 2 2 2 2 45 1024 257 +1792 2 2 2 2 3 264 1023 +1793 2 2 2 2 869 962 1074 +1794 2 2 2 2 930 1089 935 +1795 2 2 2 2 234 990 235 +1796 2 2 2 2 875 969 1113 +1797 2 2 2 2 876 1009 1083 +1798 2 2 2 2 3 1023 145 +1799 2 2 2 2 873 1013 938 +1800 2 2 2 2 929 1074 962 +1801 2 2 2 2 875 1087 969 +1802 2 2 2 2 894 949 1000 +1803 2 2 2 2 888 938 1013 +1804 2 2 2 2 873 954 1049 +1805 2 2 2 2 135 979 986 +1806 2 2 2 2 900 951 1014 +1807 2 2 2 2 45 256 1024 +1808 2 2 2 2 44 225 1027 +1809 2 2 2 2 903 1071 942 +1810 2 2 2 2 880 1045 972 +1811 2 2 2 2 880 980 1045 +1812 2 2 2 2 44 1027 233 +1813 2 2 2 2 926 1072 1098 +1814 2 2 2 2 880 1089 992 +1815 2 2 2 2 943 1116 1004 +1816 2 2 2 2 907 1046 1015 +1817 2 2 2 2 879 961 1041 +1818 2 2 2 2 932 1122 997 +1819 2 2 2 2 901 1062 940 +1820 2 2 2 2 53 54 987 +1821 2 2 2 2 1116 920 1004 +1822 2 2 2 2 262 966 263 +1823 2 2 2 2 857 934 1032 +1824 2 2 2 2 872 940 1062 +1825 2 2 2 2 923 1034 952 +1826 2 2 2 2 258 968 259 +1827 2 2 2 2 226 227 970 +1828 2 2 2 2 882 1079 998 +1829 2 2 2 2 141 940 982 +1830 2 2 2 2 854 1105 935 +1831 2 2 2 2 230 231 960 +1832 2 2 2 2 910 1059 960 +1833 2 2 2 2 238 952 239 +1834 2 2 2 2 918 966 1061 +1835 2 2 2 2 856 988 950 +1836 2 2 2 2 908 986 1008 +1837 2 2 2 2 893 1008 986 +1838 2 2 2 2 904 950 988 +1839 2 2 2 2 887 984 987 +1840 2 2 2 2 910 1006 1009 +1841 2 2 2 2 919 1060 968 +1842 2 2 2 2 926 1098 1074 +1843 2 2 2 2 927 1083 1009 +1844 2 2 2 2 916 1025 1085 +1845 2 2 2 2 137 974 937 +1846 2 2 2 2 254 1071 948 +1847 2 2 2 2 900 1067 951 +1848 2 2 2 2 873 1112 950 +1849 2 2 2 2 908 1099 956 +1850 2 2 2 2 245 949 1052 +1851 2 2 2 2 136 937 979 +1852 2 2 2 2 872 1062 936 +1853 2 2 2 2 864 1009 1006 +1854 2 2 2 2 908 956 1080 +1855 2 2 2 2 881 973 1001 +1856 2 2 2 2 248 1037 249 +1857 2 2 2 2 925 978 1117 +1858 2 2 2 2 890 1050 993 +1859 2 2 2 2 862 964 1081 +1860 2 2 2 2 252 983 942 +1861 2 2 2 2 893 986 979 +1862 2 2 2 2 858 947 1019 +1863 2 2 2 2 941 1121 1048 +1864 2 2 2 2 902 1001 973 +1865 2 2 2 2 888 955 1037 +1866 2 2 2 2 933 980 1094 +1867 2 2 2 2 1121 879 1048 +1868 2 2 2 2 862 1042 1029 +1869 2 2 2 2 903 948 1071 +1870 2 2 2 2 866 1116 943 +1871 2 2 2 2 882 997 1122 +1872 2 2 2 2 132 133 956 +1873 2 2 2 2 143 944 1056 +1874 2 2 2 2 930 1077 947 +1875 2 2 2 2 901 1056 944 +1876 2 2 2 2 934 946 1032 +1877 2 2 2 2 915 945 993 +1878 2 2 2 2 245 1038 949 +1879 2 2 2 2 260 958 261 +1880 2 2 2 2 856 993 945 +1881 2 2 2 2 907 1015 945 +1882 2 2 2 2 243 939 1104 +1883 2 2 2 2 228 229 959 +1884 2 2 2 2 54 1102 987 +1885 2 2 2 2 880 972 1089 +1886 2 2 2 2 899 989 1003 +1887 2 2 2 2 869 1128 962 +1888 2 2 2 2 248 1100 938 +1889 2 2 2 2 915 993 1050 +1890 2 2 2 2 856 945 1015 +1891 2 2 2 2 862 1002 1000 +1892 2 2 2 2 864 1076 1119 +1893 2 2 2 2 899 955 989 +1894 2 2 2 2 245 1052 246 +1895 2 2 2 2 932 1019 962 +1896 2 2 2 2 856 950 1020 +1897 2 2 2 2 858 997 992 +1898 2 2 2 2 909 1020 950 +1899 2 2 2 2 894 954 1111 +1900 2 2 2 2 890 964 981 +1901 2 2 2 2 884 981 964 +1902 2 2 2 2 905 992 997 +1903 2 2 2 2 932 962 1128 +1904 2 2 2 2 903 942 1090 +1905 2 2 2 2 1124 883 1019 +1906 2 2 2 2 864 1119 1001 +1907 2 2 2 2 927 1114 1083 +1908 2 2 2 2 947 1124 1019 +1909 2 2 2 2 134 135 986 +1910 2 2 2 2 888 1037 938 +1911 2 2 2 2 236 961 237 +1912 2 2 2 2 909 1049 1002 +1913 2 2 2 2 137 138 974 +1914 2 2 2 2 239 952 1031 +1915 2 2 2 2 140 141 982 +1916 2 2 2 2 871 937 1078 +1917 2 2 2 2 871 1093 937 +1918 2 2 2 2 872 1068 940 +1919 2 2 2 2 135 136 979 +1920 2 2 2 2 886 971 977 +1921 2 2 2 2 885 977 971 +1922 2 2 2 2 928 953 1007 +1923 2 2 2 2 942 1044 1090 +1924 2 2 2 2 909 1112 1049 +1925 2 2 2 2 874 1090 1044 +1926 2 2 2 2 876 1096 1009 +1927 2 2 2 2 899 1003 1044 +1928 2 2 2 2 244 1038 245 +1929 2 2 2 2 894 1049 954 +1930 2 2 2 2 921 1121 1022 +1931 2 2 2 2 908 1008 1099 +1932 2 2 2 2 860 1003 989 +1933 2 2 2 2 889 1000 949 +1934 2 2 2 2 894 1111 1052 +1935 2 2 2 2 892 1031 952 +1936 2 2 2 2 866 1036 1047 +1937 2 2 2 2 878 956 1099 +1938 2 2 2 2 941 1022 1121 +1939 2 2 2 2 896 985 957 +1940 2 2 2 2 891 957 985 +1941 2 2 2 2 895 944 1064 +1942 2 2 2 2 882 998 997 +1943 2 2 2 2 905 997 998 +1944 2 2 2 2 144 1064 944 +1945 2 2 2 2 888 1013 1040 +1946 2 2 2 2 934 1010 1122 +1947 2 2 2 2 888 989 955 +1948 2 2 2 2 936 1106 1070 +1949 2 2 2 2 139 957 1091 +1950 2 2 2 2 923 952 1108 +1951 2 2 2 2 865 1043 1033 +1952 2 2 2 2 1003 1117 1044 +1953 2 2 2 2 238 1108 952 +1954 2 2 2 2 901 944 1043 +1955 2 2 2 2 860 971 1101 +1956 2 2 2 2 250 955 1051 +1957 2 2 2 2 133 1080 956 +1958 2 2 2 2 885 988 1015 +1959 2 2 2 2 946 1120 1032 +1960 2 2 2 2 251 983 252 +1961 2 2 2 2 229 1059 959 +1962 2 2 2 2 921 1045 994 +1963 2 2 2 2 916 1032 1120 +1964 2 2 2 2 936 1070 953 +1965 2 2 2 2 868 994 1045 +1966 2 2 2 2 897 1065 948 +1967 2 2 2 2 240 1031 951 +1968 2 2 2 2 138 139 1091 +1969 2 2 2 2 892 1014 951 +1970 2 2 2 2 261 958 1061 +1971 2 2 2 2 260 1060 958 +1972 2 2 2 2 910 960 1006 +1973 2 2 2 2 920 1118 978 +1974 2 2 2 2 255 948 1065 +1975 2 2 2 2 928 1095 1092 +1976 2 2 2 2 923 1108 961 +1977 2 2 2 2 855 1007 953 +1978 2 2 2 2 857 991 967 +1979 2 2 2 2 903 1047 948 +1980 2 2 2 2 906 967 991 +1981 2 2 2 2 906 1029 1042 +1982 2 2 2 2 235 1041 236 +1983 2 2 2 2 918 1033 966 +1984 2 2 2 2 889 949 1038 +1985 2 2 2 2 892 951 1031 +1986 2 2 2 2 131 132 1072 +1987 2 2 2 2 227 228 1057 +1988 2 2 2 2 914 1070 1106 +1989 2 2 2 2 917 1035 970 +1990 2 2 2 2 911 1022 1110 +1991 2 2 2 2 855 978 1053 +1992 2 2 2 2 884 991 1085 +1993 2 2 2 2 941 1110 1022 +1994 2 2 2 2 250 1051 251 +1995 2 2 2 2 237 961 1108 +1996 2 2 2 2 919 968 1036 +1997 2 2 2 2 2 1123 232 +1998 2 2 2 2 884 1029 991 +1999 2 2 2 2 894 1052 949 +2000 2 2 2 2 887 1006 960 +2001 2 2 2 2 906 991 1029 +2002 2 2 2 2 885 1030 988 +2003 2 2 2 2 912 1113 969 +2004 2 2 2 2 904 988 1030 +2005 2 2 2 2 892 952 1034 +2006 2 2 2 2 139 1054 957 +2007 2 2 2 2 126 127 1005 +2008 2 2 2 2 882 1122 1010 +2009 2 2 2 2 860 1030 971 +2010 2 2 2 2 927 1039 1114 +2011 2 2 2 2 864 1073 1009 +2012 2 2 2 2 862 1029 964 +2013 2 2 2 2 902 973 1125 +2014 2 2 2 2 885 1046 977 +2015 2 2 2 2 130 131 1017 +2016 2 2 2 2 909 950 1112 +2017 2 2 2 2 904 1040 1013 +2018 2 2 2 2 861 1025 963 +2019 2 2 2 2 855 1053 1007 +2020 2 2 2 2 884 1025 981 +2021 2 2 2 2 861 1063 1050 +2022 2 2 2 2 927 1009 1073 +2023 2 2 2 2 239 1031 240 +2024 2 2 2 2 928 1092 1016 +2025 2 2 2 2 902 1125 1105 +2026 2 2 2 2 139 140 1054 +2027 2 2 2 2 959 1059 1096 +2028 2 2 2 2 862 1000 1042 +2029 2 2 2 2 888 1040 989 +2030 2 2 2 2 127 128 1109 +2031 2 2 2 2 890 993 1020 +2032 2 2 2 2 883 962 1019 +2033 2 2 2 2 144 145 1064 +2034 2 2 2 2 883 996 1018 +2035 2 2 2 2 891 1091 957 +2036 2 2 2 2 899 1051 955 +2037 2 2 2 2 241 1067 242 +2038 2 2 2 2 861 981 1025 +2039 2 2 2 2 855 953 1070 +2040 2 2 2 2 242 1067 965 +2041 2 2 2 2 911 1083 1114 +2042 2 2 2 2 958 1103 1061 +2043 2 2 2 2 860 1040 1030 +2044 2 2 2 2 887 1076 1006 +2045 2 2 2 2 255 1065 256 +2046 2 2 2 2 231 232 1069 +2047 2 2 2 2 916 963 1025 +2048 2 2 2 2 938 1100 954 +2049 2 2 2 2 896 957 1054 +2050 2 2 2 2 886 977 1095 +2051 2 2 2 2 877 1088 976 +2052 2 2 2 2 954 1100 1111 +2053 2 2 2 2 247 1111 1100 +2054 2 2 2 2 884 964 1029 +2055 2 2 2 2 914 976 1088 +2056 2 2 2 2 887 960 1069 +2057 2 2 2 2 877 1058 1088 +2058 2 2 2 2 902 1039 1073 +2059 2 2 2 2 922 987 1102 +2060 2 2 2 2 943 1088 1058 +2061 2 2 2 2 876 1083 975 +2062 2 2 2 2 865 976 1106 +2063 2 2 2 2 867 975 1110 +2064 2 2 2 2 891 1078 974 +2065 2 2 2 2 885 971 1030 +2066 2 2 2 2 927 1073 1039 +2067 2 2 2 2 895 966 1033 +2068 2 2 2 2 859 1026 985 +2069 2 2 2 2 32 1102 54 +2070 2 2 2 2 891 985 1026 +2071 2 2 2 2 890 981 1050 +2072 2 2 2 2 132 956 1072 +2073 2 2 2 2 911 975 1083 +2074 2 2 2 2 856 1015 988 +2075 2 2 2 2 900 969 1087 +2076 2 2 2 2 897 1036 968 +2077 2 2 2 2 898 970 1035 +2078 2 2 2 2 263 1082 264 +2079 2 2 2 2 896 982 1068 +2080 2 2 2 2 942 983 1044 +2081 2 2 2 2 861 1008 1063 +2082 2 2 2 2 228 959 1057 +2083 2 2 2 2 236 1041 961 +2084 2 2 2 2 128 996 1109 +2085 2 2 2 2 867 1048 1055 +2086 2 2 2 2 257 1086 258 +2087 2 2 2 2 958 1060 1126 +2088 2 2 2 2 225 226 1084 +2089 2 2 2 2 856 1020 993 +2090 2 2 2 2 134 986 1080 +2091 2 2 2 2 908 1080 986 +2092 2 2 2 2 232 984 1069 +2093 2 2 2 2 898 1055 990 +2094 2 2 2 2 893 979 1093 +2095 2 2 2 2 876 959 1096 +2096 2 2 2 2 918 1103 976 +2097 2 2 2 2 876 1107 959 +2098 2 2 2 2 891 974 1091 +2099 2 2 2 2 877 976 1103 +2100 2 2 2 2 914 1004 1070 +2101 2 2 2 2 910 1009 1096 +2102 2 2 2 2 914 1106 976 +2103 2 2 2 2 855 1070 1004 +2104 2 2 2 2 903 1090 999 +2105 2 2 2 2 877 958 1126 +2106 2 2 2 2 913 1124 1077 +2107 2 2 2 2 32 995 1102 +2108 2 2 2 2 867 1110 1048 +2109 2 2 2 2 941 1048 1110 +2110 2 2 2 2 877 1103 958 +2111 2 2 2 2 921 972 1045 +2112 2 2 2 2 876 975 1107 +2113 2 2 2 2 859 1092 1095 +2114 2 2 2 2 891 1026 1011 +2115 2 2 2 2 142 143 1056 +2116 2 2 2 2 890 1020 1081 +2117 2 2 2 2 229 230 1059 +2118 2 2 2 2 878 963 1120 +2119 2 2 2 2 259 1060 260 +2120 2 2 2 2 261 1061 262 +2121 2 2 2 2 231 1069 960 +2122 2 2 2 2 916 1120 963 +2123 2 2 2 2 947 1077 1124 +2124 2 2 2 2 924 1055 1048 +2125 2 2 2 2 917 1107 975 +2126 2 2 2 2 891 1011 1078 +2127 2 2 2 2 859 1046 1026 +2128 2 2 2 2 253 1071 254 +2129 2 2 2 2 887 1069 984 +2130 2 2 2 2 900 965 1067 +2131 2 2 2 2 881 995 1021 +2132 2 2 2 2 138 1091 974 +2133 2 2 2 2 145 1023 1064 +2134 2 2 2 2 904 1030 1040 +2135 2 2 2 2 895 1082 966 +2136 2 2 2 2 912 969 1079 +2137 2 2 2 2 859 977 1046 +2138 2 2 2 2 863 1079 969 +2139 2 2 2 2 896 1068 1016 +2140 2 2 2 2 911 1110 975 +2141 2 2 2 2 915 1050 1063 +2142 2 2 2 2 907 1011 1026 +2143 2 2 2 2 868 1045 980 +2144 2 2 2 2 860 989 1040 +2145 2 2 2 2 896 1054 982 +2146 2 2 2 2 247 1100 248 +2147 2 2 2 2 897 968 1086 +2148 2 2 2 2 233 1097 234 +2149 2 2 2 2 133 134 1080 +2150 2 2 2 2 899 1044 983 +2151 2 2 2 2 898 1084 970 +2152 2 2 2 2 890 1081 964 +2153 2 2 2 2 256 1065 1024 +2154 2 2 2 2 866 1047 999 +2155 2 2 2 2 900 1087 965 +2156 2 2 2 2 263 966 1082 +2157 2 2 2 2 907 1026 1046 +2158 2 2 2 2 925 1053 978 +2159 2 2 2 2 922 995 1119 +2160 2 2 2 2 861 1050 981 +2161 2 2 2 2 903 999 1047 +2162 2 2 2 2 899 983 1051 +2163 2 2 2 2 129 130 1115 +2164 2 2 2 2 898 1035 1055 +2165 2 2 2 2 251 1051 983 +2166 2 2 2 2 896 1092 985 +2167 2 2 2 2 898 990 1097 +2168 2 2 2 2 258 1086 968 +2169 2 2 2 2 226 970 1084 +2170 2 2 2 2 140 982 1054 +2171 2 2 2 2 243 1104 244 +2172 2 2 2 2 878 1099 963 +2173 2 2 2 2 237 1108 238 +2174 2 2 2 2 246 1111 247 +2175 2 2 2 2 940 1068 982 +2176 2 2 2 2 914 1088 1004 +2177 2 2 2 2 943 1004 1088 +2178 2 2 2 2 893 1093 1028 +2179 2 2 2 2 999 1090 1118 +2180 2 2 2 2 52 984 1123 +2181 2 2 2 2 937 974 1078 +2182 2 2 2 2 889 1042 1000 +2183 2 2 2 2 130 1017 1115 +2184 2 2 2 2 886 1101 971 +2185 2 2 2 2 929 1115 1017 +2186 2 2 2 2 886 1007 1053 +2187 2 2 2 2 912 967 1113 +2188 2 2 2 2 924 990 1055 +2189 2 2 2 2 881 1119 995 +2190 2 2 2 2 2 52 1123 +2191 2 2 2 2 909 1081 1020 +2192 2 2 2 2 906 1042 1012 +2193 2 2 2 2 889 1012 1042 +2194 2 2 2 2 264 1082 1023 +2195 2 2 2 2 257 1024 1086 +2196 2 2 2 2 871 1078 1011 +2197 2 2 2 2 894 1002 1049 +2198 2 2 2 2 859 1095 977 +2199 2 2 2 2 931 1005 1066 +2200 2 2 2 2 225 1084 1027 +2201 2 2 2 2 880 1094 980 +2202 2 2 2 2 913 1066 1005 +2203 2 2 2 2 867 1055 1035 +2204 2 2 2 2 937 1093 979 +2205 2 2 2 2 896 1016 1092 +2206 2 2 2 2 905 998 1075 +2207 2 2 2 2 898 1097 1027 +2208 2 2 2 2 871 1028 1093 +2209 2 2 2 2 921 994 1121 +2210 2 2 2 2 879 1121 994 +2211 2 2 2 2 863 1075 998 +2212 2 2 2 2 893 1063 1008 +2213 2 2 2 2 870 1125 973 +2214 2 2 2 2 910 1096 1059 +2215 2 2 2 2 859 985 1092 +2216 2 2 2 2 864 1001 1073 +2217 2 2 2 2 857 1085 991 +2218 2 2 2 2 902 1073 1001 +2219 2 2 2 2 880 992 1094 +2220 2 2 2 2 863 998 1079 +2221 2 2 2 2 865 1106 1127 +2222 2 2 2 2 127 1109 1005 +2223 2 2 2 2 918 1061 1103 +2224 2 2 2 2 893 1028 1063 +2225 2 2 2 2 905 1094 992 +2226 2 2 2 2 872 1016 1068 +2227 2 2 2 2 866 999 1116 +2228 2 2 2 2 234 1097 990 +2229 2 2 2 2 920 1116 999 +2230 2 2 2 2 922 1119 1076 +2231 2 2 2 2 895 1023 1082 +2232 2 2 2 2 929 1017 1074 +2233 2 2 2 2 926 1074 1017 +2234 2 2 2 2 897 1086 1024 +2235 2 2 2 2 864 1006 1076 +2236 2 2 2 2 874 1044 1117 +2237 2 2 2 2 895 1064 1023 +2238 2 2 2 2 916 1085 1032 +2239 2 2 2 2 857 1032 1085 +2240 2 2 2 2 878 1120 1098 +2241 2 2 2 2 897 1024 1065 +2242 2 2 2 2 232 1123 984 +2243 2 2 2 2 946 1098 1120 +2244 2 2 2 2 922 1102 995 +2245 2 2 2 2 913 996 1124 +2246 2 2 2 2 883 1124 996 +2247 2 2 2 2 915 1063 1028 +2248 2 2 2 2 898 1027 1084 +2249 2 2 2 2 886 1053 1101 +2250 2 2 2 2 913 1005 1109 +2251 2 2 2 2 854 1039 1105 +2252 2 2 2 2 913 1109 996 +2253 2 2 2 2 920 999 1118 +2254 2 2 2 2 936 1127 1106 +2255 2 2 2 2 233 1027 1097 +2256 2 2 2 2 881 1001 1119 +2257 2 2 2 2 935 1105 1125 +2258 2 2 2 2 244 1104 1038 +2259 2 2 2 2 129 1115 1018 +2260 2 2 2 2 925 1117 1003 +2261 2 2 2 2 925 1101 1053 +2262 2 2 2 2 873 1049 1112 +2263 2 2 2 2 913 1077 1066 +2264 2 2 2 2 901 1043 1127 +2265 2 2 2 2 865 1127 1043 +2266 2 2 2 2 919 1126 1060 +2267 2 2 2 2 929 1018 1115 +2268 2 2 2 2 901 1127 1062 +2269 2 2 2 2 911 1114 1022 +2270 2 2 2 2 854 1022 1114 +2271 2 2 2 2 870 1066 1077 +2272 2 2 2 2 902 1105 1039 +2273 2 2 2 2 874 1118 1090 +2274 2 2 2 2 877 1126 1058 +2275 2 2 2 2 959 1107 1057 +2276 2 2 2 2 854 1114 1039 +2277 2 2 2 2 917 1057 1107 +2278 2 2 2 2 246 1052 1111 +2279 2 2 2 2 919 1058 1126 +2280 2 2 2 2 936 1062 1127 +2281 2 2 2 2 932 1128 1122 +2282 2 2 2 2 934 1122 1128 +$EndElements diff --git a/lecture/mm/fractures/matrixproblem.hh b/lecture/mm/fractures/matrixproblem.hh new file mode 100644 index 0000000000000000000000000000000000000000..02b7564ceb2653dfd629338f0b71c12696ca273e --- /dev/null +++ b/lecture/mm/fractures/matrixproblem.hh @@ -0,0 +1,336 @@ +// -*- 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 . * + *****************************************************************************/ +/*! + * \file + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The sub-problem for the matrix domain in the exercise on two-phase flow in fractured porous media. + */ +#ifndef DUMUX_COURSE_FRACTURESEXERCISE_MATRIX_PROBLEM_HH +#define DUMUX_COURSE_FRACTURESEXERCISE_MATRIX_PROBLEM_HH + +// we use alu grid for the discretization of the matrix domain +#include + +// we need this in this test in order to define the domain +// id of the fracture problem (see function interiorBoundaryTypes()) +#include + +// we want to simulate methan gas transport in a water-saturated medium +#include +#include +#include +#include +#include +#include + +// We are using the framework for models that consider coupling +// across the element facets of the bulk domain. This has some +// properties defined, which we have to inherit here. In this +// exercise we want to use a cell-centered finite volume scheme +// with tpfa. +#include + +// include the base problem and the model we inherit from +#include +#include + +// the spatial parameters (permeabilities, material parameters etc.) +#include "matrixspatialparams.hh" + +namespace Dumux { + +// forward declaration of the problem class +template class MatrixSubProblem; + +namespace Properties { +NEW_PROP_TAG(GridData); // forward declaration of property + +// create the type tag node for the matrix sub-problem +// We need to put the facet-coupling type tag after the physics-related type tag +// because it overwrites some of the properties inherited from "TwoP". This is +// necessary because we need e.g. a modified implementation of darcys law that +// evaluates the coupling conditions on faces that coincide with the fractures. +NEW_TYPE_TAG(MatrixProblemTypeTag, INHERITS_FROM(TwoP, CCTpfaFacetCouplingModel)); +// Set the grid type +SET_TYPE_PROP(MatrixProblemTypeTag, Grid, Dune::ALUGrid<2, 2, Dune::simplex, Dune::conforming>); +// Set the problem type +SET_TYPE_PROP(MatrixProblemTypeTag, Problem, MatrixSubProblem); +// set the spatial params +SET_TYPE_PROP(MatrixProblemTypeTag, + SpatialParams, + MatrixSpatialParams< typename GET_PROP_TYPE(TypeTag, FVGridGeometry), + typename GET_PROP_TYPE(TypeTag, Scalar) >); +// the fluid system +SET_PROP(MatrixProblemTypeTag, FluidSystem) +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using H2O = Dumux::Components::H2O; + using TabH2O = Dumux::Components::TabulatedComponent; + using CH4 = Dumux::Components::CH4; + + // turn components into gas/liquid phase + using LiquidPhase = Dumux::FluidSystems::OnePLiquid; + using GasPhase = Dumux::FluidSystems::OnePGas; +public: + using type = Dumux::FluidSystems::TwoPImmiscible; +}; +} // end namespace Properties + +/*! + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The sub-problem for the matrix domain in the exercise on two-phase flow in fractured porous media. + */ +template +class MatrixSubProblem : public PorousMediumFlowProblem +{ + using ParentType = PorousMediumFlowProblem; + + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using CouplingManager = typename GET_PROP_TYPE(TypeTag, CouplingManager); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + using PrimaryVariables = typename GridVariables::PrimaryVariables; + using Scalar = typename GridVariables::Scalar; + + using FVGridGeometry = typename GridVariables::GridGeometry; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVGridGeometry::SubControlVolume; + using SubControlVolumeFace = typename FVGridGeometry::SubControlVolumeFace; + using GridView = typename FVGridGeometry::GridView; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + // some indices for convenience + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + enum + { + pressureIdx = Indices::pressureIdx, + saturationIdx = Indices::saturationIdx, + contiH2OEqIdx = Indices::conti0EqIdx + }; + +public: + //! The constructor + MatrixSubProblem(std::shared_ptr fvGridGeometry, + std::shared_ptr spatialParams, + const std::string& paramGroup = "") + : ParentType(fvGridGeometry, spatialParams, paramGroup) + , time_(0.0) + , initialShaleSaturation_(getParamFromGroup(paramGroup, "Problem.InitialShaleMethaneSaturation")) + , injectionRate_(getParam("Problem.InjectionRate")) + , injectionPosition_(getParamFromGroup(paramGroup, "Problem.InjectionPosition")) + , injectionLength_(getParamFromGroup(paramGroup, "Problem.InjectionLength")) + , injectionDuration_(getParamFromGroup(paramGroup, "Problem.InjectionDuration")) + { + // initialize the fluid system, i.e. the tabulation + // of water properties. Use the default p/T ranges. + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + FluidSystem::init(); + } + + //! Specifies the type of boundary condition at a given position + BoundaryTypes boundaryTypesAtPos(const GlobalPosition& globalPos) const + { + BoundaryTypes values; + + values.setAllNeumann(); + + // use Dirichlet at the top boundary + if (globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - 1e-6) + values.setAllDirichlet(); + + return values; + } + + //! Specifies the type of interior boundary condition to be used on a sub-control volume face + BoundaryTypes interiorBoundaryTypes(const Element& element, const SubControlVolumeFace& scvf) const + { + BoundaryTypes values; + + // Here we set the type of condition to be used on faces that coincide + // with a fracture. If Neumann is specified, a flux continuity condition + // on the basis of the normal fracture permeability is evaluated. + values.setAllNeumann(); + return values; + } + + //! evaluates the Dirichlet boundary condition for a given position + PrimaryVariables dirichletAtPos(const GlobalPosition& globalPos) const + { + auto values = initialAtPos(globalPos); + + // use zero-saturation within the overburden + if (globalPos[1] > 35.0) + values[saturationIdx] = 0.0; + + return values; + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param globalPos The position of the integration point of the boundary segment. + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + NumEqVector neumannAtPos(const GlobalPosition &globalPos) const + { + auto values = NumEqVector(0.0); + + // within the injection region + if (globalPos[1] < 1e-6 + && globalPos[0] > injectionPosition_ + && globalPos[0] < injectionPosition_ + injectionLength_ + && time_ < injectionDuration_ + 1e-6) + values[contiH2OEqIdx] = injectionRate_; + + return values; + } + + + //! evaluate the initial conditions + PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const + { + // For the grid used here, the height of the domain is equal + // to the maximum y-coordinate + const auto domainHeight = this->fvGridGeometry().bBoxMax()[1]; + + // we assume a constant water density of 1000 for initial conditions! + const auto& g = this->gravityAtPos(globalPos); + PrimaryVariables values; + Scalar densityW = 1000.0; + values[pressureIdx] = 1e5 - (domainHeight - globalPos[1])*densityW*g[1]; + + // in the overburden there is no gas initially + if (globalPos[1] > 35.0) + values[saturationIdx] = 0.0; + + // the shale formation contains gas initially + else + values[saturationIdx] = initialShaleSaturation_; + + return values; + } + + //! computes the gas flux from the reservoir into the overburden + template + Scalar computeGasFluxToOverburden(const GridVariables& gridVars, + const SolutionVector& sol) const + { + Scalar flux = 0.0; + for (const auto& element : elements(this->fvGridGeometry().gridView())) + { + // only proceed if elem could potentially be on the interface to overburden + const auto geometry = element.geometry(); + if (geometry.center()[1] < 35.0 && geometry.center()[1] > 15.0) + { + auto fvGeometry = localView(this->fvGridGeometry()); + fvGeometry.bind(element); + + auto elemVolVars = localView(gridVars.curGridVolVars()); + elemVolVars.bind(element, fvGeometry, sol); + + auto elemFluxVarsCache = localView(gridVars.gridFluxVarsCache()); + elemFluxVarsCache.bind(element, fvGeometry, elemVolVars); + + for (const auto& scvf : scvfs(fvGeometry)) + { + if (scvf.boundary()) + continue; + + const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); + const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); + + const bool isOnInterface = insideScv.center()[1] < 35.0 && outsideScv.center()[1] > 35.0; + if (isOnInterface) + { + using FluxVariables = typename GET_PROP_TYPE(TypeTag, FluxVariables); + FluxVariables fluxVars; + fluxVars.init(*this, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache); + + // mass upwind term + const auto gasPhaseIdx = GET_PROP_TYPE(TypeTag, FluidSystem)::comp1Idx; + auto upwindTerm = [gasPhaseIdx] (const auto& volVars) + { return volVars.mobility(gasPhaseIdx)*volVars.density(gasPhaseIdx); }; + + flux += fluxVars.advectiveFlux(gasPhaseIdx, upwindTerm); + } + } + } + } + + return flux; + } + + //! computes the influx of water into the domain + Scalar computeInjectionFlux() const + { + Scalar flux = 0.0; + for (const auto& element : elements(this->fvGridGeometry().gridView())) + { + auto fvGeometry = localView(this->fvGridGeometry()); + fvGeometry.bind(element); + + for (const auto& scvf : scvfs(fvGeometry)) + if (scvf.boundary()) + flux -= neumannAtPos(scvf.ipGlobal())[contiH2OEqIdx]*scvf.area(); + } + + return flux; + } + + //! returns the temperature in \f$\mathrm{[K]}\f$ in the domain + Scalar temperature() const + { return 283.15; /*10°*/ } + + //! sets the pointer to the coupling manager. + void setCouplingManager(std::shared_ptr cm) + { couplingManagerPtr_ = cm; } + + //! returns reference to the coupling manager. + const CouplingManager& couplingManager() const + { return *couplingManagerPtr_; } + + //! sets the current time + void setTime(Scalar time) + { time_ = time; } + +private: + std::shared_ptr couplingManagerPtr_; + + Scalar time_; + Scalar initialShaleSaturation_; + + // injection specifics + Scalar injectionRate_; + Scalar injectionPosition_; + Scalar injectionLength_; + Scalar injectionDuration_; +}; + +} // end namespace Dumux + +#endif diff --git a/lecture/mm/fractures/matrixspatialparams.hh b/lecture/mm/fractures/matrixspatialparams.hh new file mode 100644 index 0000000000000000000000000000000000000000..1f786110ba98f968a30421476b7f3bd461dc9971 --- /dev/null +++ b/lecture/mm/fractures/matrixspatialparams.hh @@ -0,0 +1,119 @@ +// -*- 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 . * + *****************************************************************************/ +/*! + * \file + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The spatial parameters for the fracture sub-domain in the exercise + * on two-phase flow in fractured porous media. + */ +#ifndef DUMUX_COURSE_FRACTURESEXERCISE_MATRIX_SPATIALPARAMS_HH +#define DUMUX_COURSE_FRACTURESEXERCISE_MATRIX_SPATIALPARAMS_HH + +#include +#include +#include + +namespace Dumux { + +/*! + * \ingroup MultiDomain + * \ingroup MultiDomainFacet + * \ingroup TwoPTests + * \brief The spatial params the two-phase facet coupling test + */ +template< class FVGridGeometry, class Scalar > +class MatrixSpatialParams +: public FVSpatialParams< FVGridGeometry, Scalar, MatrixSpatialParams > +{ + using ThisType = MatrixSpatialParams< FVGridGeometry, Scalar >; + using ParentType = FVSpatialParams< FVGridGeometry, Scalar, ThisType >; + + using GridView = typename FVGridGeometry::GridView; + using Grid = typename GridView::Grid; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + // use a regularized van-genuchten material law + using EffectiveLaw = RegularizedVanGenuchten; + +public: + //! export the type used for permeabilities + using PermeabilityType = Scalar; + + //! export the material law and parameters used + using MaterialLaw = EffToAbsLaw< EffectiveLaw >; + using MaterialLawParams = typename MaterialLaw::Params; + + //! the constructor + MatrixSpatialParams(std::shared_ptr fvGridGeometry, + const std::string& paramGroup) + : ParentType(fvGridGeometry) + { + porosity_ = getParamFromGroup(paramGroup, "SpatialParams.Porosity"); + porosityOverburden_ = getParamFromGroup(paramGroup, "SpatialParams.OverburdenPorosity"); + permeability_ = getParamFromGroup(paramGroup, "SpatialParams.Permeability"); + permeabilityOverburden_ = getParamFromGroup(paramGroup, "SpatialParams.OverburdenPermeability"); + + // set the material law parameters + materialLawParams_.setSnr(getParamFromGroup(paramGroup, "SpatialParams.Snr")); + materialLawParams_.setSwr(getParamFromGroup(paramGroup, "SpatialParams.Swr")); + materialLawParams_.setVgAlpha(getParamFromGroup(paramGroup, "SpatialParams.VGAlpha")); + materialLawParams_.setVgn(getParamFromGroup(paramGroup, "SpatialParams.VGN")); + + materialLawParamsOverburden_.setSnr(getParamFromGroup(paramGroup, "SpatialParams.OverburdenSnr")); + materialLawParamsOverburden_.setSwr(getParamFromGroup(paramGroup, "SpatialParams.OverburdenSwr")); + materialLawParamsOverburden_.setVgAlpha(getParamFromGroup(paramGroup, "SpatialParams.OverburdenVGAlpha")); + materialLawParamsOverburden_.setVgn(getParamFromGroup(paramGroup, "SpatialParams.OverburdenVGN")); + } + + //! Function for defining the (intrinsic) permeability \f$[m^2]\f$. + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const + { return globalPos[1] > 35.0 ? permeabilityOverburden_ : permeability_; } + + //! Return the porosity + Scalar porosityAtPos(const GlobalPosition& globalPos) const + { return globalPos[1] > 35.0 ? porosityOverburden_ : porosity_; } + + //! Return the material law parameters + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const + { return globalPos[1] > 35.0 ? materialLawParamsOverburden_ : materialLawParams_; } + + //! Water is the wetting phase + template< class FluidSystem > + int wettingPhaseAtPos(const GlobalPosition& globalPos) const + { + // we set water as the wetting phase here + // which is phase0Idx in the H2oN2 fluid system + return FluidSystem::phase0Idx; + } + +private: + Scalar porosity_; + Scalar porosityOverburden_; + PermeabilityType permeability_; + PermeabilityType permeabilityOverburden_; + MaterialLawParams materialLawParams_; + MaterialLawParams materialLawParamsOverburden_; +}; + +} // end namespace Dumux + +#endif diff --git a/lecture/mm/fuelcell/fuelcell.cc b/lecture/mm/fuelcell/fuelcell.cc index efa1044b8d2055fb7fc9ba54055f2fe11a529dd9..0054fca366495b5e2af5be64ca818efc98866094 100644 --- a/lecture/mm/fuelcell/fuelcell.cc +++ b/lecture/mm/fuelcell/fuelcell.cc @@ -24,9 +24,25 @@ * * The 2pncMin box model used for water management in PEM fuel cells adapted to the Acosta(paper 2006) problem. */ -#include "config.h" +#include #include -#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include //O: NON-ISOTHERMAL MODEL, 1: ISOTHERMAL MODEL #define ISOTHERMAL 0 @@ -66,8 +82,159 @@ void usage(const char *progName, const std::string &errorMsg) << "\n"; } } +//////////////////////// +// the main function +//////////////////////// +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(FuelCellLectureProblemTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) // Fehlt ebenso + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + problem->addVtkFields(vtkWriter); //!< Add problem specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // update the output fields with the current solution before write + problem->updateVtkFields(x); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } +} -int main(int argc, char** argv) +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/fuelcell/fuelcell.input b/lecture/mm/fuelcell/fuelcell.input index f9758de0f29289a4d8a721d737d7144879d26103..24acda34b0d398723ae495c4a384a409b07d9fbd 100644 --- a/lecture/mm/fuelcell/fuelcell.input +++ b/lecture/mm/fuelcell/fuelcell.input @@ -1,4 +1,4 @@ -[TimeManager] +[TimeLoop] DtInitial = 1e-2 # small because of Acosta pc law MaxTimeStepSize = 20 TEnd = 20 #equilibrium time diff --git a/lecture/mm/fuelcell/fuelcellproblem.hh b/lecture/mm/fuelcell/fuelcellproblem.hh index 4ad3837c3036e64356040a5e76aaeae24cc6536f..e4457c2cb73ace3ed64ef3d9bc9d7c4992cf7427 100644 --- a/lecture/mm/fuelcell/fuelcellproblem.hh +++ b/lecture/mm/fuelcell/fuelcellproblem.hh @@ -24,10 +24,13 @@ #ifndef DUMUX_FUELCELL_LECTURE_PROBLEM_HH #define DUMUX_FUELCELL_LECTURE_PROBLEM_HH -#include -#include +#include + +#include +#include #include #include +#include #if !ISOTHERMAL #include @@ -37,45 +40,44 @@ #include "fuelcellspatialparams.hh" -namespace Dumux -{ +namespace Dumux { template class FuelCellLectureProblem; -namespace Properties -{ +namespace Properties { #if ISOTHERMAL -NEW_TYPE_TAG(FuelCellLectureProblem, INHERITS_FROM(BoxTwoPNC, FuelCellLectureSpatialParams)); +NEW_TYPE_TAG(FuelCellLectureProblemTypeTag, INHERITS_FROM(TwoPNC, BoxModel, FuelCellLectureSpatialParamsTypeTag)); #else -NEW_TYPE_TAG(FuelCellLectureProblem, INHERITS_FROM(BoxTwoPNCNI, FuelCellLectureSpatialParams)); +NEW_TYPE_TAG(FuelCellLectureProblemTypeTag, INHERITS_FROM(TwoPNCNI, BoxModel, FuelCellLectureSpatialParamsTypeTag)); #endif // Set the grid type -SET_TYPE_PROP(FuelCellLectureProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(FuelCellLectureProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(FuelCellLectureProblem, Problem, FuelCellLectureProblem); +SET_TYPE_PROP(FuelCellLectureProblemTypeTag, Problem, FuelCellLectureProblem); // Set the primary variable combination for the 2pnc model -SET_INT_PROP(FuelCellLectureProblem, Formulation, TwoPNCFormulation::pnsw); +SET_PROP(FuelCellLectureProblemTypeTag, Formulation) +{ + static constexpr auto value = TwoPFormulation::p1s0; +}; // Set fluid configuration -SET_PROP(FuelCellLectureProblem, FluidSystem) -{ private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - static const bool useComplexRelations = true; - public: - typedef FluidSystems::H2ON2O2 type; +SET_PROP(FuelCellLectureProblemTypeTag, FluidSystem) +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); +public: + using type = FluidSystems::H2ON2O2; }; //define which component balance equation gets replaced by total balance {0,1,2,..,numComponents(none)} -SET_INT_PROP(FuelCellLectureProblem, ReplaceCompEqIdx, 3); +SET_INT_PROP(FuelCellLectureProblemTypeTag, ReplaceCompEqIdx, 3); -// Disable gravity -SET_BOOL_PROP(FuelCellLectureProblem, ProblemEnableGravity, false); -} +} // end namespace Properties /*! * \ingroup TwoPNCModel @@ -85,95 +87,77 @@ SET_BOOL_PROP(FuelCellLectureProblem, ProblemEnableGravity, false); * ./test_fuelcell -parameterFile ./test_fuelcell.input */ template -class FuelCellLectureProblem : public ImplicitPorousMediaProblem +class FuelCellLectureProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - - enum { dim = GridView::dimension }; - enum { dimWorld = GridView::dimensionworld }; - enum { numComponents = FluidSystem::numComponents }; - enum { numSecComponents = FluidSystem::numSecComponents }; - + using ParentType = PorousMediumFlowProblem; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionWorld; + static constexpr int numComponents = GET_PROP_TYPE(TypeTag, ModelTraits)::numComponents(); // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx - }; - enum { - wCompIdx = FluidSystem::wCompIdx, //major component of the liquid phase - nCompIdx = FluidSystem::nCompIdx //major component of the gas phase - }; - enum { + static constexpr int wPhaseIdx = Indices::Phase0Idx; + static constexpr int nPhaseIdx = Indices::Phase1Idx; + static constexpr int wCompIdx = FluidSystem::Comp0Idx; //major component of the liquid phase + static constexpr int nCompIdx = FluidSystem::Comp1Idx; //major component of the gas phase #if !ISOTHERMAL - temperatureIdx = Indices::temperatureIdx, //temperature + static constexpr int temperatureIdx = Indices::temperatureIdx; //temperature #endif - pressureIdx = Indices::pressureIdx, //gas-phase pressure - switchIdx = Indices::switchIdx //liquid saturation or mole fraction - }; - enum { + static constexpr int pressureIdx = Indices::pressureIdx; //gas-phase pressure + static constexpr int switchIdx = Indices::switchIdx; //liquid saturation or mole fraction #if !ISOTHERMAL - energyEqIdx = Indices::energyEqIdx, //energy equation + static constexpr int energyEqIdx = Indices::energyEqIdx; //energy equation #endif - conti0EqIdx = Indices::conti0EqIdx - }; - - 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 GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; - - typedef Dune::FieldVector GlobalPosition; + static constexpr int conti0EqIdx = Indices::conti0EqIdx; + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using Vertex = typename GridView::template Codim::Entity; +// TODO: Why is here no usage of this keyword? Is it correct this way? + using Intersection = typename GridView::Intersection; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); #if !ISOTHERMAL - typedef typename Dumux::ElectroChemistryNI ElectroChemistry; + using ElectroChemistry = typename Dumux::ElectroChemistryNI; #else - typedef typename Dumux::ElectroChemistry ElectroChemistry; + using ElectroChemistry = typename Dumux::ElectroChemistry; #endif - typedef Dumux::Constants Constant; - - enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; - enum { dofCodim = isBox ? dim : 0 }; + using Constant = Dumux::Constants; + static constexpr bool isBox = FVGridGeometry::discMethod == DiscretizationMethod::box; + static constexpr int dofCodim = isBox ? dim : 0; public: /*! * \brief The constructor * - * \param timeManager The time manager - * \param gridView The grid view + * \param fvGridGeometry Contains the Finite-Volume-Grid-Geometry */ - FuelCellLectureProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView) + FuelCellLectureProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - nTemperature_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.NTemperature); - nPressure_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.NPressure); - pressureLow_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.PressureLow); - pressureHigh_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.PressureHigh); - temperatureLow_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.TemperatureLow); - temperatureHigh_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.TemperatureHigh); - temperature_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.InitialTemperature); - name_ = GET_RUNTIME_PARAM(TypeTag, std::string, Problem.Name); - - pO2Inlet_ = GET_RUNTIME_PARAM(TypeTag, Scalar, ElectroChemistry.pO2Inlet); - pgInlet1_ = GET_RUNTIME_PARAM(TypeTag, Scalar, OperationalConditions.GasPressureInlet1); - pgInlet2_ = GET_RUNTIME_PARAM(TypeTag, Scalar, OperationalConditions.GasPressureInlet2); - swInlet_ = GET_RUNTIME_PARAM(TypeTag, Scalar, OperationalConditions.LiquidWaterSaturationInlet); + nTemperature_ = getParam("Problem.NTemperature"); + nPressure_ = getParam("Problem.NPressure"); + pressureLow_ = getParam("Problem.PressureLow"); + pressureHigh_ = getParam("Problem.PressureHigh"); + temperatureLow_ = getParam("Problem.TemperatureLow"); + temperatureHigh_ = getParam("Problem.TemperatureHigh"); + temperature_ = getParam("Problem.InitialTemperature"); + name_ = getParam("Problem.Name"); + + pO2Inlet_ = getParam("ElectroChemistry.pO2Inlet"); + pgInlet1_ = getParam("OperationalConditions.GasPressureInlet1"); + pgInlet2_ = getParam("OperationalConditions.GasPressureInlet2"); + swInlet_ = getParam("OperationalConditions.LiquidWaterSaturationInlet"); eps_ = 1e-6; - - // initialize the tables of the fluid system - //FluidSystem::init(); FluidSystem::init(/*Tmin=*/temperatureLow_, /*Tmax=*/temperatureHigh_, /*nT=*/nTemperature_, @@ -181,7 +165,13 @@ public: /*pmax=*/pressureHigh_, /*np=*/nPressure_); - + const auto& gridView = this->fvGridGeometry().gridView(); + currentDensity_.resize(gridView.size(dofCodim)); + reactionSourceH2O_.resize(gridView.size(dofCodim)); + reactionSourceO2_.resize(gridView.size(dofCodim)); +#if !ISOTHERMAL + reactionSourceTemp_.resize(gridView.size(dofCodim)); +#endif } /*! @@ -195,7 +185,9 @@ public: * This is used as a prefix for files generated by the simulation. */ const std::string name() const - { return name_; } + { + return name_; + } /*! * \brief Returns the temperature within the domain. @@ -203,30 +195,37 @@ public: * This problem assumes a temperature of 10 degrees Celsius. */ Scalar temperature() const - { return temperature_; } + { + return temperature_; + } - //! \copydoc Dumux::ImplicitProblem::solDependentSource() - void solDependentSource(PrimaryVariables &values, const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx, const ElementVolumeVariables &elemVolVars) const + //! \copydoc Dumux::FVProblem::source() + NumEqVector source(const Element &element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolume &scv) const { + NumEqVector values; - const GlobalPosition globalPos = element.geometry().corner(scvIdx); - const VolumeVariables& volVars = elemVolVars[scvIdx]; + const GlobalPosition globalPos = scv.dofPosition(); + const VolumeVariables& volVars = elemVolVars[scv]; values = 0.0; //reaction sources from electro chemistry - Scalar reactionLayerWidth = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.ReactionLayerWidth); + Scalar reactionLayerWidth = getParam("Problem.ReactionLayerWidth"); - if(globalPos[0] < reactionLayerWidth + eps_) + if (globalPos[0] < reactionLayerWidth + eps_) { // Hack: division by two to get to the (wrongly implemented) current density // of the Acosta paper fuelcell model auto currentDensity = ElectroChemistry::calculateCurrentDensity(volVars)/2; - ElectroChemistry::reactionSource(values, currentDensity); + ElectroChemistry::reactionSource(values, currentDensity, ""); } + + return values; } + // \} /*! @@ -236,89 +235,75 @@ public: /*! * \brief Specifies which kind of boundary condition should be - * used for which equation on a given boundary segment. + * 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 + * \param globalPos The global position */ - void boundaryTypes(BoundaryTypes &values, const Vertex &vertex) const + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition globalPos = vertex.geometry().center(); + BoundaryTypes values; + values.setAllNeumann(); - if((globalPos[1]>=0 && globalPos[1]<=0.0005 && globalPos[0] > 0.0005 - eps_) + if ((globalPos[1]>=0 && globalPos[1]<=0.0005 && globalPos[0] > 0.0005 - eps_) || (globalPos[1]<=0.002 && globalPos[1]>=0.0015 && globalPos[0] > 0.0005 - eps_)) { values.setAllDirichlet(); } + + return values; } /*! - * \brief Evaluate the boundary conditions for a dirichlet - * boundary segment. - * - * \param values The dirichlet values for the primary variables - * \param vertex The vertex for which the boundary type is set + * \brief Evaluates the boundary conditions for a Dirichlet + * boundary segment * - * For this method, the \a values parameter stores primary variables. + * \param globalPos The global position */ - void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition globalPos = vertex.geometry().center(); - initial_(values, globalPos); + PrimaryVariables values = initial_(globalPos); //lower inlet - if((globalPos[1] >= 0 && globalPos[1] <= 0.0005 && globalPos[0] > 0.0005 - eps_)) + if ((globalPos[1] >= 0 && globalPos[1] <= 0.0005 && globalPos[0] > 0.0005 - eps_)) { values[pressureIdx] = pgInlet2_; values[switchIdx] = swInlet_; //Acosta Inlet liquid water saturation values[switchIdx+1] = pO2Inlet_/Dumux::BinaryCoeff::H2O_O2::henry(temperature_);; } //upper inlet - if((globalPos[1] <= 0.002 && globalPos[1] >= 0.0015 && globalPos[0] > 0.0005 - eps_)) + if ((globalPos[1] <= 0.002 && globalPos[1] >= 0.0015 && globalPos[0] > 0.0005 - eps_)) { values[pressureIdx] = pgInlet1_; values[switchIdx] = swInlet_; //Acosta Inlet liquid water saturation values[switchIdx+1] = pO2Inlet_/Dumux::BinaryCoeff::H2O_O2::henry(temperature_); } - #if !ISOTHERMAL values[temperatureIdx] = 343.15; #endif + return values; } - /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * \param values The neumann values for the conservation equations - * \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 values Stores the Neumann values for the conservation equations in + * \f$ [ \textnormal{unit of conserved quantity} / (m^(dim-1) \cdot s )] \f$ + * \param globalPos The global position * - * For this method, the \a values parameter stores the mass flux - * in normal direction of each phase. Negative values mean influx. + * The \a values store the mass flux of each phase normal to the boundary. + * Negative values indicate an inflow. */ - void neumann(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvGeometry, - const Intersection &is, - int scvIdx, - int boundaryFaceIdx) const + NumEqVector neumannAtPos(const GlobalPosition &globalPos) const { - - const GlobalPosition globalPos = element.geometry().corner(scvIdx); - - values = 0.0; - + PrimaryVariables values(0.0); #if !ISOTHERMAL if (globalPos[1] >= 0.0005 && globalPos[1] <= 0.0015 && globalPos[0] > 0.0005 - eps_) values[energyEqIdx] = 9800; // [W/m^2]; heat conduction flow thorough the shoulder;from Acosta-paper: lambda_shoulder/delta_shoulder*DeltaT #endif + return values; } // \} @@ -328,142 +313,80 @@ public: // \{ /*! - * \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, - int scvIdx) const - { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - - initial_(values, globalPos); - } - - /*! - * \brief Return the initial phase state inside a control volume. + * \brief Evaluates the initial values for a control volume * - * \param vertex The vertex - * \param globalIdx The index of the global vertex * \param globalPos The global position */ - - /* - *if bothPhases, set initial values for pg (pressureIdx), Sl (switchIdx), xO2l [mol/m3] (switchIdx+1) - *if nPhaseOnly, set initial values for pg (pressureIdx), xH2Og (switchIdx), xO2g [mol/m3] (switchIdx+1) - *if wPhaseOnly, set initial values for pg (pressureIdx), xN2l (switchIdx), xO2l [mol/m3] (switchIdx+1) - */ - - int initialPhasePresence(const Element& element, - const FVElementGeometry& fvGeometry, - unsigned int scvIdx) const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - return Indices::bothPhases; + return initial_(globalPos); } /*! - * \brief Returns true if a VTK output file should be written. + * \brief Adds additional VTK output data to the VTKWriter. Function is called by the output module on every write. */ - bool shouldWriteOutput() const + template + void addVtkFields(VTKWriter& vtk) { - const int freqOutput = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, FreqOutput); - if (this->timeManager().timeStepIndex() % freqOutput == 0 - || this->timeManager().episodeWillBeFinished()) - return true; - return false; + vtk.addField(currentDensity_, "currentDensity [A/cm^2]"); + vtk.addField(reactionSourceH2O_, "reactionSourceH2O [mol/(sm^2)]"); + vtk.addField(reactionSourceO2_, "reactionSourceO2 [mol/(sm^2)]"); +#if !ISOTHERMAL + vtk.addField(reactionSourceTemp_, "reactionSource Heat [J/(sm^2)]"); +#endif } - /*! - * \brief Add problem specific vtk output for the electrochemistry - */ - void addOutputVtkFields() + void updateVtkFields(const SolutionVector& curSol) { - // add the output field specific to the electrochemistry - typedef Dune::BlockVector > ScalarField; - - // get the number of degrees of freedom - unsigned numDofs = this->model().numDofs(); - - // create the required scalar fields - ScalarField *currentDensity = this->resultWriter().allocateManagedBuffer (numDofs); - ScalarField *reactionSourceH2O = this->resultWriter().allocateManagedBuffer (numDofs); - ScalarField *reactionSourceO2 = this->resultWriter().allocateManagedBuffer (numDofs); -#if !ISOTHERMAL - ScalarField *reactionSourceTemp = this->resultWriter().allocateManagedBuffer (numDofs); -#endif - for (const auto& element : elements(this->gridView())) + for (const auto& element : elements(this->fvGridGeometry().gridView())) { - FVElementGeometry fvGeometry; - fvGeometry.update(this->gridView(), element); + auto elemSol = elementSolution(element, curSol, this->fvGridGeometry()); - ElementVolumeVariables elemVolVars; - elemVolVars.update(*this, - element, - fvGeometry, - false /* oldSol? */); + auto fvGeometry = localView(this->fvGridGeometry()); + fvGeometry.bindElement(element); - for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) + for (auto&& scv : scvs(fvGeometry)) { - int dofIdxGlobal = this->model().dofMapper().subIndex(element, scvIdx, dofCodim); + VolumeVariables volVars; + volVars.update(elemSol, *this, element, scv); + const auto& globalPos = scv.dofPosition(); + const auto dofIdxGlobal = scv.dofIndex(); - //reactionSource Output - const GlobalPosition& globalPos = element.geometry().corner(scvIdx); - static Scalar reactionLayerWidth = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.ReactionLayerWidth); - PrimaryVariables source(0.0); - if(globalPos[0] < reactionLayerWidth + eps_) + static Scalar reactionLayerWidth = getParam("Problem.ReactionLayerWidth"); + if (globalPos[0] < reactionLayerWidth + eps_) { - // Hack for Acosta solution (see solDependetSource comment) - auto i = ElectroChemistry::calculateCurrentDensity(elemVolVars[scvIdx])/2; - ElectroChemistry::reactionSource(source, i); + //reactionSource Output + PrimaryVariables source; + auto i = ElectroChemistry::calculateCurrentDensity(volVars); + ElectroChemistry::reactionSource(source, i, ""); - (*reactionSourceH2O)[dofIdxGlobal] = source[wPhaseIdx]; - (*reactionSourceO2)[dofIdxGlobal] = source[numComponents-1]; + reactionSourceH2O_[dofIdxGlobal] = source[Indices::conti0EqIdx + FluidSystem::H2OIdx]; + reactionSourceO2_[dofIdxGlobal] = source[Indices::conti0EqIdx + FluidSystem::O2Idx]; #if !ISOTHERMAL - (*reactionSourceTemp)[dofIdxGlobal] = source[numComponents]; + reactionSourceTemp_[dofIdxGlobal] = source[numComponents]; #endif - (*currentDensity)[dofIdxGlobal] = i/10000; + //Current Output in A/cm^2 + currentDensity_[dofIdxGlobal] = i/10000; } + else { - (*reactionSourceH2O)[dofIdxGlobal] = 0.0; - (*reactionSourceO2)[dofIdxGlobal] = 0.0; + reactionSourceH2O_[dofIdxGlobal] = 0.0; + reactionSourceO2_[dofIdxGlobal] = 0.0; #if !ISOTHERMAL - (*reactionSourceTemp)[dofIdxGlobal] = 0.0; + reactionSourceTemp_[dofIdxGlobal] = 0.0; #endif - (*currentDensity)[dofIdxGlobal] = 0.0; + currentDensity_[dofIdxGlobal] = 0.0; } } - } - - this->resultWriter().attachDofData(*reactionSourceH2O, "reactionSourceH2O [mol/(sm^2)]", isBox); - this->resultWriter().attachDofData(*reactionSourceO2, "reactionSourceO2 [mol/(sm^2)]", isBox); - this->resultWriter().attachDofData(*currentDensity, "currentDensity [A/cm^2]", isBox); -#if !ISOTHERMAL - this->resultWriter().attachDofData(*reactionSourceTemp, "reactionSource Heat [J/(sm^2)]", isBox); -#endif } private: - - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initial_( const GlobalPosition &globalPos) const { // the internal method for the initial condition - - /* - *if bothPhases, set initial values for pg (pressureIdx), Sl (switchIdx), xO2l [mol/m3] (switchIdx+1) - *if nPhaseOnly, set initial values for pg (pressureIdx), xH2Og (switchIdx), xO2g [mol/m3] (switchIdx+1) - *if wPhaseOnly, set initial values for pg (pressureIdx), xN2l (switchIdx), xO2l [mol/m3] (switchIdx+1) - */ + PrimaryVariables values; values[pressureIdx] = pgInlet1_; values[switchIdx] = swInlet_; @@ -472,7 +395,9 @@ private: #if !ISOTHERMAL values[temperatureIdx] = 343.15; #endif + values.setState(Indices::bothPhases); + return values; } Scalar temperature_; @@ -490,7 +415,15 @@ private: Scalar pgInlet1_; Scalar pgInlet2_; Scalar swInlet_; -}; -} //end namespace + std::vector currentDensity_; + std::vector reactionSourceH2O_; + std::vector reactionSourceO2_; +#if !ISOTHERMAL + std::vector reactionSourceTemp_; #endif +}; + +} //end namespace Dumux + +#endif // DUMUX_FUELCELL_LECTURE_PROBLEM_HH diff --git a/lecture/mm/fuelcell/fuelcellspatialparams.hh b/lecture/mm/fuelcell/fuelcellspatialparams.hh index ee0d994dc4cc15b403030b4e412c3f9da541f203..5a10cb22aed4238a0107ab960b7c9339f280b5b8 100644 --- a/lecture/mm/fuelcell/fuelcellspatialparams.hh +++ b/lecture/mm/fuelcell/fuelcellspatialparams.hh @@ -26,53 +26,35 @@ #ifndef DUMUX_FUELCELL_LECTURE_SPATIAL_PARAMS_HH #define DUMUX_FUELCELL_LECTURE_SPATIAL_PARAMS_HH -#include +#include #include -//#include -//#include +#include +#include #include -//#include #include "./material/regularizedacosta.hh" #include "./material/thermalconductivityconstant.hh" -namespace Dumux -{ +namespace Dumux { //forward declaration template class FuelCellLectureSpatialParams; -namespace Properties -{ +namespace Properties { + // The spatial parameters TypeTag -NEW_TYPE_TAG(FuelCellLectureSpatialParams); +NEW_TYPE_TAG(FuelCellLectureSpatialParamsTypeTag); // Set the spatial parameters -SET_TYPE_PROP(FuelCellLectureSpatialParams, SpatialParams, Dumux::FuelCellLectureSpatialParams); +SET_TYPE_PROP(FuelCellLectureSpatialParamsTypeTag, SpatialParams, FuelCellLectureSpatialParams); // Set the constant thermal conductivity law as in Acosta paper #if !ISOTHERMAL -SET_TYPE_PROP(FuelCellLectureSpatialParams, ThermalConductivityModel, Dumux::ThermalConductivityConstant); +SET_TYPE_PROP(FuelCellLectureSpatialParamsTypeTag, ThermalConductivityModel, Dumux::ThermalConductivityConstant); #endif -// Set the material Law -SET_PROP(FuelCellLectureSpatialParams, MaterialLaw) -{ - private: - // define the material law which is parameterized by effective - // saturations - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - //typedef RegularizedBrooksCorey EffMaterialLaw; - //typedef RegularizedVanGenuchten EffMaterialLaw; - typedef RegularizedAcosta EffMaterialLaw; - - public: - // define the material law parameterized by absolute saturations - typedef EffToAbsLaw type; - //typedef PhilToPhobLaw type; -}; -} +} // end namespace Properties /*! * \ingroup TwoPNCModel @@ -81,41 +63,43 @@ SET_PROP(FuelCellLectureSpatialParams, MaterialLaw) * problem which uses the (non-)isothermal 2pnc box model */ template -class FuelCellLectureSpatialParams : public ImplicitSpatialParams +class FuelCellLectureSpatialParams: public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - - enum { dim=GridView::dimension }; - enum { dimworld=GridView::dimensionworld }; - enum { wPhaseIdx = FluidSystem::wPhaseIdx }; - - typedef Dune::FieldVector GlobalPosition; - typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; + using ThisType = FuelCellLectureSpatialParams; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using ParentType = FVSpatialParams; + using GridView = typename FVGridGeometry::GridView; + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + static constexpr int dim=GridView::dimension; + static constexpr int dimworld=GridView::dimensionworld; + static constexpr int wPhaseIdx = FluidSystem::wPhaseIdx; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using EffectiveLaw = RegularizedAcosta; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; + using PermeabilityType = Scalar; /*! * \brief The constructor * * \param gridView The grid view */ - FuelCellLectureSpatialParams(const GridView &gridView) - : ParentType(gridView), K_(0) + FuelCellLectureSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) + , K_(0) { // intrinsic permeabilities K_ = 5.2e-11; // Acosta absolute permeability diffusion layer // porosities - porosity_ = 0.78; // Acosta porosity diffusion layer //thermalconductivity @@ -125,40 +109,23 @@ public: materialParams_.setSwr(0.05); //value for imbibition from Acosta materialParams_.setSnr(0.05); //assumption - // parameters for the Brooks-Corey law - //materialParams_.setPe(1.02e4); - //materialParams_.setLambda(1.59); - - //parameters for the vanGenuchten law - //materialParams_.setVgAlpha(6.66e-5); //alpha=1/pcb - //materialParams_.setVgn(3.652); - //parameters for the Acosta saturation capillary pressure relation materialParams_.setAcA(-1168.75); //imbition -1168.75; drainage -600; materialParams_.setAcB(8.5); //imbition 8.5; drainage 25; materialParams_.setAcC(-0.2); //imbition -0.2; drainage -16; materialParams_.setAcD(-700); //imbition -700; drainage -3300; materialParams_.setAcE(0.0); //imbition 0.0; drainage 800; - - eps_ = 1e-6; - - } ~FuelCellLectureSpatialParams() {} /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. + * \brief Returns the hydraulic conductivity \f$[m^2]\f$ * - * \param element The current finite element - * \param fvGeometry The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param globalPos The global position */ - const Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const { return K_; } @@ -166,37 +133,37 @@ public: /*! * \brief Define the porosity \f$[-]\f$ of the spatial parameters * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined + * \param globalPos The global position */ - Scalar porosity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - const GlobalPosition &globalPos = fvGeometry.subContVol[scvIdx].global; - - Scalar reactionLayerWidth = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.ReactionLayerWidth); + Scalar porosityAtPos(const GlobalPosition& globalPos) const + { + static Scalar reactionLayerWidth = getParam("Problem.ReactionLayerWidth"); if (globalPos[0] < reactionLayerWidth + eps_) return 0.07; + else return porosity_; - } + /*! + * \brief Function for defining which phase is to be considered as the wetting phase. + * + * \return the wetting phase index + * \param globalPos The global position + */ + template + int wettingPhaseAtPos(const GlobalPosition& globalPos) const + { + return FluidSystem::phase0Idx; + } /*! * \brief return the parameter object for the Brooks-Corey material law which depends on the position * - * \param element The current finite element - * \param fvGeometry The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param globalPos The global position */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { return materialParams_; } @@ -210,6 +177,7 @@ public: * \param fvGeometry The finite volume geometry * \param scvIdx The local index of the sub-control volume */ + template Scalar solidHeatCapacity(const Element &element, const FVElementGeometry &fvGeometry, const int scvIdx) const @@ -226,6 +194,7 @@ public: * \param fvGeometry The finite volume geometry * \param scvIdx The local index of the sub-control volume */ + template Scalar solidDensity(const Element &element, const FVElementGeometry &fvGeometry, const int scvIdx) const @@ -244,7 +213,7 @@ public: */ Scalar solidThermalConductivity(const Element &element, const FVElementGeometry &fvGeometry, - const int scvIdx) const + const SubControlVolume& scv) const { return lambdaSolid_; } @@ -252,11 +221,11 @@ public: private: Scalar K_; Scalar porosity_; - Scalar eps_; + Scalar eps_ = 1e-6; MaterialLawParams materialParams_; Scalar lambdaSolid_; }; -} +} // end namespace Dumux -#endif +#endif // DUMUX_FUELCELL_LECTURE_SPATIAL_PARAMS_HH diff --git a/lecture/mm/fuelcell/material/thermalconductivityconstant.hh b/lecture/mm/fuelcell/material/thermalconductivityconstant.hh index 932ad6edb395cf6798b2b81e1c2b3008f7deb361..1cb128bd392753705902906711dc292a28f7ae67 100644 --- a/lecture/mm/fuelcell/material/thermalconductivityconstant.hh +++ b/lecture/mm/fuelcell/material/thermalconductivityconstant.hh @@ -52,8 +52,8 @@ public: const SpatialParams& spatialParams, const Element& element, const FVGeometry& fvGeometry, - int scvIdx) - { return spatialParams.solidThermalConductivity(element, fvGeometry, scvIdx); } + const typename FVGeometry::SubControlVolume& scv) + { return spatialParams.solidThermalConductivity(element, fvGeometry, scv); } }; } #endif diff --git a/lecture/mm/heatpipe/CMakeLists.txt b/lecture/mm/heatpipe/CMakeLists.txt index b39e377f173d1763b8b09d3c3a6e1ce63bc81949..0ee91693ccce60f16b741bc7ea8d0c0ef6aa3b7e 100644 --- a/lecture/mm/heatpipe/CMakeLists.txt +++ b/lecture/mm/heatpipe/CMakeLists.txt @@ -4,7 +4,7 @@ add_dumux_test(heatpipe heatpipe heatpipe.cc python ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py --script fuzzy --files ${CMAKE_SOURCE_DIR}/lecture/references/heatpipe-reference.vtu - ${CMAKE_CURRENT_BINARY_DIR}/heatpipe-00023.vtu + ${CMAKE_CURRENT_BINARY_DIR}/heatpipe-00022.vtu --command "${CMAKE_CURRENT_BINARY_DIR}/heatpipe ") # headers for installation and headercheck diff --git a/lecture/mm/heatpipe/description/problemdescription-heatpipe.tex b/lecture/mm/heatpipe/description/problemdescription-heatpipe.tex index 5ae8c9b3d693b5710284052f16c5554529742432..5bd63c511303f30bbba663c8831b6a2273b1d8d4 100644 --- a/lecture/mm/heatpipe/description/problemdescription-heatpipe.tex +++ b/lecture/mm/heatpipe/description/problemdescription-heatpipe.tex @@ -142,7 +142,14 @@ gradients of both phases have different signs. It is recommended to you use a Plot-Over-Line to display a cross-sectional view on the parameters saturation, temperature, pressures, etc. -\subsection{Questions} +\subsection{Exercises and questions} + +Compile and run the model by modifying one by one a number of parameters. +Usually, it is sufficient to use the parameters in the input file. + +Keep attention to the length of the heatpipe. Dependent on the choice of the +parameters, it can be necessary to increase the length of the model domain if +you want to see the full heatpipe. \begin{enumerate} \item Which influence has the capillary pressure on the extension of the heatpipe? diff --git a/lecture/mm/heatpipe/heatpipe.cc b/lecture/mm/heatpipe/heatpipe.cc index d6fd721e20c298142fa86768f6e78cfd8de023fa..3b7c42bbd1a8010bd3f5fb8af40789b8c5b4a0f3 100644 --- a/lecture/mm/heatpipe/heatpipe.cc +++ b/lecture/mm/heatpipe/heatpipe.cc @@ -16,43 +16,183 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -/*! - * \file - * - * \brief test for the 2p2cni box model - */ #include "config.h" +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + #include "heatpipeproblem.hh" -#include - -/*! - * \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) + +int main(int argc, char** argv) try { - 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 DGF format\n"; - - std::cout << errorMessageOut - << "\n"; - } + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(HeatPipeTypeTag); + + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // initialize parameter tree + Parameters::init(argc, argv); + + ////////////////////////////////////////////////////////////////////// + // try to create a grid (from the given grid file or the input file) + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + auto tEnd = getParam("TimeLoop.TEnd"); + auto dt = getParam("TimeLoop.DtInitial"); + auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + + // intialize the vtk output module + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(0.0, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = ILU0BiCGSTABBackend; + auto linearSolver = std::make_shared(); + + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // set some check points for the time loop + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength")); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + if (timeLoop->isCheckPoint()) + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/false); + + return 0; } -int main(int argc, char** argv) +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(HeatpipeProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/heatpipe/heatpipe.input b/lecture/mm/heatpipe/heatpipe.input index 8b6500240309b06cd7a0467b38d79a561d76cca1..83ea0d2c67dff52b4a2797312b2041eedf5652a3 100644 --- a/lecture/mm/heatpipe/heatpipe.input +++ b/lecture/mm/heatpipe/heatpipe.input @@ -1,6 +1,4 @@ - - -[TimeManager] +[TimeLoop] DtInitial = 1 # [s] TEnd = 2.2e12 # [s] EpisodeLength = 1.e11 @@ -8,10 +6,17 @@ EpisodeLength = 1.e11 [Grid] File = ./grids/heatpipe.dgf -[] -permeability = 1.e-12 # m^2 -heatFlux = -100.0 # W/m^2 -multiplierHeatCond = 1.0 # Scaling factor for the implemented heat conductivity -multiplierPC = 1.0 # Scaling factor for the capillary pressure +[Assembly] +NumericDifferenceMethod = 0 +[Problem] +Permeability = 1.e-12 # m^2 +HeatFlux = -100.0 # W/m^2 +MultiplierPC = 1.0 # Scaling factor for the capillary pressure +Name = heatpipe +EnableGravity = false +[Component] +SolidHeatCapacity = 700 +SolidDensity = 2600 +SolidThermalConductivity = 2.8 diff --git a/lecture/mm/heatpipe/heatpipeproblem.hh b/lecture/mm/heatpipe/heatpipeproblem.hh index 0ae6e29f6c98f6b7457b35bfeac89bc3453fd15c..961c1082510428a06ab2625cb2b42cfa06f53248 100644 --- a/lecture/mm/heatpipe/heatpipeproblem.hh +++ b/lecture/mm/heatpipe/heatpipeproblem.hh @@ -16,268 +16,137 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -/*! - * \file - * - * \brief Non-isothermal gas injection problem where a gas (e.g. air) - * is injected into a fully water saturated medium. - */ #ifndef DUMUX_HEATPIPE_PROBLEM_HH #define DUMUX_HEATPIPE_PROBLEM_HH +#include + +#include + #include -#include -#include +#include +#include #include "heatpipespatialparams.hh" - namespace Dumux { + template -class HeatpipeProblem; +class HeatPipeProblem; namespace Properties { -NEW_TYPE_TAG(HeatpipeProblem, INHERITS_FROM(BoxTwoPTwoCNI, HeatpipeSpatialParams)); +NEW_TYPE_TAG(HeatPipeTypeTag, INHERITS_FROM(BoxModel, TwoPTwoCNI)); // Set the grid type -SET_TYPE_PROP(HeatpipeProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(HeatPipeTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(HeatpipeProblem, Problem, HeatpipeProblem); +SET_TYPE_PROP(HeatPipeTypeTag, Problem, HeatPipeProblem); // Set the fluid system -SET_TYPE_PROP(HeatpipeProblem, FluidSystem, FluidSystems::H2OAir); +SET_TYPE_PROP(HeatPipeTypeTag, FluidSystem, FluidSystems::H2OAir); -// Disable gravity -SET_BOOL_PROP(HeatpipeProblem, ProblemEnableGravity, false); - -// Use central differences -SET_INT_PROP(HeatpipeProblem, ImplicitNumericDifferenceMethod, 0); +// Set the spatial parameters +SET_PROP(HeatPipeTypeTag, SpatialParams) +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using type = HeatPipeSpatialParams; +}; -// Write newton convergence -SET_BOOL_PROP(HeatpipeProblem, NewtonWriteConvergence, false); } - - /* This problem uses the \ref TwoPTwoCNIModel model. - * - * To run the simulation execute the following line in shell: - * ./heatpipe -ParameterFile heatpipe.input - * */ template -class HeatpipeProblem : public ImplicitPorousMediaProblem +class HeatPipeProblem : public PorousMediumFlowProblem { - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GridView::Grid Grid; - - typedef ImplicitPorousMediaProblem ParentType; - - // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - - pressureIdx = Indices::pressureIdx, - switchIdx = Indices::switchIdx, - temperatureIdx = Indices::temperatureIdx, - energyEqIdx = Indices::energyEqIdx, - - // Phase State - bothPhases = Indices::bothPhases, - - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld, - - conti0EqIdx = Indices::conti0EqIdx, - contiNEqIdx = conti0EqIdx + Indices::nCompIdx - }; - - - typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; - typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; - typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + using ParentType = PorousMediumFlowProblem; - typedef typename GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FluidSystem =typename GET_PROP_TYPE(TypeTag, FluidSystem); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; - typedef Dune::FieldVector GlobalPosition; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); public: - /*! - * \brief The constructor - * - * \param timeManager The time manager - * \param gridView The grid view - */ - HeatpipeProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView) + HeatPipeProblem(std::shared_ptr fVGridGeometry) + : ParentType(fVGridGeometry) { - eps_ = 1e-6; - FluidSystem::init(); - episodeLength_ = GET_RUNTIME_PARAM(TypeTag, Scalar, TimeManager.EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); } - /*! - * \name Problem parameters - */ - // \{ - - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { return "heatpipe"; } - - void sourceAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const + NumEqVector sourceAtPos(const GlobalPosition &globalPos) const { - values = 0; + return NumEqVector(0.0); } - // \} - - /*! - * \name Boundary conditions - */ - // \{ - - /*! - * \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 + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition globalPos = vertex.geometry().center(); + BoundaryTypes values; if(globalPos[0] < eps_) values.setAllDirichlet(); else values.setAllNeumann(); + + return values; } - /*! - * \brief Evaluate the boundary conditions for a dirichlet - * boundary segment. - * - * \param values The dirichlet values for the primary variables - * \param vertex The vertex for which the boundary type is set - * - * For this method, the \a values parameter stores primary variables. - */ - void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - values[pressureIdx] = 1.013e5; - values[switchIdx] = 0.01; - values[temperatureIdx] = 341.75; + PrimaryVariables values(0.0); + + values[Indices::pressureIdx] = 1.013e5; + values[Indices::switchIdx] = 0.01; + values[Indices::temperatureIdx] = 341.75; + values.setState(Indices::bothPhases); + + return values; } - /*! - * \brief Evaluate the boundary conditions for a neumann - * boundary segment. - * - * \param values Stores the Neumann values for the conservation equations in - * \f$ [ \textnormal{unit of conserved quantity} / (m^(dim-1) \cdot s )] \f$ - * \param globalPos The position of the integration point of the boundary segment. - * - * 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 &globalPos) const + NumEqVector neumannAtPos( const GlobalPosition &globalPos) const { - values = 0; + NumEqVector values(0.0); // negative values for injection - if (globalPos[0] > (this->bBoxMax()[0] - eps_)) + if (globalPos[0] > (this->fvGridGeometry().bBoxMax()[0] - eps_)) { - values[energyEqIdx] = GET_RUNTIME_PARAM(TypeTag, Scalar, heatFlux); + values[Indices::energyEqIdx] = getParam("Problem.HeatFlux"); } - } - - // \} - - /*! - * \name Volume terms - */ - // \{ - - /*! - * \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, - int scvIdx) const - { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - - initial_(values, globalPos); - } - /*! - * \brief Return the initial phase state inside a control volume. - * - * \param globalPos The global position - */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const - { - return bothPhases; + return values; } - bool shouldWriteRestartFile() const - { - return 0; - } - - bool shouldWriteOutput() const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); + return initial_(globalPos); } - void episodeEnd() - { - this->timeManager().startNextEpisode(episodeLength_); - } private: - // internal method for the initial condition (reused for the - // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initial_(const GlobalPosition &globalPos) const { - values[pressureIdx] = 1.013e5; - values[switchIdx] = 0.5; - values[temperatureIdx] = 343.15; + PrimaryVariables values; + + values[Indices::pressureIdx] = 1.013e5; + values[Indices::switchIdx] = 0.5; + values[Indices::temperatureIdx] = 343.15; + values.setState(Indices::bothPhases); + + return values; } - Scalar eps_; - Scalar episodeLength_; + static constexpr Scalar eps_ = 1e-6; }; -} //end namespace -#endif +} // namespace Dumux + +#endif // DUMUX_HEATPIPE_PROBLEM_HH diff --git a/lecture/mm/heatpipe/heatpipespatialparams.hh b/lecture/mm/heatpipe/heatpipespatialparams.hh index 38ee5f6760429d627968f567e920566961a7a070..48949bcd7e98ec1e518ae4f81787653da0c9a188 100644 --- a/lecture/mm/heatpipe/heatpipespatialparams.hh +++ b/lecture/mm/heatpipe/heatpipespatialparams.hh @@ -16,210 +16,71 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -/*! - * \file - * - * \brief Definition of the spatial parameters for the water-air problem. - */ #ifndef DUMUX_HEATPIPE_SPATIAL_PARAMS_HH #define DUMUX_HEATPIPE_SPATIAL_PARAMS_HH -#include -#include -//#include -//#include -//#include +#include + #include "krpcheatpipe.hh" namespace Dumux { -//forward declaration -template -class HeatpipeSpatialParams; - -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(HeatpipeSpatialParams); - -// Set the spatial parameters -SET_TYPE_PROP(HeatpipeSpatialParams, SpatialParams, Dumux::HeatpipeSpatialParams); - -// Set the material Law -SET_PROP(HeatpipeSpatialParams, MaterialLaw) -{ - private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - public: - typedef KrPcHeatpipe type; -}; -} +template +class HeatPipeSpatialParams +: public FVSpatialParams> -/*! - * \ingroup TwoPTwoCNIModel - * \ingroup BoxTestProblems - * \brief Definition of the spatial parameters for the water-air problem - */ -template -class HeatpipeSpatialParams : public ImplicitSpatialParams { - typedef ImplicitSpatialParams ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - wPhaseIdx = Indices::wPhaseIdx - }; - - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldVector DimVector; - - typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + using ThisType = HeatPipeSpatialParams; + using ParentType = FVSpatialParams; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; + using GridView = typename FVGridGeometry::GridView; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; - - /*! - * \brief The constructor - * - * \param gridView The grid view - */ - HeatpipeSpatialParams(const GridView &gridView) - : ParentType(gridView) - { - // intrinsic permeabilities - K_ = GET_RUNTIME_PARAM(TypeTag, Scalar, permeability); - - // porosities - Porosity_ = 0.4; - - // residual saturations - MaterialParams_.setSwr(0.15); - MaterialParams_.setSnr(0.0); + using PermeabilityType = Scalar; + using MaterialLaw = KrPcHeatpipe; + using MaterialLawParams = typename MaterialLaw::Params; - // parameters for the Brooks-Corey law - MaterialParams_.setP0(std::pow((Porosity_/K_),0.5)); - } - - ~HeatpipeSpatialParams() - {} - - /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. - * - * \param element The current finite element - * \param fvGeometry The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume - */ - const Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + HeatPipeSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - return K_; - } + permeability_ = getParam("Problem.Permeability"); + porosity_ = 0.4; - /*! - * \brief Define the porosity \f$[-]\f$ of the spatial parameters - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined - */ - double porosity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return Porosity_; + materialParams_.setSwr(0.15); + materialParams_.setSnr(0.0); + materialParams_.setP0(std::pow((porosity_/permeability_), 0.5)); } - - /*! - * \brief return the parameter object for the Brooks-Corey material law which depends on the position - * - * \param element The current finite element - * \param fvGeometry The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume - */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const { - return MaterialParams_; + return permeability_; } - /*! - * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidHeatCapacity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + Scalar porosityAtPos(const GlobalPosition& globalPos) const { - return 700; // specific heat capacity [J / (kg K)] + return porosity_; } - /*! - * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidDensity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { - return 2600; // density [kg/m^3] + return materialParams_; } - /*! - * \brief Returns the thermal conductivity \f$[W/m^2]\f$ of the porous material. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the heat capacity needs to be defined - */ - Scalar solidThermalConductivity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + template + int wettingPhaseAtPos(const GlobalPosition& globalPos) const { - return 2.8*GET_RUNTIME_PARAM(TypeTag, Scalar, multiplierHeatCond); + return FluidSystem::phase0Idx; } - private: - - Scalar K_; - - Scalar Porosity_; - - MaterialLawParams MaterialParams_; + PermeabilityType permeability_; + Scalar porosity_; + MaterialLawParams materialParams_; }; } -#endif +#endif // DUMUX_HEATPIPE_SPATIAL_PARAMS_HH diff --git a/lecture/mm/heatpipe/krpcheatpipe.hh b/lecture/mm/heatpipe/krpcheatpipe.hh index 1da6ee6b8e86260f17cc62f1888af91b2b8e01ed..4f3843590baf4e87800ccf8127e3a9f28ab8f4f1 100644 --- a/lecture/mm/heatpipe/krpcheatpipe.hh +++ b/lecture/mm/heatpipe/krpcheatpipe.hh @@ -16,13 +16,6 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ -/*! - * \file - * - * \brief Implementation of the capillary pressure and - * relative permeability <-> saturation relations according to Brooks and Corey. - * - */ #ifndef DUMUX_KR_PC_HEATPIPE_HH #define DUMUX_KR_PC_HEATPIPE_HH @@ -42,12 +35,12 @@ namespace Dumux * cap-press based on the function of Leverett. * */ -template > +template > class KrPcHeatpipe { public: - typedef ParamsT Params; - typedef typename Params::Scalar Scalar; + using Params = ParamsT; + using Scalar = ScalarT; /*! * \brief The capillary pressure-saturation curve according to Leverett. @@ -70,7 +63,7 @@ public: value = a*std::pow(sw,4)+c; } - Scalar MODIFY_ME_CAPILLARY_PRESSURE = GET_RUNTIME_PARAM(TypeTag, Scalar, multiplierPC); + Scalar MODIFY_ME_CAPILLARY_PRESSURE = getParam("Problem.MultiplierPC"); return(value * MODIFY_ME_CAPILLARY_PRESSURE); } @@ -111,4 +104,4 @@ public: }; } -#endif // BROOKS_COREY_HH +#endif // DUMUX_KR_PC_HEATPIPE_HH diff --git a/lecture/mm/heavyoil/3p/parkervangenuchtenzero.hh b/lecture/mm/heavyoil/3p/parkervangenuchtenzero.hh index 7db55e632bb07fe1fdebcfe5b540210753e1948c..fa831947e287dd8b6998e1094f265a626662a912 100644 --- a/lecture/mm/heavyoil/3p/parkervangenuchtenzero.hh +++ b/lecture/mm/heavyoil/3p/parkervangenuchtenzero.hh @@ -29,8 +29,8 @@ #include "parkervangenuchtenzeroparams.hh" -namespace Dumux -{ +namespace Dumux { + /*! * \ingroup material * @@ -44,10 +44,9 @@ namespace Dumux template > class ParkerVanGenZero3P { - public: - typedef ParamsT Params; - typedef typename Params::Scalar Scalar; + using Params = ParamsT; + using Scalar = typename Params::Scalar; /*! * \brief The capillary pressure-saturation curve. @@ -171,16 +170,14 @@ public: */ static Scalar krw(const Params ¶ms, Scalar saturation, Scalar sn, Scalar sg) { - //transformation to effective saturation - //Scalar se = (saturation - params.swr()) / (1-params.swr()); - Scalar se = (saturation - params.swr()) / (1-params.swr()); - + Scalar se = (saturation - params.swr()) / (1-params.swr()); - - /* regularization */ - if(se > 1.0) return 1.; - if(se < 0.0) return 0.; + // regularization + if (se > 1.0) + return 1.; + if (se < 0.0) + return 0.; Scalar r = 1. - std::pow(1 - std::pow(se, 1/params.vgm()), params.vgm()); return std::sqrt(se)*r*r; @@ -205,14 +202,16 @@ public: */ static Scalar krn(const Params ¶ms, Scalar sw, Scalar saturation, Scalar sg) { - Scalar swe = std::min((sw - params.swr()) / (1 - params.swr()), 1.); Scalar ste = std::min((sw + saturation - params.swr()) / (1 - params.swr()), 1.); // regularization - if(swe <= 0.0) swe = 0.; - if(ste <= 0.0) ste = 0.; - if(ste - swe <= 0.0) return 0.; + if (swe <= 0.0) + swe = 0.; + if (ste <= 0.0) + ste = 0.; + if (ste - swe <= 0.0) + return 0.; Scalar krn_; krn_ = std::pow(1 - std::pow(swe, 1/params.vgm()), params.vgm()); @@ -225,14 +224,13 @@ public: Scalar resIncluded = std::max(std::min((saturation - params.snr()/ (1-params.swr())), 1.), 0.); krn_ *= std::sqrt(resIncluded ); } + else krn_ *= std::sqrt(saturation / (1 - params.swr())); // Hint: (ste - swe) = sn / (1-Srw) - return krn_; }; - /*! * \brief The relative permeability for the non-wetting phase * of the medium implied by van Genuchten's @@ -249,14 +247,16 @@ public: */ static Scalar krg(const Params ¶ms, Scalar sw, Scalar sn, Scalar saturation) { - // se = (sw+sn - Sgr)/(1-Sgr) Scalar se = std::min(((1-saturation) - params.sgr()) / (1 - params.sgr()), 1.); /* regularization */ - if(se > 1.0) return 0.0; - if(se < 0.0) return 1.0; + if(se > 1.0) + return 0.0; + if(se < 0.0) + return 1.0; + Scalar scalFact = 1.; if (saturation<=0.1) { @@ -278,7 +278,6 @@ public: * \param phase indicator, The saturation of all phases. */ static Scalar kr(const Params ¶ms, const int phase, const Scalar sw, const Scalar sn, const Scalar sg) - { switch (phase) { @@ -292,6 +291,7 @@ public: return krg(params, sw, sn, sg); break; } + return 0; }; @@ -303,7 +303,9 @@ public: { return params.rhoBulk() * params.KdNAPL(); } + }; -} -#endif +} // end namespace Dumux + +#endif // DUMUX_PARKERVANGENZERO_3P_HH diff --git a/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresis.hh b/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresis.hh index b2d9a27df8a60aefda61974e50447e2dc3908ebf..7c3a40b830dfc05b43b01618569c44d9027e3deb 100644 --- a/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresis.hh +++ b/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresis.hh @@ -29,8 +29,7 @@ #include "parkervangenuchtenzerohysteresisparams.hh" -namespace Dumux -{ +namespace Dumux { /*! * \ingroup material * @@ -44,10 +43,9 @@ namespace Dumux template > class ParkerVanGenZeroHyst3P { - public: - typedef ParamsT Params; - typedef typename Params::Scalar Scalar; + using Params = ParamsT; + using Scalar = typename Params::Scalar; /*! * \brief The capillary pressure-saturation curve. @@ -117,7 +115,6 @@ public: DUNE_THROW(Dune::NotImplemented, "sw(pc) for three phases not implemented! Do it yourself!"); } - DUNE_DEPRECATED_MSG("use sw() (uncapitalized 's') instead") static Scalar Sw(const Params ¶ms, Scalar pc) { @@ -171,18 +168,17 @@ public: */ static Scalar krw(const Params ¶ms, Scalar saturation, Scalar sn, Scalar sg) { - //transformation to effective saturation - //Scalar se = (saturation - params.swr()) / (1-params.swr()); - Scalar se = (saturation - params.swr()) / (1-params.TrappedSatN()-params.swr()); - + Scalar se = (saturation - params.swr()) / (1-params.TrappedSatN()-params.swr()); - - /* regularization */ - if(se > 1.0) return 1.; - if(se < 0.0) return 0.; + // regularization + if (se > 1.0) + return 1.; + if (se < 0.0) + return 0.; Scalar r = 1. - std::pow(1 - std::pow(se, 1/params.vgm()), params.vgm()); + return std::sqrt(se)*r*r; }; @@ -205,14 +201,16 @@ public: */ static Scalar krn(const Params ¶ms, Scalar sw, Scalar saturation, Scalar sg) { - Scalar swe = std::min((sw - params.swr()) / (1 - params.TrappedSatN()- params.swr()), 1.); Scalar ste = std::min((sw + saturation - params.swr()) / (1 - params.swr()), 1.); // regularization - if(swe <= 0.0) swe = 0.; - if(ste <= 0.0) ste = 0.; - if(ste - swe <= 0.0) return 0.; + if (swe <= 0.0) + swe = 0.; + if (ste <= 0.0) + ste = 0.; + if (ste - swe <= 0.0) + return 0.; Scalar krn_; krn_ = std::pow(1 - std::pow(swe, 1/params.vgm()), params.vgm()); @@ -225,14 +223,13 @@ public: Scalar resIncluded = std::max(std::min((saturation - params.snr()/ (1-params.swr())), 1.), 0.); krn_ *= std::sqrt(resIncluded ); } + else krn_ *= std::sqrt(saturation / (1 - params.swr())); // Hint: (ste - swe) = sn / (1-Srw) - return krn_; }; - /*! * \brief The relative permeability for the non-wetting phase * of the medium implied by van Genuchten's @@ -249,19 +246,22 @@ public: */ static Scalar krg(const Params ¶ms, Scalar sw, Scalar sn, Scalar saturation) { - // se = (sw+sn - Sgr)/(1-Sgr) Scalar se = std::min(((1-saturation) - params.sgr()) / (1 - params.sgr()), 1.); + // regularization + if (se > 1.0) + return 0.0; + if (se < 0.0) + return 1.0; - /* regularization */ - if(se > 1.0) return 0.0; - if(se < 0.0) return 1.0; Scalar scalFact = 1.; + if (saturation<=0.1) { scalFact = (saturation - params.sgr())/(0.1 - params.sgr()); - if (scalFact < 0.) scalFact = 0.; + if (scalFact < 0.) + scalFact = 0.; } Scalar result = scalFact * std::pow(1 - se, 1.0/3.) * std::pow(1 - std::pow(se, 1/params.vgm()), 2*params.vgm()); @@ -278,7 +278,6 @@ public: * \param phase indicator, The saturation of all phases. */ static Scalar kr(const Params ¶ms, const int phase, const Scalar sw, const Scalar sn, const Scalar sg) - { switch (phase) { @@ -292,6 +291,7 @@ public: return krg(params, sw, sn, sg); break; } + return 0; }; @@ -303,7 +303,9 @@ public: { return params.rhoBulk() * params.KdNAPL(); } + }; -} -#endif +} // end namespace Dumux + +#endif // DUMUX_PARKERVANGENZEROHYST_3P_HH diff --git a/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresisparams.hh b/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresisparams.hh index c26fe58fc4af4722fb572d5d981bd64366840a10..744ae92f6a34bcaf5c67e351eb121a48ba99a604 100644 --- a/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresisparams.hh +++ b/lecture/mm/heavyoil/3p/parkervangenuchtenzerohysteresisparams.hh @@ -30,8 +30,7 @@ #include -namespace Dumux -{ +namespace Dumux { /*! * \brief Reference implementation of a van Genuchten params */ @@ -39,13 +38,23 @@ template class ParkerVanGenZeroHyst3PParams { public: - typedef ScalarT Scalar; + using Scalar = ScalarT; ParkerVanGenZeroHyst3PParams() - {betaGw_ = betaNw_ = betaGn_ = 1.;} + { + betaGw_ = betaNw_ = betaGn_ = 1.; + } - ParkerVanGenZeroHyst3PParams(Scalar vgAlpha, Scalar vgn, Scalar KdNAPL, Scalar rhoBulk, Dune::FieldVector residualSaturation, Scalar betaNw = 1., Scalar betaGn = 1., Scalar betaGw = 1., bool regardSnr=false, - Scalar TrappedSatN = 1.) + ParkerVanGenZeroHyst3PParams(Scalar vgAlpha, + Scalar vgn, + Scalar KdNAPL, + Scalar rhoBulk, + Dune::FieldVector residualSaturation, + Scalar betaNw = 1., + Scalar betaGn = 1., + Scalar betaGw = 1., + bool regardSnr=false, + Scalar TrappedSatN = 1.) { setVgAlpha(vgAlpha); setVgn(vgn); @@ -59,40 +68,51 @@ public: setBetaGn(betaGn); setBetaGw(betaGw); setRhoBulk(rhoBulk); - setTrappedSatN(TrappedSatN); }; Scalar TrappedSatN() const - { return TrappedSatN_; } - void setTrappedSatN(Scalar v) - { TrappedSatN_ = v; } + { + return TrappedSatN_; + } + void setTrappedSatN(Scalar v) + { + TrappedSatN_ = v; + } /*! * \brief Return the \f$\alpha\f$ shape parameter of van Genuchten's * curve. */ Scalar vgAlpha() const - { return vgAlpha_; } + { + return vgAlpha_; + } /*! * \brief Set the \f$\alpha\f$ shape parameter of van Genuchten's * curve. */ void setVgAlpha(Scalar v) - { vgAlpha_ = v; } + { + vgAlpha_ = v; + } /*! * \brief Return the \f$m\f$ shape parameter of van Genuchten's * curve. */ Scalar vgm() const - { return vgm_; } + { + return vgm_; + } DUNE_DEPRECATED_MSG("use vgm() (uncapitalized 'm') instead") Scalar vgM() const - { return vgm(); } + { + return vgm(); + } /*! * \brief Set the \f$m\f$ shape parameter of van Genuchten's @@ -101,22 +121,30 @@ public: * The \f$n\f$ shape parameter is set to \f$n = \frac{1}{1 - m}\f$ */ void setVgm(Scalar m) - { vgm_ = m; vgn_ = 1/(1 - vgm_); } + { + vgm_ = m; vgn_ = 1/(1 - vgm_); + } DUNE_DEPRECATED_MSG("use setVgm() (uncapitalized 'm') instead") void setVgM(Scalar m) - { setVgm(m); } + { + setVgm(m); + } /*! * \brief Return the \f$n\f$ shape parameter of van Genuchten's * curve. */ Scalar vgn() const - { return vgn_; } + { + return vgn_; + } DUNE_DEPRECATED_MSG("use vgn() (uncapitalized 'n') instead") Scalar vgN() const - { return vgn(); } + { + return vgn(); + } /*! * \brief Set the \f$n\f$ shape parameter of van Genuchten's @@ -125,11 +153,15 @@ public: * The \f$n\f$ shape parameter is set to \f$m = 1 - \frac{1}{n}\f$ */ void setVgn(Scalar n) - { vgn_ = n; vgm_ = 1 - 1/vgn_; } + { + vgn_ = n; vgm_ = 1 - 1/vgn_; + } DUNE_DEPRECATED_MSG("use setVgn() (uncapitalized 'n') instead") void setVgN(Scalar n) - { setVgn(n); } + { + setVgn(n); + } /*! * \brief Return the residual saturation. @@ -148,6 +180,7 @@ public: return sgr_; break; } + DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); } @@ -166,152 +199,213 @@ public: * \brief Return the residual wetting saturation. */ Scalar swr() const - { return swr_; } + { + return swr_; + } DUNE_DEPRECATED_MSG("use swr() (uncapitalized 's') instead") Scalar Swr() const - { return swr(); } + { + return swr(); + } /*! * \brief Set the residual wetting saturation. */ void setSwr(Scalar input) - { swr_ = input; } + { + swr_ = input; + } /*! * \brief Return the residual non-wetting saturation. */ Scalar snr() const - { return snr_; } + { + return snr_; + } DUNE_DEPRECATED_MSG("use snr() (uncapitalized 's') instead") Scalar Snr() const - { return snr(); } + { + return snr(); + } /*! * \brief Set the residual non-wetting saturation. */ void setSnr(Scalar input) - { snr_ = input; } + { + snr_ = input; + } /*! * \brief Return the residual gas saturation. */ Scalar sgr() const - { return sgr_; } + { + return sgr_; + } DUNE_DEPRECATED_MSG("use sgr() (uncapitalized 's') instead") Scalar Sgr() const - { return sgr(); } + { + return sgr(); + } /*! * \brief Set the residual gas saturation. */ void setSgr(Scalar input) - { sgr_ = input; } + { + sgr_ = input; + } Scalar swrx() const - { return swrx_; } + { + return swrx_; + } DUNE_DEPRECATED_MSG("use swrx() (uncapitalized 's') instead") Scalar Swrx() const - { return swrx(); } + { + return swrx(); + } /*! * \brief Set the residual gas saturation. */ void setSwrx(Scalar input) - { swrx_ = input; } + { + swrx_ = input; + } /*! * \brief defines the scaling parameters of capillary pressure between the phases (=1 for Gas-Water) */ void setBetaNw(Scalar input) - { betaNw_ = input; } + { + betaNw_ = input; + } DUNE_DEPRECATED_MSG("use setBetaNw() (uncapitalized 'w') instead") void setBetaNW(Scalar input) - { setBetaNw(input); } + { + setBetaNw(input); + } void setBetaGn(Scalar input) - { betaGn_ = input; } + { + betaGn_ = input; + } DUNE_DEPRECATED_MSG("use setBetaGn() (uncapitalized 'n') instead") void setBetaGN(Scalar input) - { setBetaGn(input); } + { + setBetaGn(input); + } void setBetaGw(Scalar input) - { betaGw_ = input; } + { + betaGw_ = input; + } DUNE_DEPRECATED_MSG("use setBetaGw() (uncapitalized 'w') instead") void setBetaGW(Scalar input) - { setBetaGw(input); } + { + setBetaGw(input); + } /*! * \brief Return the values for the beta scaling parameters of capillary pressure between the phases */ Scalar betaNw() const - { return betaNw_; } + { + return betaNw_; + } DUNE_DEPRECATED_MSG("use betaNw() (uncapitalized 'w') instead") Scalar betaNW() const - { return betaNw(); } + { + return betaNw(); + } Scalar betaGn() const - { return betaGn_; } + { + return betaGn_; + } DUNE_DEPRECATED_MSG("use betaGn() (uncapitalized 'n') instead") Scalar betaGN() const - { return betaGn(); } + { + return betaGn(); + } Scalar betaGw() const - { return betaGw_; } + { + return betaGw_; + } DUNE_DEPRECATED_MSG("use betaGw() (uncapitalized 'w') instead") Scalar betaGW() const - { return betaGw(); } + { + return betaGw(); + } /*! * \brief defines if residual n-phase saturation should be regarded in its relative permeability. */ void setKrRegardsSnr(bool input) - { krRegardsSnr_ = input; } + { + krRegardsSnr_ = input; + } DUNE_DEPRECATED_MSG("use setKrRegardsSnr() (capitalized 'K') instead") void setkrRegardsSnr(bool input) - { setKrRegardsSnr(input); } + { + setKrRegardsSnr(input); + } /*! * \brief Calls if residual n-phase saturation should be regarded in its relative permeability. */ bool krRegardsSnr() const - { return krRegardsSnr_; } + { + return krRegardsSnr_; + } /*! * \brief Return the bulk density of the porous medium */ Scalar rhoBulk() const - { return rhoBulk_; } + { + return rhoBulk_; + } /*! * \brief Set the bulk density of the porous medium */ void setRhoBulk(Scalar input) - { rhoBulk_ = input; } + { + rhoBulk_ = input; + } /*! * \brief Return the adsorption coefficient */ Scalar KdNAPL() const - { return KdNAPL_; } + { + return KdNAPL_; + } /*! * \brief Set the adsorption coefficient */ void setKdNAPL(Scalar input) - { KdNAPL_ = input; } - + { + KdNAPL_ = input; + } private: Scalar vgAlpha_; @@ -329,11 +423,11 @@ private: Scalar betaGn_; Scalar betaGw_; -Scalar TrappedSatN_; - + Scalar TrappedSatN_; bool krRegardsSnr_ ; }; -} // namespace Dumux -#endif +} // end namespace Dumux + +#endif // DUMUX_PARKERVANGENZEROHYST_PARAMS_3P_HH diff --git a/lecture/mm/heavyoil/3p/parkervangenuchtenzeroparams.hh b/lecture/mm/heavyoil/3p/parkervangenuchtenzeroparams.hh index 1c72f0ff3edb28b82c4eb8a1cd52e743298fe049..701006eeb6da2169b0c190c5b4dbb19ee3b7f4c0 100644 --- a/lecture/mm/heavyoil/3p/parkervangenuchtenzeroparams.hh +++ b/lecture/mm/heavyoil/3p/parkervangenuchtenzeroparams.hh @@ -30,8 +30,8 @@ #include -namespace Dumux -{ +namespace Dumux { + /*! * \brief Reference implementation of a van Genuchten params */ @@ -39,13 +39,21 @@ template class ParkerVanGenZero3PParams { public: - typedef ScalarT Scalar; + using Scalar = ScalarT; ParkerVanGenZero3PParams() {betaGw_ = betaNw_ = betaGn_ = 1.;} - ParkerVanGenZero3PParams(Scalar vgAlpha, Scalar vgn, Scalar KdNAPL, Scalar rhoBulk, Dune::FieldVector residualSaturation, Scalar betaNw = 1., Scalar betaGn = 1., Scalar betaGw = 1., bool regardSnr=false, - Scalar TrappedSatN = 1.) + ParkerVanGenZero3PParams(Scalar vgAlpha, + Scalar vgn, + Scalar KdNAPL, + Scalar rhoBulk, + Dune::FieldVector residualSaturation, + Scalar betaNw = 1., + Scalar betaGn = 1., + Scalar betaGw = 1., + bool regardSnr=false, + Scalar TrappedSatN = 1.) { setVgAlpha(vgAlpha); setVgn(vgn); @@ -64,7 +72,9 @@ public: }; Scalar TrappedSatN() const - { return TrappedSatN_; } + { + return TrappedSatN_; + } void setTrappedSatN(Scalar v) { TrappedSatN_ = v; } @@ -74,25 +84,33 @@ public: * curve. */ Scalar vgAlpha() const - { return vgAlpha_; } + { + return vgAlpha_; + } /*! * \brief Set the \f$\alpha\f$ shape parameter of van Genuchten's * curve. */ void setVgAlpha(Scalar v) - { vgAlpha_ = v; } + { + vgAlpha_ = v; + } /*! * \brief Return the \f$m\f$ shape parameter of van Genuchten's * curve. */ Scalar vgm() const - { return vgm_; } + { + return vgm_; + } DUNE_DEPRECATED_MSG("use vgm() (uncapitalized 'm') instead") Scalar vgM() const - { return vgm(); } + { + return vgm(); + } /*! * \brief Set the \f$m\f$ shape parameter of van Genuchten's @@ -101,22 +119,30 @@ public: * The \f$n\f$ shape parameter is set to \f$n = \frac{1}{1 - m}\f$ */ void setVgm(Scalar m) - { vgm_ = m; vgn_ = 1/(1 - vgm_); } + { + vgm_ = m; vgn_ = 1/(1 - vgm_); + } DUNE_DEPRECATED_MSG("use setVgm() (uncapitalized 'm') instead") void setVgM(Scalar m) - { setVgm(m); } + { + setVgm(m); + } /*! * \brief Return the \f$n\f$ shape parameter of van Genuchten's * curve. */ Scalar vgn() const - { return vgn_; } + { + return vgn_; + } DUNE_DEPRECATED_MSG("use vgn() (uncapitalized 'n') instead") Scalar vgN() const - { return vgn(); } + { + return vgn(); + } /*! * \brief Set the \f$n\f$ shape parameter of van Genuchten's @@ -125,11 +151,15 @@ public: * The \f$n\f$ shape parameter is set to \f$m = 1 - \frac{1}{n}\f$ */ void setVgn(Scalar n) - { vgn_ = n; vgm_ = 1 - 1/vgn_; } + { + vgn_ = n; vgm_ = 1 - 1/vgn_; + } DUNE_DEPRECATED_MSG("use setVgn() (uncapitalized 'n') instead") void setVgN(Scalar n) - { setVgn(n); } + { + setVgn(n); + } /*! * \brief Return the residual saturation. @@ -161,157 +191,216 @@ public: setSgr(residualSaturation[2]); } - /*! * \brief Return the residual wetting saturation. */ Scalar swr() const - { return swr_; } + { + return swr_; + } DUNE_DEPRECATED_MSG("use swr() (uncapitalized 's') instead") Scalar Swr() const - { return swr(); } + { + return swr(); + } /*! * \brief Set the residual wetting saturation. */ void setSwr(Scalar input) - { swr_ = input; } + { + swr_ = input; + } /*! * \brief Return the residual non-wetting saturation. */ Scalar snr() const - { return snr_; } + { + return snr_; + } DUNE_DEPRECATED_MSG("use snr() (uncapitalized 's') instead") Scalar Snr() const - { return snr(); } + { + return snr(); + } /*! * \brief Set the residual non-wetting saturation. */ void setSnr(Scalar input) - { snr_ = input; } + { + snr_ = input; + } /*! * \brief Return the residual gas saturation. */ Scalar sgr() const - { return sgr_; } + { + return sgr_; + } DUNE_DEPRECATED_MSG("use sgr() (uncapitalized 's') instead") Scalar Sgr() const - { return sgr(); } + { + return sgr(); + } /*! * \brief Set the residual gas saturation. */ void setSgr(Scalar input) - { sgr_ = input; } + { + sgr_ = input; + } Scalar swrx() const - { return swrx_; } + { + return swrx_; + } DUNE_DEPRECATED_MSG("use swrx() (uncapitalized 's') instead") Scalar Swrx() const - { return swrx(); } + { + return swrx(); + } /*! * \brief Set the residual gas saturation. */ void setSwrx(Scalar input) - { swrx_ = input; } + { + swrx_ = input; + } /*! * \brief defines the scaling parameters of capillary pressure between the phases (=1 for Gas-Water) */ void setBetaNw(Scalar input) - { betaNw_ = input; } + { + betaNw_ = input; + } DUNE_DEPRECATED_MSG("use setBetaNw() (uncapitalized 'w') instead") void setBetaNW(Scalar input) - { setBetaNw(input); } + { + setBetaNw(input); + } void setBetaGn(Scalar input) - { betaGn_ = input; } + { + betaGn_ = input; + } DUNE_DEPRECATED_MSG("use setBetaGn() (uncapitalized 'n') instead") void setBetaGN(Scalar input) - { setBetaGn(input); } + { + setBetaGn(input); + } void setBetaGw(Scalar input) - { betaGw_ = input; } + { + betaGw_ = input; + } DUNE_DEPRECATED_MSG("use setBetaGw() (uncapitalized 'w') instead") void setBetaGW(Scalar input) - { setBetaGw(input); } + { + setBetaGw(input); + } /*! * \brief Return the values for the beta scaling parameters of capillary pressure between the phases */ Scalar betaNw() const - { return betaNw_; } + { + return betaNw_; + } DUNE_DEPRECATED_MSG("use betaNw() (uncapitalized 'w') instead") Scalar betaNW() const - { return betaNw(); } + { + return betaNw(); + } Scalar betaGn() const - { return betaGn_; } + { + return betaGn_; + } DUNE_DEPRECATED_MSG("use betaGn() (uncapitalized 'n') instead") Scalar betaGN() const - { return betaGn(); } + { + return betaGn(); + } Scalar betaGw() const - { return betaGw_; } + { + return betaGw_; + } DUNE_DEPRECATED_MSG("use betaGw() (uncapitalized 'w') instead") Scalar betaGW() const - { return betaGw(); } + { + return betaGw(); + } /*! * \brief defines if residual n-phase saturation should be regarded in its relative permeability. */ void setKrRegardsSnr(bool input) - { krRegardsSnr_ = input; } + { + krRegardsSnr_ = input; + } DUNE_DEPRECATED_MSG("use setKrRegardsSnr() (capitalized 'K') instead") void setkrRegardsSnr(bool input) - { setKrRegardsSnr(input); } + { + setKrRegardsSnr(input); + } /*! * \brief Calls if residual n-phase saturation should be regarded in its relative permeability. */ bool krRegardsSnr() const - { return krRegardsSnr_; } - + { + return krRegardsSnr_; + } /*! * \brief Return the bulk density of the porous medium */ Scalar rhoBulk() const - { return rhoBulk_; } + { + return rhoBulk_; + } /*! * \brief Set the bulk density of the porous medium */ void setRhoBulk(Scalar input) - { rhoBulk_ = input; } + { + rhoBulk_ = input; + } /*! * \brief Return the adsorption coefficient */ Scalar KdNAPL() const - { return KdNAPL_; } + { + return KdNAPL_; + } /*! * \brief Set the adsorption coefficient */ void setKdNAPL(Scalar input) - { KdNAPL_ = input; } - + { + KdNAPL_ = input; + } private: Scalar vgAlpha_; @@ -329,11 +418,10 @@ private: Scalar betaGn_; Scalar betaGw_; -Scalar TrappedSatN_; - - + Scalar TrappedSatN_; bool krRegardsSnr_ ; }; + } // namespace Dumux -#endif +#endif // DUMUX_PARKERVANGENZERO_PARAMS_3P_HH diff --git a/lecture/mm/heavyoil/3p/relativepermeabilityhysteresis.hh b/lecture/mm/heavyoil/3p/relativepermeabilityhysteresis.hh index b0649d5e0bd5b75b10eaca532a5c6519a6b337f3..b2c41b8ff96de05da11cb569ca0a6f18b9f2faac 100644 --- a/lecture/mm/heavyoil/3p/relativepermeabilityhysteresis.hh +++ b/lecture/mm/heavyoil/3p/relativepermeabilityhysteresis.hh @@ -29,8 +29,8 @@ #include "relativepermeabilityhysteresisparams.hh" -namespace Dumux -{ +namespace Dumux { + /*! * \ingroup material * @@ -44,11 +44,9 @@ namespace Dumux template > class relativPermHyst3P { - public: - typedef ParamsT Params; - typedef typename Params::Scalar Scalar; - //typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + using Params = ParamsT; + using Scalar = typename Params::Scalar; static Scalar pc(const Params ¶ms, Scalar sw) { @@ -196,27 +194,18 @@ public: static Scalar s2Pr_w(const Params ¶ms,Scalar sw, Scalar sn, Scalar sg) { - // redSwrn = reduced two phase Residual Saturation (water and non-wetting) - //Scalar s2Pr_w_ = std::min(params.redSwrn(), params.redSwrg()); - Scalar s2Pr_w_ = ((sn - params.Snc()) * params.redSwrn() + ((sg - params.Sgc()) * params.redSwrg()))/ (1-sw-params.Snc()- params.Sgc()); - return s2Pr_w_; }; static Scalar s2Pr_n(const Params ¶ms,Scalar sw, Scalar sn, Scalar sg) { - // Scalar s2Pr_n_ = std::min(params.redSnrw(), params.redSnrg()); - Scalar s2Pr_n_ = ((sw - params.Swc()) * params.redSnrw() + ((sg - params.Sgc()) * params.redSnrg()))/ (1-sn-params.Swc()- params.Sgc()); return s2Pr_n_; }; static Scalar s2Pr_g(const Params ¶ms,Scalar sw, Scalar sn, Scalar sg) { - // redSwrn = reduced two phase Residual Saturation (water and non-wetting) - //Scalar s2Pr_g_ = std::min(params.redSgrw(),params.redSgrn()); - Scalar s2Pr_g_ = ((sw - params.Swc()) * params.redSgrw() + ((sn - params.Snc()) * params.redSgrn()))/ (1-sg-params.Swc()- params.Snc()); return s2Pr_g_; @@ -243,6 +232,7 @@ public: Scalar s3Pr_g_ = std::min(sg, std::max(params.Sgc(), s2Pr_g_ * (1-params.Bg()*(sn - params.Snc())*(sw-params.Sgc())))); return s3Pr_g_; }; + static Scalar krw(const Params ¶ms, Scalar sw, Scalar sn, Scalar sg) { Scalar C1_w_ = C1_w ( params, sw, sn, sg ); @@ -253,15 +243,11 @@ public: Scalar s3Pr_g_ = s3Pr_g ( params, sw, sn, sg ); Scalar normalSw = ((sw - std::min(sw, params.TrappedSatWx()))/(1-(s3Pr_w_ + s3Pr_n_ + s3Pr_g_))); //eq. 4 - //std::cout<<" TrappedSatWx " << params.TrappedSatWx() << std::endl; - //std::cout<<" TrappedSatNx " << params.TrappedSatNx() << std::endl; - //std::cout<<" LandBetaW " << params.LandBetaW() << std::endl; - //std::cout<<" TrappedSatGx " << params.TrappedSatGx() << std::endl; - Scalar krw_ = kr0_w_ * ((1+C2_w_)*std::pow(normalSw,C1_w_)) / (1+C2_w_*std::pow(normalSw,C1_w_*(1+(1/C2_w_)))); return krw_; }; + static Scalar krn(const Params ¶ms, Scalar sw, Scalar sn, Scalar sg) { Scalar C1_n_ = C1_n ( params, sw, sn, sg ); @@ -271,12 +257,12 @@ public: Scalar s3Pr_n_ = s3Pr_n ( params, sw, sn, sg ); Scalar s3Pr_g_ = s3Pr_g ( params, sw, sn, sg ); - Scalar normalSn = ((sn - std::min(sn, params.TrappedSatNx()))/(1-(s3Pr_w_ + s3Pr_n_ + s3Pr_g_))); //eq. 4 Scalar krn_ = kr0_n_ * ((1+C2_n_)*std::pow(normalSn,C1_n_)) / (1+C2_n_*std::pow(normalSn,C1_n_*(1+(1/C2_n_)))); return krn_; }; + static Scalar krg(const Params ¶ms, Scalar sw, Scalar sn, Scalar sg) { Scalar C1_g_ = C1_g ( params, sw, sn, sg ); @@ -306,6 +292,7 @@ public: return krg(params, sw, sn, sg); break; } + return 0; }; @@ -315,6 +302,7 @@ public: } }; -} -#endif +} // end namespace Dumux + +#endif // DUMUX_RELATIVEPERMHYST3P_HH diff --git a/lecture/mm/heavyoil/3p/relativepermeabilityhysteresisparams.hh b/lecture/mm/heavyoil/3p/relativepermeabilityhysteresisparams.hh index 647c08473fa4dd655d3ae0dcef8c962aba929fa8..feac5695ea5e669afc3711d91d6ca6983d459ba9 100644 --- a/lecture/mm/heavyoil/3p/relativepermeabilityhysteresisparams.hh +++ b/lecture/mm/heavyoil/3p/relativepermeabilityhysteresisparams.hh @@ -30,8 +30,7 @@ #include -namespace Dumux -{ +namespace Dumux { /*! * \brief Reference implementation of a van Genuchten params */ @@ -39,10 +38,12 @@ template class relativPermHyst3PParams { public: - typedef ScalarT Scalar; + using Scalar = ScalarT; relativPermHyst3PParams() - {betaGw_ = betaNw_ = betaGn_ = 1.;} + { + betaGw_ = betaNw_ = betaGn_ = 1.; + } relativPermHyst3PParams(Scalar vgAlpha, Scalar vgn, Scalar KdNAPL, Scalar rhoBulk, Dune::FieldVector residualSaturation, Scalar betaNw = 1., Scalar betaGn = 1., Scalar betaGw = 1., bool regardSnr=false, Scalar Bw= 1., Scalar Bn= 1., Scalar Bg= 1.,bool firstTimeStepindex=true, @@ -82,7 +83,6 @@ public: setredSgrw(redSgrw); setredSgrn(redSgrn); - setC1_wn(C1_wn); setC1_wg(C1_wg); setC1_ng(C1_ng); @@ -122,207 +122,369 @@ public: }; Scalar Swc() const - { return Swc_; } + { + return Swc_; + } + void setSwc(Scalar v) - { Swc_ = v; } + { + Swc_ = v; + } Scalar Snc() const - { return Snc_; } + { + return Snc_; + } + void setSnc(Scalar v) - { Snc_ = v; } + { + Snc_ = v; + } Scalar Sgc() const - { return Sgc_; } + { + return Sgc_; + } + void setSgc(Scalar v) - { Sgc_ = v; } + { + Sgc_ = v; + } Scalar TrappedSatWx() const - { return TrappedSatWx_; } + { + return TrappedSatWx_; + } + void setTrappedSatWx(Scalar v) - { TrappedSatWx_ = v; } + { + TrappedSatWx_ = v; + } Scalar TrappedSatNx() const - { return TrappedSatNx_; } + { + return TrappedSatNx_; + } + void setTrappedSatNx(Scalar v) - { TrappedSatNx_ = v; } + { + TrappedSatNx_ = v; + } Scalar TrappedSatGx() const - { return TrappedSatGx_; } - void setTrappedSatGx(Scalar v) - { TrappedSatGx_ = v; } - + { + return TrappedSatGx_; + } + void setTrappedSatGx(Scalar v) + { + TrappedSatGx_ = v; + } Scalar Bw() const - { return Bw_; } + { + return Bw_; + } + void setBw(Scalar v) - { Bw_ = v; } + { + Bw_ = v; + } + Scalar Bn() const - { return Bn_; } + { + return Bn_; + } + void setBn(Scalar v) - { Bn_ = v; } + { + Bn_ = v; + } + Scalar Bg() const - { return Bg_; } + { + return Bg_; + } + void setBg(Scalar v) - { Bg_ = v; } + { + Bg_ = v; + } Scalar redSwrn() const //1 - { return redSwrn_; } + { + return redSwrn_; + } + void setredSwrn(Scalar v) - { redSwrn_ = v; } + { + redSwrn_ = v; + } + Scalar redSwrg() const //2 - { return redSwrg_; } + { + return redSwrg_; + } void setredSwrg(Scalar v) - { redSwrg_ = v; } + { + redSwrg_ = v; + } Scalar redSnrw() const //3 - { return redSnrw_; } + { + return redSnrw_; + } void setredSnrw(Scalar v) - { redSnrw_ = v; } + { + redSnrw_ = v; + } Scalar redSnrg() const //4 - { return redSnrg_; } + { + return redSnrg_; + } void setredSnrg(Scalar v) - { redSnrg_ = v; } + { + redSnrg_ = v; + } Scalar redSgrw() const //5 - { return redSgrw_; } + { + return redSgrw_; + } void setredSgrw(Scalar v) - { redSgrw_ = v; } + { + redSgrw_ = v; + } Scalar redSgrn() const //6 - { return redSgrn_; } + { + return redSgrn_; + } void setredSgrn(Scalar v) - { redSgrn_ = v; } + { + redSgrn_ = v; + } Scalar C1_wn() const //1 - { return C1_wn_; } + { + return C1_wn_; + + } void setC1_wn(Scalar v) - { C1_wn_ = v; } + { + C1_wn_ = v; + } Scalar C1_wg() const //1 - { return C1_wg_; } + { + return C1_wg_; + } void setC1_wg(Scalar v) - { C1_wg_ = v; } + { + C1_wg_ = v; + } Scalar C1_ng() const //1 - { return C1_ng_; } + { + return C1_ng_; + } void setC1_ng(Scalar v) - { C1_ng_ = v; } + { + C1_ng_ = v; + } Scalar C2_wn() const //1 - { return C2_wn_; } + { + return C2_wn_; + } void setC2_wn(Scalar v) - { C2_wn_ = v; } + { + C2_wn_ = v; + } Scalar C2_wg() const //1 - { return C2_wg_; } + { + return C2_wg_; + } void setC2_wg(Scalar v) - { C2_wg_ = v; } + { + C2_wg_ = v; + } Scalar C2_ng() const //1 - { return C2_ng_; } + { + return C2_ng_; + } void setC2_ng(Scalar v) - { C2_ng_ = v; } + { + C2_ng_ = v; + } Scalar kr0_wn() const //1 - { return kr0_wn_; } + { + return kr0_wn_; + } void setkr0_wn(Scalar v) - { kr0_wn_ = v; } + { + kr0_wn_ = v; + } Scalar kr0_wg() const //1 - { return kr0_wg_; } + { + return kr0_wg_; + } void setkr0_wg(Scalar v) - { kr0_wg_ = v; } + { + kr0_wg_ = v; + } Scalar kr0_ng() const //1 - { return kr0_ng_; } + { + return kr0_ng_; + + } void setkr0_ng(Scalar v) - { kr0_ng_ = v; } + { + kr0_ng_ = v; + } Scalar SwMax() const - { return SwMax_; } + { + return SwMax_; + } void setSwMax(Scalar v) - { SwMax_ = v; } + { + SwMax_ = v; + } Scalar SnMax() const - { return SnMax_; } + { + return SnMax_; + } void setSnMax(Scalar v) - { SnMax_ = v; } + { + SnMax_ = v; + } Scalar SgMax() const - { return SgMax_; } + { + return SgMax_; + } void setSgMax(Scalar v) - { SgMax_ = v; } - + { + SgMax_ = v; + } Scalar LandAw() const - { return LandAw_; } + { + return LandAw_; + } void setLandAw(Scalar v) - { LandAw_ = v; } + { + LandAw_ = v; + } Scalar LandDw() const - { return LandDw_; } + { + return LandDw_; + } void setLandDw(Scalar v) - { LandDw_ = v; } + { + LandDw_ = v; + } Scalar LandAn() const - { return LandAn_; } + { + return LandAn_; + } void setLandAn(Scalar v) - { LandAn_ = v; } + { + LandAn_ = v; + } Scalar LandDn() const - { return LandDn_; } + { + return LandDn_; + } void setLandDn(Scalar v) - { LandDn_ = v; } + { + LandDn_ = v; + } Scalar LandAg() const - { return LandAg_; } + { + return LandAg_; + } void setLandAg(Scalar v) - { LandAg_ = v; } + { + LandAg_ = v; + } Scalar LandDg() const - { return LandDg_; } + { + return LandDg_; + } void setLandDg(Scalar v) - { LandDg_ = v; } - + { + LandDg_ = v; + } Scalar LandBetaW() const - { return LandBetaW_; } + { + return LandBetaW_; + } void setLandBetaW(Scalar v) - { LandBetaW_ = v; } + { + LandBetaW_ = v; + } Scalar LandBetaN() const - { return LandBetaN_; } + { + return LandBetaN_; + } void setLandBetaN(Scalar v) - { LandBetaN_ = v; } + { + LandBetaN_ = v; + } Scalar LandBetaG() const - { return LandBetaG_; } + { + return LandBetaG_; + } void setLandBetaG(Scalar v) - { LandBetaG_ = v; } - + { + LandBetaG_ = v; + } /*! * \brief Return the \f$\alpha\f$ shape parameter of van Genuchten's * curve. */ Scalar vgAlpha() const - { return vgAlpha_; } + { + return vgAlpha_; + } /*! * \brief Set the \f$\alpha\f$ shape parameter of van Genuchten's * curve. */ void setVgAlpha(Scalar v) - { vgAlpha_ = v; } + { + vgAlpha_ = v; + } /*! * \brief Return the \f$m\f$ shape parameter of van Genuchten's * curve. */ Scalar vgm() const - { return vgm_; } + { + return vgm_; + } DUNE_DEPRECATED_MSG("use vgm() (uncapitalized 'm') instead") Scalar vgM() const - { return vgm(); } + { + return vgm(); + } /*! * \brief Set the \f$m\f$ shape parameter of van Genuchten's @@ -331,22 +493,30 @@ public: * The \f$n\f$ shape parameter is set to \f$n = \frac{1}{1 - m}\f$ */ void setVgm(Scalar m) - { vgm_ = m; vgn_ = 1/(1 - vgm_); } + { + vgm_ = m; vgn_ = 1/(1 - vgm_); + } DUNE_DEPRECATED_MSG("use setVgm() (uncapitalized 'm') instead") void setVgM(Scalar m) - { setVgm(m); } + { + setVgm(m); + } /*! * \brief Return the \f$n\f$ shape parameter of van Genuchten's * curve. */ Scalar vgn() const - { return vgn_; } + { + return vgn_; + } DUNE_DEPRECATED_MSG("use vgn() (uncapitalized 'n') instead") Scalar vgN() const - { return vgn(); } + { + return vgn(); + } /*! * \brief Set the \f$n\f$ shape parameter of van Genuchten's @@ -355,23 +525,15 @@ public: * The \f$n\f$ shape parameter is set to \f$m = 1 - \frac{1}{n}\f$ */ void setVgn(Scalar n) - { vgn_ = n; vgm_ = 1 - 1/vgn_; } - - DUNE_DEPRECATED_MSG("use setVgn() (uncapitalized 'n') instead") - void setVgN(Scalar n) - { setVgn(n); } - - - /* void setFirstTimeStepindex(bool FirstTimeStepindex) { - FirstTimeStepindex_ = FirstTimeStepindex; + vgn_ = n; vgm_ = 1 - 1/vgn_; } - bool FirstTimeStepindex() const + DUNE_DEPRECATED_MSG("use setVgn() (uncapitalized 'n') instead") + void setVgN(Scalar n) { - return FirstTimeStepindex_; + setVgn(n); } -*/ /*! * \brief Return the residual saturation. @@ -390,6 +552,7 @@ public: return sgr_; break; } + DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); } @@ -408,162 +571,230 @@ public: * \brief Return the residual wetting saturation. */ Scalar swr() const - { return swr_; } + { + return swr_; + } DUNE_DEPRECATED_MSG("use swr() (uncapitalized 's') instead") Scalar Swr() const - { return swr(); } + { + return swr(); + } /*! * \brief Set the residual wetting saturation. */ void setSwr(Scalar input) - { swr_ = input; } + { + swr_ = input; + } /*! * \brief Return the residual non-wetting saturation. */ Scalar snr() const - { return snr_; } + { + return snr_; + } DUNE_DEPRECATED_MSG("use snr() (uncapitalized 's') instead") Scalar Snr() const - { return snr(); } + { + return snr(); + } /*! * \brief Set the residual non-wetting saturation. */ void setSnr(Scalar input) - { snr_ = input; } + { + snr_ = input; + } /*! * \brief Return the residual gas saturation. */ Scalar sgr() const - { return sgr_; } + { + return sgr_; + } DUNE_DEPRECATED_MSG("use sgr() (uncapitalized 's') instead") Scalar Sgr() const - { return sgr(); } + { + return sgr(); + } /*! * \brief Set the residual gas saturation. */ void setSgr(Scalar input) - { sgr_ = input; } + { + sgr_ = input; + } Scalar swrx() const - { return swrx_; } + { + return swrx_; + } DUNE_DEPRECATED_MSG("use swrx() (uncapitalized 's') instead") Scalar Swrx() const - { return swrx(); } + { + return swrx(); + } /*! * \brief Set the residual gas saturation. */ void setSwrx(Scalar input) - { swrx_ = input; } + { + swrx_ = input; + } /*! * \brief defines the scaling parameters of capillary pressure between the phases (=1 for Gas-Water) */ void setBetaNw(Scalar input) - { betaNw_ = input; } + { + betaNw_ = input; + } DUNE_DEPRECATED_MSG("use setBetaNw() (uncapitalized 'w') instead") void setBetaNW(Scalar input) - { setBetaNw(input); } + { + setBetaNw(input); + } void setBetaGn(Scalar input) - { betaGn_ = input; } + { + betaGn_ = input; + } DUNE_DEPRECATED_MSG("use setBetaGn() (uncapitalized 'n') instead") void setBetaGN(Scalar input) - { setBetaGn(input); } + { + setBetaGn(input); + } void setBetaGw(Scalar input) - { betaGw_ = input; } + { + betaGw_ = input; + } DUNE_DEPRECATED_MSG("use setBetaGw() (uncapitalized 'w') instead") void setBetaGW(Scalar input) - { setBetaGw(input); } + { + setBetaGw(input); + } /*! * \brief Return the values for the beta scaling parameters of capillary pressure between the phases */ Scalar betaNw() const - { return betaNw_; } + { + return betaNw_; + } DUNE_DEPRECATED_MSG("use betaNw() (uncapitalized 'w') instead") Scalar betaNW() const - { return betaNw(); } + { + return betaNw(); + } Scalar betaGn() const - { return betaGn_; } + { + return betaGn_; + } DUNE_DEPRECATED_MSG("use betaGn() (uncapitalized 'n') instead") Scalar betaGN() const - { return betaGn(); } + { + return betaGn(); + } Scalar betaGw() const - { return betaGw_; } + { + return betaGw_; + } DUNE_DEPRECATED_MSG("use betaGw() (uncapitalized 'w') instead") Scalar betaGW() const - { return betaGw(); } + { + return betaGw(); + } /*! * \brief defines if residual n-phase saturation should be regarded in its relative permeability. */ void setKrRegardsSnr(bool input) - { krRegardsSnr_ = input; } + { + krRegardsSnr_ = input; + } DUNE_DEPRECATED_MSG("use setKrRegardsSnr() (capitalized 'K') instead") void setkrRegardsSnr(bool input) - { setKrRegardsSnr(input); } + { + setKrRegardsSnr(input); + } /*! * \brief Calls if residual n-phase saturation should be regarded in its relative permeability. */ bool krRegardsSnr() const - { return krRegardsSnr_; } + { + return krRegardsSnr_; + } //---------------------------------------- void setFirstTimeStepindex(bool input) - { firstTimeStepindex_ = input; } + { + firstTimeStepindex_ = input; + } DUNE_DEPRECATED_MSG("use setFirstTimeStepindex() (capitalized 'F') instead") void setfirstTimeStepindex(bool input) - { setFirstTimeStepindex(input); } + { + setFirstTimeStepindex(input); + } bool firstTimeStepindex() const - { return firstTimeStepindex_; } + { + return firstTimeStepindex_; + } //---------------------------------------- /*! * \brief Return the bulk density of the porous medium */ Scalar rhoBulk() const - { return rhoBulk_; } + { + return rhoBulk_; + } /*! * \brief Set the bulk density of the porous medium */ void setRhoBulk(Scalar input) - { rhoBulk_ = input; } + { + rhoBulk_ = input; + } /*! * \brief Return the adsorption coefficient */ Scalar KdNAPL() const - { return KdNAPL_; } + { + return KdNAPL_; + } /*! * \brief Set the adsorption coefficient */ void setKdNAPL(Scalar input) - { KdNAPL_ = input; } + { + KdNAPL_ = input; + } private: @@ -626,6 +857,7 @@ private: Scalar Sgc_; }; -} // namespace Dumux -#endif +} // end namespace Dumux + +#endif // DUMUX_RELATIVEPERMHYST_PARAMS_3P_HH diff --git a/lecture/mm/heavyoil/sagd/CMakeLists.txt b/lecture/mm/heavyoil/sagd/CMakeLists.txt index 4639c54035b3e76f80565492aa4b2e9b3aa00bcc..1f614da268c41decdec7f160e193ab2154002db4 100644 --- a/lecture/mm/heavyoil/sagd/CMakeLists.txt +++ b/lecture/mm/heavyoil/sagd/CMakeLists.txt @@ -1,7 +1,6 @@ # headers for installation and headercheck -add_dumux_test(sagd sagd sagd.cc "${CMAKE_CURRENT_BINARY_DIR}/sagd" "-TimeManager.TEnd" "43200" "-TimeManager.MaxTimeStepSize" "21600") - +add_dumux_test(sagd sagd sagd.cc "${CMAKE_CURRENT_BINARY_DIR}/sagd" "-TimeLoop.TEnd" "43200" "-TimeLoop.MaxTimeStepSize" "21600") install(FILES sagdproblem.hh sagdspatialparams.hh diff --git a/lecture/mm/heavyoil/sagd/problem.hh b/lecture/mm/heavyoil/sagd/problem.hh index fd4c027b745d782e6783154e4d9e0a9a39c69f77..0bb354a4beb923ce9ebf148c8cde9a72b5225433 100644 --- a/lecture/mm/heavyoil/sagd/problem.hh +++ b/lecture/mm/heavyoil/sagd/problem.hh @@ -18,57 +18,70 @@ *****************************************************************************/ /*! * \file - * + * \ingroup ThreePWaterOilTests * \brief Non-isothermal SAGD problem - * */ #ifndef DUMUX_SAGDPROBLEM_HH #define DUMUX_SAGDPROBLEM_HH -#include +#include +#include + +#include #include -#include + +#include +#include +#include + #include "spatialparams.hh" -#define ISOTHERMAL 0 +namespace Dumux { -namespace Dumux -{ +/*! + * \file + * \ingroup ThreePWaterOilTests + * \brief Non-isothermal SAGD problem + */ template class SagdProblem; -namespace Properties -{ -NEW_TYPE_TAG(SagdProblem, INHERITS_FROM(ThreePWaterOilNI, SagdSpatialParams)); -NEW_TYPE_TAG(SagdBoxProblem, INHERITS_FROM(BoxModel, SagdProblem)); -NEW_TYPE_TAG(SagdCCProblem, INHERITS_FROM(CCModel, SagdProblem)); +namespace Properties { +NEW_TYPE_TAG(SagdTypeTag, INHERITS_FROM(ThreePWaterOilNI)); +NEW_TYPE_TAG(ThreePWaterOilSagdBoxTypeTag, INHERITS_FROM(BoxModel, SagdTypeTag)); // Set the grid type -SET_TYPE_PROP(SagdProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(SagdTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(SagdProblem, Problem, Dumux::SagdProblem); +SET_TYPE_PROP(SagdTypeTag, Problem, Dumux::SagdProblem); + +// Set the spatial parameters +SET_PROP(SagdTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = SagdSpatialParams; +}; // Set the fluid system -SET_TYPE_PROP(SagdProblem, +SET_TYPE_PROP(SagdTypeTag, FluidSystem, - Dumux::FluidSystems::H2OHeavyOil); - + Dumux::FluidSystems::H2OHeavyOil); -// Enable gravity -SET_BOOL_PROP(SagdProblem, ProblemEnableGravity, true); +SET_BOOL_PROP(SagdTypeTag, OnlyGasPhaseCanDisappear, true); -// Use forward differences instead of central differences -SET_INT_PROP(SagdProblem, ImplicitNumericDifferenceMethod, +1); +SET_BOOL_PROP(SagdTypeTag, UseMoles, true); -// Write newton convergence -SET_BOOL_PROP(SagdProblem, NewtonWriteConvergence, false); - -SET_BOOL_PROP(SagdProblem, UseSimpleModel, true); - -SET_BOOL_PROP(SagdProblem, UseMassOutput, true); -} +// Set the fluid system +SET_PROP(SagdTypeTag, SolidSystem) +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using InertComponent = Components::Constant<1, Scalar>; + using type = SolidSystems::InertSolidPhase; +}; +} // end namespace Properties /*! @@ -80,189 +93,54 @@ SET_BOOL_PROP(SagdProblem, UseMassOutput, true); * * */ template -class SagdProblem : public ImplicitPorousMediaProblem +class SagdProblem : public PorousMediumFlowProblem { - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GridView::Grid Grid; - - typedef ImplicitPorousMediaProblem ParentType; - - // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + using ParentType = PorousMediumFlowProblem; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; enum { pressureIdx = Indices::pressureIdx, switch1Idx = Indices::switch1Idx, switch2Idx = Indices::switch2Idx, + contiWEqIdx = Indices::conti0EqIdx + FluidSystem::wCompIdx, + contiNEqIdx = Indices::conti0EqIdx + FluidSystem::nCompIdx, energyEqIdx = Indices::energyEqIdx, - // phase and component indices - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - gPhaseIdx = Indices::gPhaseIdx, - wCompIdx = Indices::wCompIdx, - nCompIdx = Indices::nCompIdx, + // phase indices + wPhaseIdx = FluidSystem::wPhaseIdx, + nPhaseIdx = FluidSystem::nPhaseIdx, - // Phase State - wPhaseOnly = Indices::wPhaseOnly, + // phase state wnPhaseOnly = Indices::wnPhaseOnly, - wgPhaseOnly = Indices::wgPhaseOnly, - threePhases = Indices::threePhases, - //contiWEqIdx = Indices::contiWEqIdx, - //contiNEqIdx = Indices::contiNEqIdx, - // Grid and world dimension - dim = GridView::dimension, + // world dimension dimWorld = GridView::dimensionworld }; - - 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 GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - - typedef Dune::FieldVector GlobalPosition; - - enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using GlobalPosition = typename SubControlVolumeFace::GlobalPosition; public: - /*! - * \brief The constructor - * - * \param timeManager The time manager - * \param gridView The grid view - */ - - SagdProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-6), pOut_(4e6) + SagdProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - maxDepth_ = 400.0; // [m] FluidSystem::init(); - totalMassProducedOil_ =0; - totalMassProducedWater_ =0; - - this->timeManager().startNextEpisode(86400); - - name_ = GET_RUNTIME_PARAM(TypeTag, std::string, Problem.Name); - } - - bool shouldWriteRestartFile() const - { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().timeStepIndex() % 10000 == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } - - bool shouldWriteOutput() const - { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().timeStepIndex() % 10000 == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } - - /*! - * \brief Called directly after the time integration. - */ - - - void postTimeStep() - { - double time = this->timeManager().time(); - double dt = this->timeManager().timeStepSize(); - - // Calculate storage terms - PrimaryVariables storage; - this->model().globalStorage(storage); - - // Write mass balance information for rank 0 - if (this->gridView().comm().rank() == 0) - { - std::cout<<"Storage: " << storage << "Time: " << time+dt << std::endl; - massBalance.open ("massBalance.txt", std::ios::out | std::ios::app ); - massBalance << " Storage " << storage - << " Time " << time+dt - << std::endl; - massBalance.close(); - - } - - // Calculate storage terms - PrimaryVariables storageW, storageN; - //Dune::FieldVector flux(0.0); - this->model().globalPhaseStorage(storageW, wPhaseIdx); - this->model().globalPhaseStorage(storageN, nPhaseIdx); - - //mass of Oil - const Scalar newMassProducedOil_ = massProducedOil_; - std::cout<<" newMassProducedOil_ : "<< newMassProducedOil_ << " Time: " << time+dt << std::endl; - - totalMassProducedOil_ += newMassProducedOil_; - std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; - //mass of Water - const Scalar newMassProducedWater_ = massProducedWater_; - //std::cout<<" newMassProducedWater_ : "<< newMassProducedWater_ << " Time: " << time+dt << std::endl; - - totalMassProducedWater_ += newMassProducedWater_; - //std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; - - - const int timeStepIndex = this->timeManager().timeStepIndex(); - - if (timeStepIndex == 0 || - timeStepIndex % 100 == 0 || //after every 1000000 secs - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished()) - { - std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; - std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; - } - } - - void episodeEnd() - { - // Start new episode if episode is over - // for first 10 year episode length is 1 year - this->timeManager().startNextEpisode(3600*24*1.); //episode length sent to 1 day - std::cout<<"Episode index is set to: "<timeManager().episodeIndex()< 8.5 + eps_ && globalPos[1] < 9.5 - eps_) + if (globalPos[1] > 8.5 - eps_ && globalPos[1] < 9.5 + eps_) { - values[Indices::contiNEqIdx] = -0.0; - values[Indices::contiWEqIdx] = -0.193;//*0.5; // (55.5 mol*12.5)/3600 mol/s m = 0.193 - values[Indices::energyEqIdx] = -9132;//*0.5; // J/sec m 9132 + values[contiNEqIdx] = -0.0; + values[contiWEqIdx] = -0.193;//*0.5; // (55.5 mol*12.5)/3600 mol/s m = 0.193 + values[energyEqIdx] = -9132;//*0.5; // J/sec m 9132 } - else if (globalPos[1] > 2.5 + eps_ && globalPos[1] < 3.5 - eps_) // production well + else if (globalPos[1] > 2.5 - eps_ && globalPos[1] < 3.5 + eps_) // production well { - //Scalar satWBound = 1.0; - // const Scalar satW = elemVolVars[scvIdx].saturation(wPhaseIdx); // Saturations - // const Scalar satG = elemVolVars[scvIdx].saturation(gPhaseIdx); - // const Scalar satN = elemVolVars[scvIdx].saturation(nPhaseIdx); - - const Scalar elemPressW = elemVolVars[scvIdx].pressure(wPhaseIdx); //Pressures - // const Scalar elemPressG = elemVolVars[scvIdx].pressure(gPhaseIdx); - const Scalar elemPressN = elemVolVars[scvIdx].pressure(nPhaseIdx); - - const Scalar densityW = elemVolVars[scvIdx].fluidState().density(wPhaseIdx); //Densities - // const Scalar densityG = elemVolVars[scvIdx].fluidState().density(gPhaseIdx); - const Scalar densityN = elemVolVars[scvIdx].fluidState().density(nPhaseIdx); - - // const Scalar moDensityW = elemVolVars[scvIdx].fluidState().molarDensity(wPhaseIdx); //Molar Densities - // const Scalar moDensityG = elemVolVars[scvIdx].fluidState().molarDensity(gPhaseIdx); - // const Scalar moDensityN = elemVolVars[scvIdx].fluidState().molarDensity(nPhaseIdx); + const Scalar elemPressW = elemVolVars[scvf.insideScvIdx()].pressure(wPhaseIdx); //Pressures + const Scalar elemPressN = elemVolVars[scvf.insideScvIdx()].pressure(nPhaseIdx); + const Scalar densityW = elemVolVars[scvf.insideScvIdx()].fluidState().density(wPhaseIdx); //Densities + const Scalar densityN = elemVolVars[scvf.insideScvIdx()].fluidState().density(nPhaseIdx); - // const Scalar maDensityW = elemVolVars[scvIdx].fluidState().density(wPhaseIdx); //Molar Densities - // const Scalar maDensityG = elemVolVars[scvIdx].fluidState().density(gPhaseIdx); - // const Scalar maDensityN = elemVolVars[scvIdx].fluidState().density(nPhaseIdx); + const Scalar elemMobW = elemVolVars[scvf.insideScvIdx()].mobility(wPhaseIdx); //Mobilities + const Scalar elemMobN = elemVolVars[scvf.insideScvIdx()].mobility(nPhaseIdx); - const Scalar elemMobW = elemVolVars[scvIdx].mobility(wPhaseIdx); //Mobilities - // const Scalar elemMobG = elemVolVars[scvIdx].mobility(gPhaseIdx); - const Scalar elemMobN = elemVolVars[scvIdx].mobility(nPhaseIdx); - - // const Scalar molFracWinW = elemVolVars[scvIdx].fluidState().moleFraction(wPhaseIdx, wCompIdx); - // const Scalar molFracWinN = elemVolVars[scvIdx].fluidState().moleFraction(nPhaseIdx, wCompIdx); - // const Scalar molFracWinG = elemVolVars[scvIdx].fluidState().moleFraction(gPhaseIdx, wCompIdx); - // const Scalar molFracNinW = elemVolVars[scvIdx].fluidState().moleFraction(wPhaseIdx, nCompIdx); - // const Scalar molFracNinN = elemVolVars[scvIdx].fluidState().moleFraction(nPhaseIdx, nCompIdx); - // const Scalar molFracNinG = elemVolVars[scvIdx].fluidState().moleFraction(gPhaseIdx, nCompIdx); - - const Scalar enthW = elemVolVars[scvIdx].enthalpy(wPhaseIdx); //Mobilities - // const Scalar enthG = elemVolVars[scvIdx].enthalpy(gPhaseIdx); - const Scalar enthN = elemVolVars[scvIdx].enthalpy(nPhaseIdx); + const Scalar enthW = elemVolVars[scvf.insideScvIdx()].enthalpy(wPhaseIdx); //Enthalpies + const Scalar enthN = elemVolVars[scvf.insideScvIdx()].enthalpy(nPhaseIdx); const Scalar wellRadius = 0.50 * 0.3048; // 0.50 ft as specified by SPE9 - // const Scalar wellArea = M_PI*std::pow(wellRadius,2); // [m^2] - const Scalar gridHeight_ = 0.5; - const Scalar effectiveRadius_ = 0.208 * gridHeight_; //Peaceman's Well Model - // const Scalar effectiveRadius_ = 0.56; + const Scalar gridHeight = 0.5; + const Scalar effectiveRadius = 0.208 * gridHeight; //Peaceman's Well Model + const Scalar pOut = 4e6; + using std::log; //divided by molarMass() of water to convert from kg/m s to mol/m s - const Scalar qW = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * - densityW * elemMobW * ( elemPressW-pOut_))/0.01801528; + const Scalar qW = (((2*3.1415*0.5*4e-14)/(log(effectiveRadius/wellRadius))) * + densityW * elemMobW * ( elemPressW-pOut))/0.01801528; //divided by molarMass() of HeavyOil to convert from kg/m s to mol/m s - const Scalar qN = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * - densityN * elemMobN * (elemPressN-pOut_))/0.35; + const Scalar qN = (((2*3.1415*0.5*4e-14)/(log(effectiveRadius/wellRadius))) * + densityN * elemMobN * (elemPressN-pOut))/0.35; + Scalar qE; //without cooling: - // const Scalar qE = qW*0.018*enthW + qN*enthN*0.350; + // qE = qW*0.018*enthW + qN*enthN*0.350; //with cooling: see Diplomarbeit Stefan Roll, Sept. 2015 - Scalar wT = elemVolVars[scvIdx].temperature(); // well temperature - Scalar qE; + Scalar wT = elemVolVars[scvf.insideScvIdx()].temperature(); // well temperature if ( wT > 495. ) { qE = qW*0.018*enthW + qN*enthN*0.350 + (wT-495.)*5000.; // ~3x injected enthalpy std::cout<< "Cooling now! Extracted enthalpy: " << qE << std::endl; - } else { + } + else qE = qW*0.018*enthW + qN*enthN*0.350; - } - - values[Indices::contiWEqIdx] = qW; - values[Indices::contiNEqIdx] = qN; - values[Indices::energyEqIdx] = qE; + values[contiWEqIdx] = qW; + values[contiNEqIdx] = qN; + values[energyEqIdx] = qE; massProducedOil_ = qN; massProducedWater_ = qW; } + return values; } // \} @@ -434,70 +275,71 @@ public: /*! * \brief Evaluate the initial value for a control volume. * - * \param values The initial values for the primary variables * \param globalPos The position for which the initial condition should be evaluated - * - * For this method, the \a values parameter stores primary - * variables. */ - void initialAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - initial_(values, globalPos); + return initial_(globalPos); } - /*! - * \brief Return the initial phase state inside a control volume. - * - * \param globalPos The global position - */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const + template + void postTimeStep(const TimeLoop& timeLoop, const PrimaryVariables& storage, const SolutionVector& x) { - return wnPhaseOnly; - //return threePhases; - } + const Scalar time = timeLoop.time(); + const Scalar dt = timeLoop.timeStepSize(); - void setMassProd (Scalar value) const - { - massProducedOil_ = value; - } + // print storage terms + std::cout<<"Storage: " << storage << " Time: " << time+dt << std::endl; + massBalance.open ("massBalance.txt", std::ios::out | std::ios::app ); + massBalance << " Storage " << storage + << " Time " << time+dt + << std::endl; + massBalance.close(); - Scalar getMassProd () - { - return massProducedOil_; - } + //mass of Oil + Scalar newMassProducedOil = massProducedOil_; + //mass of Water + Scalar newMassProducedWater = massProducedWater_; + totalMassProducedOil_ += newMassProducedOil; + totalMassProducedWater_ += newMassProducedWater; + const int timeStepIndex = timeLoop.timeStepIndex(); + if (timeStepIndex == 0 || + timeStepIndex % 100 == 0 || //after every 1000000 secs + timeLoop.willBeFinished()) + + { + std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; + std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; + } + } private: // internal method for the initial condition (reused for the // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initial_(const GlobalPosition &globalPos) const { + PrimaryVariables values(0.0); + values.setState(wnPhaseOnly); Scalar densityW = 1000.0; values[pressureIdx] = 101300.0 + (maxDepth_ - globalPos[1])*densityW*9.81; values[switch1Idx] = 295.13; // temperature values[switch2Idx] = 0.3; //NAPL saturation + return values; } Scalar maxDepth_; - Scalar eps_; - Scalar pIn_; - Scalar pOut_; + static constexpr Scalar eps_ = 1e-6; + std::string name_; + + int episodeIdx_; + Scalar totalMassProducedOil_; Scalar totalMassProducedWater_; - - // TODO this is a very evil hack mutable Scalar massProducedOil_; mutable Scalar massProducedWater_; - //Scalar maxDepth_; - //Scalar episodeLength_; - //Scalar nEpisodes_ ; - //int indexEpisode; - //int episodeType_; - std::string name_; - std::ofstream massBalance; }; } //end namespace diff --git a/lecture/mm/heavyoil/sagd/sagd.cc b/lecture/mm/heavyoil/sagd/sagd.cc index 6809c39f715cedaccfbcc3c7915c3109abad4dbd..dbb7bf55e547f6dbddedcfa470fb86fe34e2e09e 100644 --- a/lecture/mm/heavyoil/sagd/sagd.cc +++ b/lecture/mm/heavyoil/sagd/sagd.cc @@ -18,19 +18,41 @@ *****************************************************************************/ /*! * \file - * - * \brief test for the 3p3cni box model + * \ingroup OnePTests + * \brief Test for the three-phase three-component box model */ -#include "config.h" -#include "problem.hh" -#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "problem.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. + * \ingroup OnePTests * \param errorMsg The error message that was issued by the start function. * Comprises the thing that went wrong and a general help message. */ @@ -42,22 +64,171 @@ void usage(const char *progName, const std::string &errorMsg) 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 DGF format\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 DGF format\n"; std::cout << errorMessageOut << "\n"; } } -int main(int argc, char** argv) +int main(int argc, char** argv) try { + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(ThreePWaterOilSagdBoxTypeTag); + + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + VtkOutputFields::init(vtkWriter); //! Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(0, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength", std::numeric_limits::max())); - typedef TTAG(SagdBoxProblem) ProblemTypeTag; - //typedef TTAG(SagdCCProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + // the linear solver + using LinearSolver = Dumux::AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + PrimaryVariables storage(0); + const auto& localResidual = assembler->localResidual(); + for (const auto& element : elements(leafGridView, Dune::Partitions::interior)) + { + auto storageVec = localResidual.evalStorage(*problem, element, *fvGridGeometry, *gridVariables, x); + storage += storageVec[0]; + } + problem->postTimeStep(*timeLoop, storage, x); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + +} + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) +{ + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/heavyoil/sagd/sagd.input b/lecture/mm/heavyoil/sagd/sagd.input index 1863a0b24e5c35057ebe1b8dec5c40263d15fa37..10b4ff7e7dbddcbc146132842006f4b3edefef7a 100644 --- a/lecture/mm/heavyoil/sagd/sagd.input +++ b/lecture/mm/heavyoil/sagd/sagd.input @@ -1,4 +1,4 @@ -[TimeManager] +[TimeLoop] DtInitial = 1800 # [s] TEnd = 31467600 # [s] a year MaxTimeStepSize = 1800 # [s] @@ -14,6 +14,10 @@ Name = sagd # name passed to the output routines MaxTimeStepDivisions = 100 [Newton] -RelTolerance = 1e-02 +MaxRelativeShift = 1.e-2 MaxSteps = 8 -WriteConvergence = 0 + +[Component] +SolidDensity = 2650 +SolidThermalConductivity = 2.8 +SolidHeatCapacity = 850 diff --git a/lecture/mm/heavyoil/sagd/spatialparams.hh b/lecture/mm/heavyoil/sagd/spatialparams.hh index 034a607b8f3de8e669cc796736e81948c2a403be..1cd28b924d96b3a961dd93857e8d733597a1e640 100644 --- a/lecture/mm/heavyoil/sagd/spatialparams.hh +++ b/lecture/mm/heavyoil/sagd/spatialparams.hh @@ -18,93 +18,60 @@ *****************************************************************************/ /*! * \file - * + * \ingroup ThreePWaterOilTests * \brief Definition of the spatial parameters for the SAGD problem. */ #ifndef DUMUX_SAGD_SPATIAL_PARAMS_HH #define DUMUX_SAGD_SPATIAL_PARAMS_HH -#include +#include +#include #include -#include "../3p/parkervangenuchtenzero.hh" -#include "../3p/parkervangenuchtenzeroparams.hh" - -namespace Dumux -{ - -//forward declaration -template -class SagdSpatialParams; - -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(SagdSpatialParams); -// Set the spatial parameters -SET_TYPE_PROP(SagdSpatialParams, SpatialParams, Dumux::SagdSpatialParams); +#include +#include +#include -// Set the material Law -SET_PROP(SagdSpatialParams, MaterialLaw) -{ - private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - public: - typedef ParkerVanGenZero3P type; -}; -} +namespace Dumux { /*! * \ingroup ThreePThreeCNIModel * * \brief Definition of the spatial parameters for the SAGD problem */ -template -class SagdSpatialParams : public ImplicitSpatialParams +template +class SagdSpatialParams +: public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; + using GridView = typename FVGridGeometry::GridView; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx - }; + enum { dimWorld=GridView::dimensionworld }; - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldVector DimVector; + using GlobalPosition = typename SubControlVolume::GlobalPosition; + using Element = typename GridView::template Codim<0>::Entity; + using ParentType = FVSpatialParams>; - 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, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; - + using EffectiveLaw = RegularizedParkerVanGen3P; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; + using PermeabilityType = Scalar; /*! * \brief The constructor * * \param gridView The grid view */ - SagdSpatialParams(const GridView &gridView) - : ParentType(gridView) + SagdSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry), eps_(1e-6) { layerBottom_ = 35.0; @@ -116,149 +83,101 @@ public: finePorosity_ = 0.10; coarsePorosity_ = 0.1; - // heat conductivity of granite - lambdaSolid_ = 2.8; - - // specific heat capacities - fineHeatCap_ = 850.; - coarseHeatCap_ = 850; - // residual saturations fineMaterialParams_.setSwr(0.1); - fineMaterialParams_.setSwrx(0.12); //Total liquid Residual Saturation fineMaterialParams_.setSnr(0.09); //Residual of NAPL if there is no water fineMaterialParams_.setSgr(0.01); coarseMaterialParams_.setSwr(0.1); - coarseMaterialParams_.setSwrx(0.12); coarseMaterialParams_.setSnr(0.09); coarseMaterialParams_.setSgr(0.01); // parameters for the 3phase van Genuchten law fineMaterialParams_.setVgn(4.0); coarseMaterialParams_.setVgn(4.0); - fineMaterialParams_.setVgAlpha(0.0005); - coarseMaterialParams_.setVgAlpha(0.0015); + fineMaterialParams_.setVgAlpha(1.); + coarseMaterialParams_.setVgAlpha(1.); coarseMaterialParams_.setKrRegardsSnr(false); fineMaterialParams_.setKrRegardsSnr(false); } /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. + * \brief Function for defining the (intrinsic) permeability \f$[m^2]\f$ + * \note It is possibly solution dependent. * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element. + * \return permeability */ - Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineK_; - return coarseK_; - } + template + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { return permeabilityAtPos(scv.dofPosition());} /*! - * \brief Define the porosity \f$[-]\f$ of the spatial parameters + * \brief Returns the intrinsic permeability tensor \f$[m^2]\f$ * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined + * \param globalPos The global position */ - Scalar porosity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return finePorosity_; - else - return coarsePorosity_; + if (isFineMaterial_(globalPos)) + return fineK_; + return coarseK_; } - /*! - * \brief return the parameter object for the Brooks-Corey material law which depends on the position + * \brief Returns the porosity \f$[-]\f$ * - * \param element The current finite element - * \param fvGeometry The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param globalPos The global position */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + Scalar porosityAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineMaterialParams_; - else - return coarseMaterialParams_; - } - - /*! - * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidHeatCapacity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineHeatCap_; + if (isFineMaterial_(globalPos)) + return finePorosity_; else - return coarseHeatCap_; + return coarsePorosity_; } /*! - * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. + * \brief Function for defining the parameters needed by constitutive relationships (kr-sw, pc-sw, etc.). * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element. + * \return the material parameters object */ - Scalar solidDensity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + template + const MaterialLawParams& materialLawParams(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { - return 2650; // density of sand [kg/m^3] + return materialLawParamsAtPos(scv.dofPosition()); } /*! - * \brief Returns the thermal conductivity \f$\mathrm{[W/(m K)]}\f$ of the solid - * - * This is only required for non-isothermal models. + * \brief Returns the parameter object for the capillary-pressure/ + * saturation material law * - * \param element The finite element - * \param fvGeometry The finite volume geometry of the element - * \param scvIdx The local index of the sub-control volume + * \param globalPos The global position */ - Scalar solidThermalConductivity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { - return lambdaSolid_; + if (isFineMaterial_(globalPos)) + return fineMaterialParams_; + else + return coarseMaterialParams_; } private: bool isFineMaterial_(const GlobalPosition &pos) const { - return pos[dim-1] > layerBottom_ - eps_; - } + return pos[dimWorld-1] > layerBottom_ - eps_; + }; Scalar layerBottom_; - Scalar lambdaSolid_; Scalar fineK_; Scalar coarseK_; @@ -266,13 +185,10 @@ private: Scalar finePorosity_; Scalar coarsePorosity_; - Scalar fineHeatCap_; - Scalar coarseHeatCap_; - MaterialLawParams fineMaterialParams_; MaterialLawParams coarseMaterialParams_; - static constexpr Scalar eps_ = 1.5e-7; + Scalar eps_; }; } diff --git a/lecture/mm/heavyoil/sagdcyclic/CMakeLists.txt b/lecture/mm/heavyoil/sagdcyclic/CMakeLists.txt index 98eb6dd8eb338018e954c7ae9a50f9223fd75b09..90a3e0888a93c60acedf28fac67e56d032ad46ac 100644 --- a/lecture/mm/heavyoil/sagdcyclic/CMakeLists.txt +++ b/lecture/mm/heavyoil/sagdcyclic/CMakeLists.txt @@ -1,6 +1,6 @@ # headers for installation and headercheck -add_dumux_test(sagd_cyclic sagd_cyclic sagd_cyclic.cc "${CMAKE_CURRENT_BINARY_DIR}/sagd_cyclic" "-TimeManager.TEnd" "43200" "-TimeManager.MaxTimeStepSize" "21600") +add_dumux_test(sagd_cyclic sagd_cyclic sagd_cyclic.cc "${CMAKE_CURRENT_BINARY_DIR}/sagd_cyclic" "-TimeLoop.TEnd" "43200" "-TimeLoop.MaxTimeStepSize" "21600") install(FILES problem.hh diff --git a/lecture/mm/heavyoil/sagdcyclic/problem.hh b/lecture/mm/heavyoil/sagdcyclic/problem.hh index cf9928cf99935b33b85af9d6c75a6bfb45db64b1..6e25f04b144a9342fd79b91622601eef61c1b311 100644 --- a/lecture/mm/heavyoil/sagdcyclic/problem.hh +++ b/lecture/mm/heavyoil/sagdcyclic/problem.hh @@ -18,59 +18,70 @@ *****************************************************************************/ /*! * \file - * + * \ingroup ThreePWaterOilTests * \brief Non-isothermal SAGD problem - * */ - #ifndef DUMUX_SAGDCYCLICPROBLEM_HH #define DUMUX_SAGDCYCLICPROBLEM_HH -#include +#include + +#include +#include #include -#include + +#include +#include +#include + #include "spatialparams.hh" -#define ISOTHERMAL 0 +namespace Dumux { -namespace Dumux -{ +/*! + * \file + * \ingroup ThreePWaterOilTests + * \brief Non-isothermal SAGD problem + */ template class SagdCyclicProblem; -namespace Properties -{ -NEW_TYPE_TAG(SagdCyclicProblem, INHERITS_FROM(ThreePWaterOilNI, SagdCyclicSpatialParams)); - -NEW_TYPE_TAG(SagdCyclicBoxProblem, INHERITS_FROM(BoxModel, SagdCyclicProblem)); - -NEW_TYPE_TAG(SagdCyclicCCProblem, INHERITS_FROM(CCModel, SagdCyclicProblem)); +namespace Properties { +NEW_TYPE_TAG(SagdTypeTag, INHERITS_FROM(ThreePWaterOilNI)); +NEW_TYPE_TAG(ThreePWaterOilSagdCyclicBoxTypeTag, INHERITS_FROM(BoxModel, SagdTypeTag)); // Set the grid type -SET_TYPE_PROP(SagdCyclicProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(SagdTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(SagdCyclicProblem, Problem, Dumux::SagdCyclicProblem); +SET_TYPE_PROP(SagdTypeTag, Problem, Dumux::SagdCyclicProblem); + +// Set the spatial parameters +SET_PROP(SagdTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = SagdSpatialParams; +}; // Set the fluid system -SET_TYPE_PROP(SagdCyclicProblem, +SET_TYPE_PROP(SagdTypeTag, FluidSystem, - Dumux::FluidSystems::H2OHeavyOil); -// Enable gravity -SET_BOOL_PROP(SagdCyclicProblem, ProblemEnableGravity, true); - -// Use forward differences instead of central differences -SET_INT_PROP(SagdCyclicProblem, ImplicitNumericDifferenceMethod, +1); + Dumux::FluidSystems::H2OHeavyOil); -// Write newton convergence -SET_BOOL_PROP(SagdCyclicProblem, NewtonWriteConvergence, false); +SET_BOOL_PROP(SagdTypeTag, OnlyGasPhaseCanDisappear, true); -SET_BOOL_PROP(SagdCyclicProblem, UseSimpleModel, true); +SET_BOOL_PROP(SagdTypeTag, UseMoles, true); -// Set the maximum time step -//SET_SCALAR_PROP(SagdCyclicProblem, TimeManagerMaxTimeStepvectorSize, 4.); -} +// Set the fluid system +SET_PROP(SagdTypeTag, SolidSystem) +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using InertComponent = Components::Constant<1, Scalar>; + using type = SolidSystems::InertSolidPhase; +}; +} // end namespace Properties /*! @@ -82,185 +93,61 @@ SET_BOOL_PROP(SagdCyclicProblem, UseSimpleModel, true); * * */ template -class SagdCyclicProblem : public ImplicitPorousMediaProblem +class SagdCyclicProblem : public PorousMediumFlowProblem { - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GridView::Grid Grid; - - typedef ImplicitPorousMediaProblem ParentType; - - // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + using ParentType = PorousMediumFlowProblem; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; enum { pressureIdx = Indices::pressureIdx, switch1Idx = Indices::switch1Idx, switch2Idx = Indices::switch2Idx, + contiWEqIdx = Indices::conti0EqIdx + FluidSystem::wCompIdx, + contiNEqIdx = Indices::conti0EqIdx + FluidSystem::nCompIdx, energyEqIdx = Indices::energyEqIdx, - // phase and component indices - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - gPhaseIdx = Indices::gPhaseIdx, - wCompIdx = Indices::wCompIdx, - nCompIdx = Indices::nCompIdx, + // phase indices + wPhaseIdx = FluidSystem::wPhaseIdx, + nPhaseIdx = FluidSystem::nPhaseIdx, - // Phase State - wPhaseOnly = Indices::wPhaseOnly, + // phase state wnPhaseOnly = Indices::wnPhaseOnly, - wgPhaseOnly = Indices::wgPhaseOnly, - threePhases = Indices::threePhases, - //contiWEqIdx = Indices::contiWEqIdx, - //contiNEqIdx = Indices::contiNEqIdx, - // Grid and world dimension - dim = GridView::dimension, + // world dimension dimWorld = GridView::dimensionworld }; - - 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 GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - - typedef Dune::FieldVector GlobalPosition; - - enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using GlobalPosition = typename SubControlVolumeFace::GlobalPosition; public: - /*! - * \brief The constructor - * - * \param timeManager The time manager - * \param gridView The grid view - */ - SagdCyclicProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-6), pOut_(4.0e6) + + SagdCyclicProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { maxDepth_ = 400.0; // [m] FluidSystem::init(); - totalMassProducedOil_ = 0; - totalMassProducedWater_ = 0; - - //episodeLength_ = 3600*12; //12 hours episodeLength - this->timeManager().startNextEpisode(3600*12.); - name_ = GET_RUNTIME_PARAM(TypeTag, std::string, Problem.Name); - } - - bool shouldWriteRestartFile() const - { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().timeStepIndex() % 10000 == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); + totalMassProducedOil_ = 0.0; + totalMassProducedWater_ = 0.0; } - bool shouldWriteOutput() const + void setEpisodeIdx(Scalar epiIdx ) { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().timeStepIndex() % 10000 == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); + episodeIdx_ = epiIdx; } - - void postTimeStep() - { - Scalar time = this->timeManager().time(); - Scalar dt = this->timeManager().timeStepSize(); - - // Calculate storage terms - PrimaryVariables storage; - this->model().globalStorage(storage); - - // Write mass balance information for rank 0 - if (this->gridView().comm().rank() == 0) - { - std::cout<<"Storage: " << storage << " Time: " << time + dt << std::endl; - massBalance.open ("massBalanceCyclic.txt", std::ios::out | std::ios::app ); - massBalance << " Storage " << storage - << " Time " << time+dt - << std::endl; - massBalance.close(); - - } - - //mass of Oil - Scalar newMassProducedOil_ = massProducedOil_; - //std::cout<<" newMassProducedOil_ : "<< newMassProducedOil_ << " Time: " << time+dt << std::endl; - - totalMassProducedOil_ += newMassProducedOil_; - //std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; - //mass of Water - Scalar newMassProducedWater_ = massProducedWater_; - //std::cout<<" newMassProducedWater_ : "<< newMassProducedWater_ << " Time: " << time+dt << std::endl; - - totalMassProducedWater_ += newMassProducedWater_; - //std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; - - const int timeStepIndex = this->timeManager().timeStepIndex(); - if (timeStepIndex == 0 || timeStepIndex % 100 == 0 - || this->timeManager().episodeWillBeFinished() - || this->timeManager().willBeFinished()) - - { - //std::cout<<" newMassProducedOil_ : "<< newMassProducedOil_ << " Time: " << time+dt << std::endl; - std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; - //std::cout<<" newMassProducedWater_ : "<< newMassProducedWater_ << " Time: " << time+dt << std::endl; - std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; - } - - //this->spatialParams().getMaxSaturation(*this); - //this->spatialParams().trappedSat(*this); - } - - void episodeEnd() - { - indexEpisode_ = this->timeManager().episodeIndex(); - - // Start new episode if episode is over - // for first 10 year episode length is 1 year - this->timeManager().startNextEpisode(3600*12); //episode length sent to 12 hours - std::cout<<"Episode index is set to: "< 40.0 - eps_) // on top - { + + // on top + else if (globalPos[1] > 40.0 - eps_) bcTypes.setAllNeumann(); - } - else if ( globalPos[0] > (60.0 - eps_) ) - { + + // on bottom other than corners + else if (globalPos[0] > 60 - eps_ ) bcTypes.setAllDirichlet(); - } - else // on Left - { + + // on Left + else bcTypes.setAllNeumann(); - } + return bcTypes; } /*! * \brief Evaluate the boundary conditions for a dirichlet - * boundary segment. - * - * \param values The dirichlet values for the primary variables - * \param globalPos The position for which the bc type should be evaluated + * control volume. * - * For this method, the \a values parameter stores primary variables. + * \param globalPos The center of the finite volume which ought to be set. */ - void dirichletAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - initial_(values, globalPos); // Everywhere else + return initial_(globalPos); } /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * \param values The neumann values for the conservation equations * \param element The finite element - * \param fvGeomtry 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 fvGeometry The finite-volume geometry in the box scheme + * \param elemVolVars The element volume variables + * \param scvf The sub control volume face * - * For this method, the \a values parameter stores the mass flux - * in normal direction of each phase. Negative values mean influx. + * Negative values mean influx. */ - void solDependentNeumann(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvGeometry, - const Intersection &is, - const int scvIdx, - const int boundaryFaceIdx, - const ElementVolumeVariables &elemVolVars) const + NumEqVector neumann(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolumeFace& scvf) const { - values = 0.0; - int indexEpisode = this->timeManager().episodeIndex(); + NumEqVector values(0.0); - GlobalPosition globalPos; - if (isBox) - globalPos = element.geometry().corner(scvIdx); - else - globalPos = is.geometry().center(); + const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); + const auto& globalPos = insideScv.dofPosition(); // negative values for injection at injection well - if (globalPos[1] > 8.5 + eps_ && globalPos[1] < 9.5 - eps_ ) + if (globalPos[1] > 8.5 - eps_ && globalPos[1] < 9.5 + eps_) { - if (indexEpisode % 2 == 0) // if episodeIndex is even >> noInjection Phase + if (episodeIdx_ % 2 == 0) // if episodeIndex is even >> noInjection Phase { - values[Indices::contiNEqIdx] = 0.0; - values[Indices::contiWEqIdx] = 0.0; - values[Indices::energyEqIdx] = 0.0; + values[contiNEqIdx] = 0.0; + values[contiWEqIdx] = 0.0; + values[energyEqIdx] = 0.0; } else - { // if episodeIndex is odd >> Injection Phase - values[Indices::contiNEqIdx] = -0.0; - values[Indices::contiWEqIdx] = -0.193*2; // (55.5 mol*47.6)/3600 mol/s m = 0.733833333333 - values[Indices::energyEqIdx] = -9132*2; // J/sec m 34788.3611111 + { + values[contiNEqIdx] = -0.0; + values[contiWEqIdx] = -0.193*2; // (55.5 mol*12.5)/3600 mol/s m = 0.193 + values[energyEqIdx] = -9132*2; // J/sec m 9132 } } + else if (globalPos[1] > 2.5 - eps_ && globalPos[1] < 3.5 + eps_) // production well + { + const Scalar elemPressW = elemVolVars[scvf.insideScvIdx()].pressure(wPhaseIdx); //Pressures + const Scalar elemPressN = elemVolVars[scvf.insideScvIdx()].pressure(nPhaseIdx); - else if (globalPos[1] > 2.5 + eps_ && globalPos[1] < 3.5 - eps_) // production well - { - //Scalar satWBound = 1.0; - // const Scalar satW = elemVolVars[scvIdx].saturation(wPhaseIdx); // Saturations - // const Scalar satG = elemVolVars[scvIdx].saturation(gPhaseIdx); - // const Scalar satN = elemVolVars[scvIdx].saturation(nPhaseIdx); - - const Scalar elemPressW = elemVolVars[scvIdx].pressure(wPhaseIdx); //Pressures - // const Scalar elemPressG = elemVolVars[scvIdx].pressure(gPhaseIdx); - const Scalar elemPressN = elemVolVars[scvIdx].pressure(nPhaseIdx); - - const Scalar densityW = elemVolVars[scvIdx].fluidState().density(wPhaseIdx); //Densities - const Scalar densityN = elemVolVars[scvIdx].fluidState().density(nPhaseIdx); - - // const Scalar moDensityW = elemVolVars[scvIdx].fluidState().molarDensity(wPhaseIdx); //Molar Densities - // const Scalar moDensityG = elemVolVars[scvIdx].fluidState().molarDensity(gPhaseIdx); - // const Scalar moDensityN = elemVolVars[scvIdx].fluidState().molarDensity(nPhaseIdx); - - // const Scalar maDensityW = elemVolVars[scvIdx].fluidState().density(wPhaseIdx); //Molar Densities - // const Scalar maDensityG = elemVolVars[scvIdx].fluidState().density(gPhaseIdx); - // const Scalar maDensityN = elemVolVars[scvIdx].fluidState().density(nPhaseIdx); - const Scalar elemMobW = elemVolVars[scvIdx].mobility(wPhaseIdx); //Mobilities - // const Scalar elemMobG = elemVolVars[scvIdx].mobility(gPhaseIdx); - const Scalar elemMobN = elemVolVars[scvIdx].mobility(nPhaseIdx); - - // const Scalar molFracWinW = elemVolVars[scvIdx].fluidState().moleFraction(wPhaseIdx, wCompIdx); - // const Scalar molFracWinN = elemVolVars[scvIdx].fluidState().moleFraction(nPhaseIdx, wCompIdx); - // const Scalar molFracWinG = elemVolVars[scvIdx].fluidState().moleFraction(gPhaseIdx, wCompIdx); - // const Scalar molFracNinW = elemVolVars[scvIdx].fluidState().moleFraction(wPhaseIdx, nCompIdx); - // const Scalar molFracNinN = elemVolVars[scvIdx].fluidState().moleFraction(nPhaseIdx, nCompIdx); - // const Scalar molFracNinG = elemVolVars[scvIdx].fluidState().moleFraction(gPhaseIdx, nCompIdx); - - const Scalar enthW = elemVolVars[scvIdx].enthalpy(wPhaseIdx); //Mobilities - // const Scalar enthG = elemVolVars[scvIdx].enthalpy(gPhaseIdx); - const Scalar enthN = elemVolVars[scvIdx].enthalpy(nPhaseIdx); + const Scalar densityW = elemVolVars[scvf.insideScvIdx()].fluidState().density(wPhaseIdx); //Densities + const Scalar densityN = elemVolVars[scvf.insideScvIdx()].fluidState().density(nPhaseIdx); + + const Scalar elemMobW = elemVolVars[scvf.insideScvIdx()].mobility(wPhaseIdx); //Mobilities + const Scalar elemMobN = elemVolVars[scvf.insideScvIdx()].mobility(nPhaseIdx); + + const Scalar enthW = elemVolVars[scvf.insideScvIdx()].enthalpy(wPhaseIdx); //Enthalpies + const Scalar enthN = elemVolVars[scvf.insideScvIdx()].enthalpy(nPhaseIdx); const Scalar wellRadius = 0.50 * 0.3048; // 0.50 ft as specified by SPE9 - // const Scalar wellArea = M_PI*std::pow(wellRadius,2); // [m^2] - const Scalar gridHeight_ = 0.5; - const Scalar effectiveRadius_ = 0.208 * gridHeight_; //Peaceman's Well Model - // const Scalar effectiveRadius_ = 0.56; + const Scalar gridHeight = 0.5; + const Scalar effectiveRadius = 0.208 * gridHeight; //Peaceman's Well Model + const Scalar pOut = 4e6; + + using std::log; //divided by molarMass() of water to convert from kg/m s to mol/m s - const Scalar qW = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * - densityW * elemMobW * ( elemPressW-pOut_))/0.01801528; + const Scalar qW = (((2*3.1415*0.5*4e-14)/(log(effectiveRadius/wellRadius))) * + densityW * elemMobW * ( elemPressW-pOut))/0.01801528; //divided by molarMass() of HeavyOil to convert from kg/m s to mol/m s - const Scalar qN = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * - densityN * elemMobN * (elemPressN-pOut_))/0.35; + const Scalar qN = (((2*3.1415*0.5*4e-14)/(log(effectiveRadius/wellRadius))) * + densityN * elemMobN * (elemPressN-pOut))/0.35; - // without cooling: - //const Scalar qE = qW*0.018*enthW + qN*enthN*0.350; - - // with cooling: see Diplomarbeit Stefan Roll, Sept. 2015 - Scalar wT = elemVolVars[scvIdx].temperature(); // well temperature Scalar qE; + //without cooling: + // qE = qW*0.018*enthW + qN*enthN*0.350; + + //with cooling: see Diplomarbeit Stefan Roll, Sept. 2015 + Scalar wT = elemVolVars[scvf.insideScvIdx()].temperature(); // well temperature if ( wT > 495. ) { qE = qW*0.018*enthW + qN*enthN*0.350 + (wT-495.)*5000.; // ~3x injected enthalpy std::cout<< "Cooling now! Extracted enthalpy: " << qE << std::endl; - } else { + } + else qE = qW*0.018*enthW + qN*enthN*0.350; - } - - - values[Indices::contiWEqIdx] = qW; - values[Indices::contiNEqIdx] = qN; - values[Indices::energyEqIdx] = qE; + values[contiWEqIdx] = qW; + values[contiNEqIdx] = qN; + values[energyEqIdx] = qE; massProducedOil_ = qN; massProducedWater_ = qW; } + return values; } // \} @@ -440,60 +290,74 @@ public: /*! * \brief Evaluate the initial value for a control volume. * - * \param values The initial values for the primary variables * \param globalPos The position for which the initial condition should be evaluated - * - * For this method, the \a values parameter stores primary - * variables. */ - void initialAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - initial_(values, globalPos); + return initial_(globalPos); } - /*! - * \brief Return the initial phase state inside a control volume. - * - * \param globalPos The global position - */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const + template + void postTimeStep(const TimeLoop& timeLoop, const PrimaryVariables& storage, const SolutionVector& x) { - return wnPhaseOnly; - //return threePhases; + const Scalar time = timeLoop.time(); + const Scalar dt = timeLoop.timeStepSize(); + + // print storage terms + std::cout<<"Storage: " << storage << " Time: " << time+dt << std::endl; + massBalance.open ("massBalanceCyclic.txt", std::ios::out | std::ios::app ); + massBalance << " Storage " << storage + << " Time " << time+dt + << std::endl; + massBalance.close(); + + //mass of Oil + Scalar newMassProducedOil = massProducedOil_; + //mass of Water + Scalar newMassProducedWater = massProducedWater_; + + totalMassProducedOil_ += newMassProducedOil; + totalMassProducedWater_ += newMassProducedWater; + + const int timeStepIndex = timeLoop.timeStepIndex(); + if (timeStepIndex == 0 || + timeStepIndex % 100 == 0 || //after every 1000000 secs + timeLoop.willBeFinished()) + + { + std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; + std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; + } } private: // internal method for the initial condition (reused for the // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initial_(const GlobalPosition &globalPos) const { + PrimaryVariables values(0.0); + values.setState(wnPhaseOnly); Scalar densityW = 1000.0; - values[pressureIdx] = 101300. + (maxDepth_ - globalPos[1])*densityW*9.81; + values[pressureIdx] = 101300.0 + (maxDepth_ - globalPos[1])*densityW*9.81; values[switch1Idx] = 295.13; // temperature values[switch2Idx] = 0.3; //NAPL saturation + return values; } Scalar maxDepth_; - Scalar eps_; - Scalar pIn_; - Scalar pOut_; + static constexpr Scalar eps_ = 1e-6; + std::string name_; + + int episodeIdx_; + Scalar totalMassProducedOil_; Scalar totalMassProducedWater_; - // TODO this is a very dirty evil solution mutable Scalar massProducedOil_; mutable Scalar massProducedWater_; - // int vectorSize; - Scalar episodeLength_; - int indexEpisode_; - std::string name_; - // std::string name_; - // std::ifstream inputFile; //Input file stream object - // std::vector hours; std::ofstream massBalance; -}; +}; } //end namespace #endif diff --git a/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.cc b/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.cc index 00087f1f4738142ef2302007da0b0a9c7f1e2e4d..60cd20771c7f75bc3f34b3cf1b6ba152f57f84a9 100644 --- a/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.cc +++ b/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.cc @@ -18,19 +18,41 @@ *****************************************************************************/ /*! * \file - * - * \brief test for the 3p3cni box model + * \ingroup OnePTests + * \brief Test for the three-phase three-component box model */ -#include "config.h" -#include "problem.hh" -#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include "problem.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. + * \ingroup OnePTests * \param errorMsg The error message that was issued by the start function. * Comprises the thing that went wrong and a general help message. */ @@ -42,19 +64,183 @@ void usage(const char *progName, const std::string &errorMsg) 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 DGF format\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 DGF format\n"; std::cout << errorMessageOut << "\n"; } } -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(ThreePWaterOilSagdCyclicBoxTypeTag); + + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + VtkOutputFields::init(vtkWriter); //! Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto episodeIdx = 0.0; + auto timeLoop = std::make_shared>(0, dt, tEnd); + problem->setEpisodeIdx(episodeIdx); + timeLoop->setMaxTimeStepSize(maxDt); + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength", std::numeric_limits::max())); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = Dumux::AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + PrimaryVariables storage(0); + const auto& localResidual = assembler->localResidual(); + for (const auto& element : elements(leafGridView, Dune::Partitions::interior)) + { + auto storageVec = localResidual.evalStorage(*problem, element, *fvGridGeometry, *gridVariables, x); + storage += storageVec[0]; + } + problem->postTimeStep(*timeLoop, storage, x); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // report and count episodes and tell the problem when advancing to the next episode + if (hasParam("TimeLoop.EpisodeLength")) + if(timeLoop->isCheckPoint()) + { + std::cout << "\n Episode " << episodeIdx << " done \n" << std::endl; + + episodeIdx++; + problem->setEpisodeIdx(episodeIdx); + } + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + +} + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(SagdCyclicBoxProblem) ProblemTypeTag; - //typedef TTAG(SagdCCProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.input b/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.input index a93eb715f23acad206a38ce0f6d8bc14a47a02c3..97215579c11726deaf605efa6a3a17949c681137 100644 --- a/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.input +++ b/lecture/mm/heavyoil/sagdcyclic/sagd_cyclic.input @@ -1,7 +1,8 @@ -[TimeManager] +[TimeLoop] DtInitial = 900 # [s] TEnd = 31467600 # [s] a year MaxTimeStepSize = 1800 # [s] +EpisodeLength = 43200 # 43200 # [s] (43200s = 12h) [Grid] UpperRight = 60 40 @@ -14,6 +15,10 @@ Name = sagdCyclic # name passed to the output routines MaxTimeStepDivisions = 100 [Newton] -RelTolerance = 1e-02 +MaxRelativeShift = 1.e-2 MaxSteps = 8 -WriteConvergence = 0 + +[Component] +SolidDensity = 2650 +SolidThermalConductivity = 2.8 +SolidHeatCapacity = 850 diff --git a/lecture/mm/heavyoil/sagdcyclic/spatialparams.hh b/lecture/mm/heavyoil/sagdcyclic/spatialparams.hh index f87e3b5ff8a56f51596a875ef77dc484daec3ccf..1cd28b924d96b3a961dd93857e8d733597a1e640 100644 --- a/lecture/mm/heavyoil/sagdcyclic/spatialparams.hh +++ b/lecture/mm/heavyoil/sagdcyclic/spatialparams.hh @@ -18,107 +18,61 @@ *****************************************************************************/ /*! * \file - * + * \ingroup ThreePWaterOilTests * \brief Definition of the spatial parameters for the SAGD problem. */ -#ifndef DUMUX_SAGDCYCLIC_SPATIAL_PARAMS_HH -#define DUMUX_SAGDCYCLIC_SPATIAL_PARAMS_HH +#ifndef DUMUX_SAGD_SPATIAL_PARAMS_HH +#define DUMUX_SAGD_SPATIAL_PARAMS_HH -#include +#include +#include #include -#include "../3p/parkervangenuchtenzero.hh" -#include "../3p/parkervangenuchtenzeroparams.hh" - -namespace Dumux -{ - -//forward declaration -template -class SagdCyclicSpatialParams; - -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(SagdCyclicSpatialParams); -// Set the spatial parameters -SET_TYPE_PROP(SagdCyclicSpatialParams, SpatialParams, Dumux::SagdCyclicSpatialParams); +#include +#include +#include -// Set the material Law -SET_PROP(SagdCyclicSpatialParams, MaterialLaw) -{ -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef ParkerVanGenZero3P type; -}; -} +namespace Dumux { /*! * \ingroup ThreePThreeCNIModel * * \brief Definition of the spatial parameters for the SAGD problem */ -template -class SagdCyclicSpatialParams : public ImplicitSpatialParams +template +class SagdSpatialParams +: public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; -typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - - numPhases = GET_PROP_VALUE(TypeTag, NumPhases), - numComponents = GET_PROP_VALUE(TypeTag, NumComponents), - - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - gPhaseIdx = Indices::gPhaseIdx - }; - - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldVector DimVector; - - - typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; - - typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + using GridView = typename FVGridGeometry::GridView; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; + enum { dimWorld=GridView::dimensionworld }; + using GlobalPosition = typename SubControlVolume::GlobalPosition; - typedef typename GridView::template Codim<0>::Iterator ElementIterator; - typedef typename GridView::template Codim::Iterator VertexIterator; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - - enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; - enum { dofCodim = isBox ? dim : 0 }; + using Element = typename GridView::template Codim<0>::Entity; + using ParentType = FVSpatialParams>; + using EffectiveLaw = RegularizedParkerVanGen3P; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; + using PermeabilityType = Scalar; /*! * \brief The constructor * * \param gridView The grid view */ - SagdCyclicSpatialParams(const GridView &gridView) : ParentType(gridView) + SagdSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry), eps_(1e-6) { - land_ = 5.0; layerBottom_ = 35.0; // intrinsic permeabilities @@ -129,236 +83,101 @@ public: finePorosity_ = 0.10; coarsePorosity_ = 0.1; - // heat conductivity of granite - lambdaSolid_ = 2.8; - - // specific heat capacities - fineHeatCap_ = 850.; - coarseHeatCap_ = 850; - // residual saturations fineMaterialParams_.setSwr(0.1); - fineMaterialParams_.setSwrx(0.12); //Total liquid Residual Saturation fineMaterialParams_.setSnr(0.09); //Residual of NAPL if there is no water fineMaterialParams_.setSgr(0.01); coarseMaterialParams_.setSwr(0.1); - coarseMaterialParams_.setSwrx(0.12); coarseMaterialParams_.setSnr(0.09); coarseMaterialParams_.setSgr(0.01); // parameters for the 3phase van Genuchten law fineMaterialParams_.setVgn(4.0); coarseMaterialParams_.setVgn(4.0); - fineMaterialParams_.setVgAlpha(0.0005); - coarseMaterialParams_.setVgAlpha(0.0015); + fineMaterialParams_.setVgAlpha(1.); + coarseMaterialParams_.setVgAlpha(1.); coarseMaterialParams_.setKrRegardsSnr(false); fineMaterialParams_.setKrRegardsSnr(false); - - /* // parameters for adsorption - coarseMaterialParams_.setKdNAPL(0.); - coarseMaterialParams_.setRhoBulk(0); - fineMaterialParams_.setKdNAPL(0.); - fineMaterialParams_.setRhoBulk(0); */ } /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. + * \brief Function for defining the (intrinsic) permeability \f$[m^2]\f$ + * \note It is possibly solution dependent. + * + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element. + * \return permeability + */ + template + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { return permeabilityAtPos(scv.dofPosition());} + + /*! + * \brief Returns the intrinsic permeability tensor \f$[m^2]\f$ * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param globalPos The global position */ - Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) + if (isFineMaterial_(globalPos)) return fineK_; return coarseK_; } /*! - * \brief Define the porosity \f$[-]\f$ of the spatial parameters + * \brief Returns the porosity \f$[-]\f$ * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined + * \param globalPos The global position */ - Scalar porosity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + Scalar porosityAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) + if (isFineMaterial_(globalPos)) return finePorosity_; else return coarsePorosity_; } - /*! - * \brief return the parameter object for the Brooks-Corey material law which depends on the position + * \brief Function for defining the parameters needed by constitutive relationships (kr-sw, pc-sw, etc.). * - * \param element The current finite element - * \param fvGeometry The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element. + * \return the material parameters object */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + template + const MaterialLawParams& materialLawParams(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineMaterialParams_; - else - return coarseMaterialParams_; + return materialLawParamsAtPos(scv.dofPosition()); } /*! - * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. + * \brief Returns the parameter object for the capillary-pressure/ + * saturation material law * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume + * \param globalPos The global position */ - Scalar solidHeatCapacity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineHeatCap_; + if (isFineMaterial_(globalPos)) + return fineMaterialParams_; else - return coarseHeatCap_; - } - - /*! - * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidDensity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return 2650; // density of sand [kg/m^3] - } - - /*! - * \brief Returns the thermal conductivity \f$\mathrm{[W/(m K)]}\f$ of the solid - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry of the element - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidThermalConductivity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return lambdaSolid_; - } - - struct MaxSaturations - { - Scalar MaxSatW; - Scalar MaxSatN; - Scalar MaxSatG; - }; - - struct trappedSaturations - { - Scalar trappedSatN; - }; - - void getMaxSaturation(Problem &problem) - { - - typedef Dune::BlockVector > ScalarField; - - // get the number of degrees of freedom - FVElementGeometry fvGeometry; - VolumeVariables volVars; - maxSats_.resize(problem.model().numDofs()); - - ScalarField *getMaxSaturation[numPhases]; - - for (const auto& element : elements(problem.gridView())) - { - fvGeometry.update(problem.gridView(), element); - - for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) - { - int dofIdxGlobal = problem.model().dofMapper().map(element, scvIdx, dofCodim); - volVars.update(problem.model().curSol()[dofIdxGlobal], - problem, - element, - fvGeometry, - scvIdx, - false); - - if (volVars.saturation(wPhaseIdx) > maxSats_[dofIdxGlobal].MaxSatW) - maxSats_[dofIdxGlobal].MaxSatW = volVars.saturation(wPhaseIdx); - - if (volVars.saturation(wPhaseIdx) > maxSats_[dofIdxGlobal].MaxSatW) - maxSats_[dofIdxGlobal].MaxSatW = volVars.saturation(wPhaseIdx); - - if (volVars.saturation(nPhaseIdx) > maxSats_[dofIdxGlobal].MaxSatN) - maxSats_[dofIdxGlobal].MaxSatN = volVars.saturation(nPhaseIdx); - - if (volVars.saturation(gPhaseIdx) > maxSats_[dofIdxGlobal].MaxSatG) - maxSats_[dofIdxGlobal].MaxSatG = volVars.saturation(gPhaseIdx); - } - } - } - - void trappedSat(Problem &problem) - { - // get the number of degrees of freedom - FVElementGeometry fvGeometry; - VolumeVariables volVars; - trapSats_.resize(problem.model().numDofs()); - - for (const auto& element : elements(problem.gridView())) - { - fvGeometry.update(problem.gridView(), element); - for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) - { - const unsigned int dofIdxGlobal = problem.model().dofMapper().subIndex(element, scvIdx, dofCodim); - volVars.update(problem.model().curSol()[dofIdxGlobal], - problem, - element, - fvGeometry, - scvIdx, - false); - trapSats_[dofIdxGlobal].trappedSatN = - (maxSats_[dofIdxGlobal].MaxSatN)/(1+land_*maxSats_[dofIdxGlobal].MaxSatN); - - coarseMaterialParams_.setTrappedSatN(trapSats_[dofIdxGlobal].trappedSatN); - fineMaterialParams_.setTrappedSatN(trapSats_[dofIdxGlobal].trappedSatN); - } - } + return coarseMaterialParams_; } private: bool isFineMaterial_(const GlobalPosition &pos) const { - return pos[dim-1] > layerBottom_ - eps_; - } + return pos[dimWorld-1] > layerBottom_ - eps_; + }; Scalar layerBottom_; - Scalar lambdaSolid_; Scalar fineK_; Scalar coarseK_; @@ -366,21 +185,12 @@ private: Scalar finePorosity_; Scalar coarsePorosity_; - Scalar land_; - - Scalar fineHeatCap_; - Scalar coarseHeatCap_; - MaterialLawParams fineMaterialParams_; MaterialLawParams coarseMaterialParams_; - std::vector maxSats_; - - std::vector trapSats_; - - static constexpr Scalar eps_ = 1.5e-7; + Scalar eps_; }; -} // end namespace dumux +} #endif diff --git a/lecture/mm/heavyoil/sagdcyclichyst/CMakeLists.txt b/lecture/mm/heavyoil/sagdcyclichyst/CMakeLists.txt index 65de4c8659d4778de96f2a14d76ad2f2d082479a..64ffa5b5f020202765f2704eb03b5a7253d8f1e2 100644 --- a/lecture/mm/heavyoil/sagdcyclichyst/CMakeLists.txt +++ b/lecture/mm/heavyoil/sagdcyclichyst/CMakeLists.txt @@ -1,6 +1,6 @@ # headers for installation and headercheck -add_dumux_test(sagd_cyclic_hyst sagd_cyclic_hyst sagd_cyclic_hyst.cc "${CMAKE_CURRENT_BINARY_DIR}/sagd_cyclic_hyst" "-TimeManager.TEnd" "43200" "-TimeManager.MaxTimeStepSize" "21600") +add_dumux_test(sagd_cyclic_hyst sagd_cyclic_hyst sagd_cyclic_hyst.cc "${CMAKE_CURRENT_BINARY_DIR}/sagd_cyclic_hyst" "-TimeLoop.TEnd" "43200" "-TimeLoop.MaxTimeStepSize" "21600") install(FILES problem.hh diff --git a/lecture/mm/heavyoil/sagdcyclichyst/problem.hh b/lecture/mm/heavyoil/sagdcyclichyst/problem.hh index 750d6ed1b5be0e15a2a3838f630bef57b5ada297..0428c7adfd8faea956957df6f563b8b2dbfcb83f 100644 --- a/lecture/mm/heavyoil/sagdcyclichyst/problem.hh +++ b/lecture/mm/heavyoil/sagdcyclichyst/problem.hh @@ -18,58 +18,70 @@ *****************************************************************************/ /*! * \file - * + * \ingroup ThreePWaterOilTests * \brief Non-isothermal SAGD problem - * */ #ifndef DUMUX_SAGDCYCLICHYSTPROBLEM_HH #define DUMUX_SAGDCYCLICHYSTPROBLEM_HH -#include +#include -#include +#include +#include #include -#include + +#include +#include +#include + #include "spatialparams.hh" -#define ISOTHERMAL 0 +namespace Dumux { -namespace Dumux -{ +/*! + * \file + * \ingroup ThreePWaterOilTests + * \brief Non-isothermal SAGD problem + */ template class SagdCyclicHystProblem; -namespace Properties -{ -NEW_TYPE_TAG(SagdCyclicHystProblem, INHERITS_FROM(ThreePWaterOilNI, SagdCyclicHystSpatialParams)); -NEW_TYPE_TAG(SagdCyclicHystBoxProblem, INHERITS_FROM(BoxModel, SagdCyclicHystProblem)); -NEW_TYPE_TAG(SagdCyclicHystCCProblem, INHERITS_FROM(CCModel, SagdCyclicHystProblem)); +namespace Properties { +NEW_TYPE_TAG(SagdTypeTag, INHERITS_FROM(ThreePWaterOilNI)); +NEW_TYPE_TAG(ThreePWaterOilSagdCyclicHystBoxTypeTag, INHERITS_FROM(BoxModel, SagdTypeTag)); // Set the grid type -SET_TYPE_PROP(SagdCyclicHystProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(SagdTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(SagdCyclicHystProblem, Problem, Dumux::SagdCyclicHystProblem); +SET_TYPE_PROP(SagdTypeTag, Problem, Dumux::SagdCyclicHystProblem); + +// Set the spatial parameters +SET_PROP(SagdTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = SagdSpatialParams; +}; // Set the fluid system -SET_TYPE_PROP(SagdCyclicHystProblem, +SET_TYPE_PROP(SagdTypeTag, FluidSystem, - Dumux::FluidSystems::H2OHeavyOil); - -// Enable gravity -SET_BOOL_PROP(SagdCyclicHystProblem, ProblemEnableGravity, true); + Dumux::FluidSystems::H2OHeavyOil); -// Use forward differences instead of central differences -SET_INT_PROP(SagdCyclicHystProblem, ImplicitNumericDifferenceMethod, +1); +SET_BOOL_PROP(SagdTypeTag, OnlyGasPhaseCanDisappear, true); -// Write newton convergence -SET_BOOL_PROP(SagdCyclicHystProblem, NewtonWriteConvergence, false); +SET_BOOL_PROP(SagdTypeTag, UseMoles, true); -SET_BOOL_PROP(SagdCyclicHystProblem, UseSimpleModel, true); - -SET_BOOL_PROP(SagdCyclicHystProblem, UseMoles, true); -} +// Set the fluid system +SET_PROP(SagdTypeTag, SolidSystem) +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using InertComponent = Components::Constant<1, Scalar>; + using type = SolidSystems::InertSolidPhase; +}; +} // end namespace Properties /*! @@ -80,190 +92,62 @@ SET_BOOL_PROP(SagdCyclicHystProblem, UseMoles, true); * This problem uses the \ref ThreePWaterOilModel. * * */ -template -class SagdCyclicHystProblem : public ImplicitPorousMediaProblem +template +class SagdCyclicHystProblem : public PorousMediumFlowProblem { - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GridView::Grid Grid; - - typedef ImplicitPorousMediaProblem ParentType; - - // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + using ParentType = PorousMediumFlowProblem; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; enum { - - numPhases = GET_PROP_VALUE(TypeTag, NumPhases), - numComponents = GET_PROP_VALUE(TypeTag, NumComponents), - pressureIdx = Indices::pressureIdx, switch1Idx = Indices::switch1Idx, switch2Idx = Indices::switch2Idx, + contiWEqIdx = Indices::conti0EqIdx + FluidSystem::wCompIdx, + contiNEqIdx = Indices::conti0EqIdx + FluidSystem::nCompIdx, energyEqIdx = Indices::energyEqIdx, - // phase and component indices - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - gPhaseIdx = Indices::gPhaseIdx, - wCompIdx = Indices::wCompIdx, - nCompIdx = Indices::nCompIdx, + // phase indices + wPhaseIdx = FluidSystem::wPhaseIdx, + nPhaseIdx = FluidSystem::nPhaseIdx, - // Phase State - wPhaseOnly = Indices::wPhaseOnly, + // phase state wnPhaseOnly = Indices::wnPhaseOnly, - wgPhaseOnly = Indices::wgPhaseOnly, - threePhases = Indices::threePhases, - //contiWEqIdx = Indices::contiWEqIdx, - //contiNEqIdx = Indices::contiNEqIdx, - // Grid and world dimension - dim = GridView::dimension, + // world dimension dimWorld = GridView::dimensionworld }; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using GlobalPosition = typename SubControlVolumeFace::GlobalPosition; - typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - - typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; - typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; - - typedef typename GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - - - typedef typename GridView::template Codim<0>::Iterator ElementIterator; - typedef typename GridView::template Codim::Iterator VertexIterator; - - typedef Dune::FieldVector GlobalPosition; - - enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; - enum { dofCodim = isBox ? dim : 0 }; - static const bool useMoles = GET_PROP_VALUE(TypeTag, UseMoles); public: - /*! - * \brief The constructor - * - * \param timeManager The time manager - * \param gridView The grid view - */ - SagdCyclicHystProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-6), pOut_(4.0e6) + + SagdCyclicHystProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { - name_ = GET_RUNTIME_PARAM(TypeTag, std::string, Problem.Name); maxDepth_ = 400.0; // [m] FluidSystem::init(); - totalMassProducedOil_ =0; - totalMassProducedWater_ =0; - - this->timeManager().startNextEpisode(3600*12.); - - - //stateing in the console whether mole or mass fractions are used - if(!useMoles) - std::cout<<"Problem uses mass-fractions."<timeManager().timeStepIndex() == 0 || - this->timeManager().timeStepIndex() % 10000 == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } - - bool shouldWriteOutput() const - { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().timeStepIndex() % 10000 == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } - - void postTimeStep() - { - const Scalar time = this->timeManager().time(); - const Scalar dt = this->timeManager().timeStepSize(); - - // Calculate storage terms - PrimaryVariables storage; - this->model().globalStorage(storage); - // Write mass balance information for rank 0 - if (this->gridView().comm().rank() == 0) - { - std::cout<<"Storage: " << storage << " Time: " << time+dt << std::endl; - massBalance.open ("massBalanceCyclicHyst.txt", std::ios::out | std::ios::app ); - massBalance << " Storage " << storage - << " Time " << time+dt - << std::endl; - massBalance.close(); - } - - - this->spatialParams().getMaxSaturation(*this); - this->spatialParams().trappedSat(*this); - - //mass of Oil - const Scalar newMassProducedOil_ = massProducedOil_; - - totalMassProducedOil_ += newMassProducedOil_; - //mass of Water - Scalar newMassProducedWater_ = massProducedWater_; - - totalMassProducedWater_ += newMassProducedWater_; - - const int timeStepIndex = this->timeManager().timeStepIndex(); - if (timeStepIndex == 0 || - timeStepIndex % 100 == 0 || //after every 1000000 secs - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished()) - - { - std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; - std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; - } - - - + totalMassProducedOil_ = 0.0; + totalMassProducedWater_ = 0.0; } - void episodeEnd() - { - int indexEpisode = this->timeManager().episodeIndex(); - this->timeManager().startNextEpisode(3600*12); //episode length sent to 12 hours - std::cout<<"Episode index is set to: "< 40 - eps_) + else if (globalPos[1] > 40.0 - eps_) bcTypes.setAllNeumann(); - else if (globalPos[0] > 60 - eps_) + // on bottom other than corners + else if (globalPos[0] > 60 - eps_ ) bcTypes.setAllDirichlet(); - // on left + // on Left else bcTypes.setAllNeumann(); + return bcTypes; } /*! * \brief Evaluate the boundary conditions for a dirichlet - * boundary segment. - * - * \param values The dirichlet values for the primary variables - * \param globalPos The position for which the bc type should be evaluated + * control volume. * - * For this method, the \a values parameter stores primary variables. + * \param globalPos The center of the finite volume which ought to be set. */ - void dirichletAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - initial_(values, globalPos); + return initial_(globalPos); } /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * \param values The neumann values for the conservation equations * \param element The finite element - * \param fvGeomtry 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 fvGeometry The finite-volume geometry in the box scheme + * \param elemVolVars The element volume variables + * \param scvf The sub control volume face * - * For this method, the \a values parameter stores the mass flux - * in normal direction of each phase. Negative values mean influx. + * Negative values mean influx. */ - void solDependentNeumann(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvGeometry, - const Intersection &is, - const int scvIdx, - const int boundaryFaceIdx, - const ElementVolumeVariables &elemVolVars) const + NumEqVector neumann(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolumeFace& scvf) const { - values = 0.0; - const int indexEpisode = this->timeManager().episodeIndex(); + NumEqVector values(0.0); + const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); + const auto& globalPos = insideScv.dofPosition(); - GlobalPosition globalPos; - if (isBox) - globalPos = element.geometry().corner(scvIdx); - else - globalPos = is.geometry().center(); - - - if (globalPos[1] > 8.5 + eps_ && globalPos[1] < 9.5 - eps_ ) + // negative values for injection at injection well + if (globalPos[1] > 8.5 - eps_ && globalPos[1] < 9.5 + eps_) { - if (indexEpisode % 2 == 0) // if episodeIndex is even >> noInjection Phase + if (episodeIdx_ % 2 == 0) // if episodeIndex is even >> noInjection Phase { - values[Indices::contiNEqIdx] = 0.0; - values[Indices::contiWEqIdx] = 0.0; - values[Indices::energyEqIdx] = 0.0; + values[contiNEqIdx] = 0.0; + values[contiWEqIdx] = 0.0; + values[energyEqIdx] = 0.0; } else - {// if episodeIndex is odd >> Injection Phase - values[Indices::contiNEqIdx] = -0.0; - values[Indices::contiWEqIdx] = -0.193*2; // (55.5 mol*47.6)/3600 mol/s m = 0.733833333333 - values[Indices::energyEqIdx] = -9132*2; // J/sec m 34788.3611111 + { + values[contiNEqIdx] = -0.0; + values[contiWEqIdx] = -0.193*2; // (55.5 mol*12.5)/3600 mol/s m = 0.193 + values[energyEqIdx] = -9132*2; // J/sec m 9132 } } - - - else if (globalPos[1] > 2.5 + eps_ && globalPos[1] < 3.5 - eps_) // production well + else if (globalPos[1] > 2.5 - eps_ && globalPos[1] < 3.5 + eps_) // production well { + const Scalar elemPressW = elemVolVars[scvf.insideScvIdx()].pressure(wPhaseIdx); //Pressures + const Scalar elemPressN = elemVolVars[scvf.insideScvIdx()].pressure(nPhaseIdx); - //Scalar satWBound = 1.0; - // const Scalar satW = elemVolVars[scvIdx].saturation(wPhaseIdx); // Saturations - // const Scalar satG = elemVolVars[scvIdx].saturation(gPhaseIdx); - // const Scalar satN = elemVolVars[scvIdx].saturation(nPhaseIdx); - - const Scalar elemPressW = elemVolVars[scvIdx].pressure(wPhaseIdx); //Pressures - // const Scalar elemPressG = elemVolVars[scvIdx].pressure(gPhaseIdx); - const Scalar elemPressN = elemVolVars[scvIdx].pressure(nPhaseIdx); - - const Scalar densityW = elemVolVars[scvIdx].fluidState().density(wPhaseIdx); //Densities - // const Scalar densityG = elemVolVars[scvIdx].fluidState().density(gPhaseIdx); - const Scalar densityN = elemVolVars[scvIdx].fluidState().density(nPhaseIdx); - - // const Scalar moDensityW = elemVolVars[scvIdx].fluidState().molarDensity(wPhaseIdx); //Molar Densities - // const Scalar moDensityG = elemVolVars[scvIdx].fluidState().molarDensity(gPhaseIdx); - // const Scalar moDensityN = elemVolVars[scvIdx].fluidState().molarDensity(nPhaseIdx); - - // const Scalar maDensityW = elemVolVars[scvIdx].fluidState().density(wPhaseIdx); //Molar Densities - // const Scalar maDensityG = elemVolVars[scvIdx].fluidState().density(gPhaseIdx); - // const Scalar maDensityN = elemVolVars[scvIdx].fluidState().density(nPhaseIdx); - const Scalar elemMobW = elemVolVars[scvIdx].mobility(wPhaseIdx); //Mobilities - // const Scalar elemMobG = elemVolVars[scvIdx].mobility(gPhaseIdx); - const Scalar elemMobN = elemVolVars[scvIdx].mobility(nPhaseIdx); - - // const Scalar molFracWinW = elemVolVars[scvIdx].fluidState().moleFraction(wPhaseIdx, wCompIdx); - // const Scalar molFracWinN = elemVolVars[scvIdx].fluidState().moleFraction(nPhaseIdx, wCompIdx); - // const Scalar molFracWinG = elemVolVars[scvIdx].fluidState().moleFraction(gPhaseIdx, wCompIdx); - // const Scalar molFracNinW = elemVolVars[scvIdx].fluidState().moleFraction(wPhaseIdx, nCompIdx); - // const Scalar molFracNinN = elemVolVars[scvIdx].fluidState().moleFraction(nPhaseIdx, nCompIdx); - // const Scalar molFracNinG = elemVolVars[scvIdx].fluidState().moleFraction(gPhaseIdx, nCompIdx); - - const Scalar enthW = elemVolVars[scvIdx].enthalpy(wPhaseIdx); //Mobilities - // const Scalar enthG = elemVolVars[scvIdx].enthalpy(gPhaseIdx); - const Scalar enthN = elemVolVars[scvIdx].enthalpy(nPhaseIdx); + const Scalar densityW = elemVolVars[scvf.insideScvIdx()].fluidState().density(wPhaseIdx); //Densities + const Scalar densityN = elemVolVars[scvf.insideScvIdx()].fluidState().density(nPhaseIdx); - const Scalar wellRadius = 0.50 * 0.3048; // 0.50 ft as specified by SPE9 - // const Scalar wellArea = M_PI*std::pow(wellRadius,2); // [m^2] + const Scalar elemMobW = elemVolVars[scvf.insideScvIdx()].mobility(wPhaseIdx); //Mobilities + const Scalar elemMobN = elemVolVars[scvf.insideScvIdx()].mobility(nPhaseIdx); + const Scalar enthW = elemVolVars[scvf.insideScvIdx()].enthalpy(wPhaseIdx); //Enthalpies + const Scalar enthN = elemVolVars[scvf.insideScvIdx()].enthalpy(nPhaseIdx); - const Scalar gridHeight_ = 0.5; - const Scalar effectiveRadius_ = 0.208 * gridHeight_; //Peaceman's Well Model + const Scalar wellRadius = 0.50 * 0.3048; // 0.50 ft as specified by SPE9 - const Scalar qW = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * //divided by molarMass() of water to convert from kg/m s to mol/m s - densityW * elemMobW * ( elemPressW-pOut_))/0.01801528; + const Scalar pOut = 4e6; + const Scalar gridHeight = 0.5; + const Scalar effectiveRadius = 0.208 * gridHeight; //Peaceman's Well Model - const Scalar qN = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * - densityN * elemMobN * (elemPressN-pOut_))/0.35; //divided by molarMass() of HeavyOil to convert from kg/m s to mol/m s + using std::log; + //divided by molarMass() of water to convert from kg/m s to mol/m s + const Scalar qW = (((2*3.1415*0.5*4e-14)/(log(effectiveRadius/wellRadius))) * + densityW * elemMobW * ( elemPressW-pOut))/0.01801528; + //divided by molarMass() of HeavyOil to convert from kg/m s to mol/m s + const Scalar qN = (((2*3.1415*0.5*4e-14)/(log(effectiveRadius/wellRadius))) * + densityN * elemMobN * (elemPressN-pOut))/0.35; + Scalar qE; //without cooling: - // const Scalar qE = qW*0.018*enthW + qN*enthN*0.350; + // qE = qW*0.018*enthW + qN*enthN*0.350; //with cooling: see Diplomarbeit Stefan Roll, Sept. 2015 - Scalar wT = elemVolVars[scvIdx].temperature(); // well temperature - Scalar qE; - if ( wT > 495. ) + Scalar wT = elemVolVars[scvf.insideScvIdx()].temperature(); // well temperature + if (wT > 495. ) { qE = qW*0.018*enthW + qN*enthN*0.350 + (wT-495.)*5000.; // ~3x injected enthalpy std::cout<< "Cooling now! Extracted enthalpy: " << qE << std::endl; - } else { + } + else qE = qW*0.018*enthW + qN*enthN*0.350; - } - - - - values[Indices::contiWEqIdx] = qW; - values[Indices::contiNEqIdx] = qN; - values[Indices::energyEqIdx] = qE; + values[contiWEqIdx] = qW; + values[contiNEqIdx] = qN; + values[energyEqIdx] = qE; massProducedOil_ = qN; massProducedWater_ = qW; } + return values; } // \} @@ -445,61 +289,72 @@ public: /*! * \brief Evaluate the initial value for a control volume. * - * \param values The initial values for the primary variables * \param globalPos The position for which the initial condition should be evaluated - * - * For this method, the \a values parameter stores primary - * variables. */ - void initialAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - initial_(values, globalPos); + return initial_(globalPos); } - /*! - * \brief Return the initial phase state inside a control volume. - * - * \param globalPos The global position - */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const + template + void postTimeStep(const TimeLoop& timeLoop, const PrimaryVariables& storage, const SolutionVector& x) { - return wnPhaseOnly; - //return threePhases; + const Scalar time = timeLoop.time(); + const Scalar dt = timeLoop.timeStepSize(); + + // print storage terms + std::cout<<"Storage: " << storage << " Time: " << time+dt << std::endl; + massBalance.open ("massBalanceCyclicHyst.txt", std::ios::out | std::ios::app ); + massBalance << " Storage " << storage + << " Time " << time+dt + << std::endl; + massBalance.close(); + + this->spatialParams().template getMaxSaturation(*this, x); + this->spatialParams().template trappedSat(*this, x); + + totalMassProducedOil_ += massProducedOil_; + totalMassProducedWater_ += massProducedWater_; + + const int timeStepIndex = timeLoop.timeStepIndex(); + if (timeStepIndex == 0 || + timeStepIndex % 100 == 0 || //after every 1000000 secs + timeLoop.willBeFinished()) + + { + std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; + std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; + } } private: // internal method for the initial condition (reused for the // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initial_(const GlobalPosition &globalPos) const { + PrimaryVariables values(0.0); + values.setState(wnPhaseOnly); Scalar densityW = 1000.0; - values[pressureIdx] = 101300. + (maxDepth_ - globalPos[1])*densityW*9.81; + values[pressureIdx] = 101300.0 + (maxDepth_ - globalPos[1])*densityW*9.81; values[switch1Idx] = 295.13; // temperature values[switch2Idx] = 0.3; //NAPL saturation + return values; } Scalar maxDepth_; - Scalar eps_; - Scalar pIn_; - Scalar pOut_; - //int vectorSize; - Scalar episodeLength_; + static constexpr Scalar eps_ = 1e-6; std::string name_; + + int episodeIdx_; + Scalar totalMassProducedOil_; Scalar totalMassProducedWater_; - - // TODO very evil hack mutable Scalar massProducedOil_; mutable Scalar massProducedWater_; - // std::string name_; - //std::ifstream inputFile; //Input file stream object - //std::vector hours; std::ofstream massBalance; - //std::vector maxSats_; -}; +}; } //end namespace #endif diff --git a/lecture/mm/heavyoil/sagdcyclichyst/problem.hh.SAVE.hh b/lecture/mm/heavyoil/sagdcyclichyst/problem.hh.SAVE.hh new file mode 100644 index 0000000000000000000000000000000000000000..c8de471acf095746892d400a176075c1eda98d59 --- /dev/null +++ b/lecture/mm/heavyoil/sagdcyclichyst/problem.hh.SAVE.hh @@ -0,0 +1,467 @@ +// -*- 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 vesion. * + * * + * 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 . * + *****************************************************************************/ +/*! + * \file + * + * \brief Non-isothermal SAGD problem + * + */ +#ifndef DUMUX_SAGDCYCLICHYSTPROBLEM_HH +#define DUMUX_SAGDCYCLICHYSTPROBLEM_HH + +#include +#include +#include +#include +#include +#include +#include "spatialparams.hh" + +#define ISOTHERMAL 0 + +namespace Dumux { + +template +class SagdCyclicHystProblem; + +namespace Properties { + +NEW_TYPE_TAG(SagdCyclicHystProblemTypeTag, INHERITS_FROM(ThreePWaterOilNI, SagdCyclicHystSpatialParamsTypeTag)); + +NEW_TYPE_TAG(SagdCyclicHystBoxProblem, INHERITS_FROM(BoxModel, SagdCyclicHystProblemTypeTag)); + +NEW_TYPE_TAG(SagdCyclicHystCCProblem, INHERITS_FROM(CCModel, SagdCyclicHystProblemTypeTag)); + +// Set the grid type +SET_TYPE_PROP(SagdCyclicHystProblemTypeTag, Grid, Dune::YaspGrid<2>); + +// Set the problem property +SET_TYPE_PROP(SagdCyclicHystProblemTypeTag, Problem, Dumux::SagdCyclicHystProblem); + +// Set the fluid system +SET_TYPE_PROP(SagdCyclicHystProblemTypeTag, + FluidSystem, + Dumux::FluidSystems::H2OHeavyOil); + +// Enable gravity +SET_BOOL_PROP(SagdCyclicHystProblemTypeTag, ProblemEnableGravity, true); + +// Use forward differences instead of central differences +SET_INT_PROP(SagdCyclicHystProblemTypeTag, ImplicitNumericDifferenceMethod, +1); + +// Write newton convergence +SET_BOOL_PROP(SagdCyclicHystProblemTypeTag, NewtonWriteConvergence, false); + +SET_BOOL_PROP(SagdCyclicHystProblemTypeTag, UseSimpleModel, true); + +SET_BOOL_PROP(SagdCyclicHystProblemTypeTag, UseMoles, true); + +} // end namespace Properties + +/*! + * \ingroup ThreePWaterOilBoxModel + * \ingroup ImplicitTestProblems + * \brief Non-isothermal problem where ... + * + * This problem uses the \ref ThreePWaterOilModel. + * + * */ +template +class SagdCyclicHystProblem : public PorousMediumFlowProblem +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar) + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Grid = typename GridView::Grid; + using ParentType = PorousMediumFlowProblem; + // copy some indices for convenience + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + static constexpr int numPhases = GET_PROP_VALUE(TypeTag, NumPhases); + static constexpr int numComponents = GET_PROP_VALUE(TypeTag, NumComponents); + static constexpr int pressureIdx = Indices::pressureIdx; + static constexpr int switch1Idx = Indices::switch1Idx; + static constexpr int switch2Idx = Indices::switch2Idx; + static constexpr int energyEqIdx = Indices::energyEqIdx; + // phase and component indices + static constexpr int wPhaseIdx = Indices::Phase0Idx; + static constexpr int nPhaseIdx = Indices::Phase1Idx; + static constexpr int gPhaseIdx = Indices::Phase2Idx; + static constexpr int wCompIdx = Indices::Comp0Idx; + static constexpr int nCompIdx = Indices::Comp1Idx; + // Phase State + static constexpr int wPhaseOnly = Indices::wPhaseOnly; + static constexpr int wnPhaseOnly = Indices::wnPhaseOnly; + static constexpr int wgPhaseOnly = Indices::wgPhaseOnly; + static constexpr int threePhases = Indices::threePhases; + // Grid and world dimension + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using Vertex = typename GridView::template Codim::Entity; + using Intersection = typename GridView::Intersection; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using FluidSystem = typedef typename GET_PROP_TYPE(TypeTag, FluidSystem); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using ElementIterator = typename GridView::template Codim<0>::Iterator; + using VertexIterator = typename GridView::template Codim::Iterator; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + static constexpr bool isBox = FVGridGeometry::discMethod == DiscretizationMethod::box; + static constexpr int dofCodim = isBox ? dim : 0; + static const bool useMoles = GET_PROP_VALUE(TypeTag, UseMoles); + +public: + /*! + * \brief The constructor + * + * \param timeLoop The time manager + * \param gridView The grid view + */ + SagdCyclicHystProblem(Tstd::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry), pOut_(4.0e6) + { + name_ = getParam("Problem.Name"); + maxDepth_ = 400.0; // [m] + FluidSystem::init(); + totalMassProducedOil_ =0; + totalMassProducedWater_ =0; + + this->timeLoop().startNextEpisode(3600*12.); + + //stateing in the console whether mole or mass fractions are used + if (!useMoles) + std::cout<<"Problem uses mass-fractions."<timeLoop().timeStepIndex() == 0 || + this->timeLoop().timeStepIndex() % 10000 == 0 || + this->timeLoop().episodeWillBeFinished() || + this->timeLoop().willBeFinished(); + } + + bool shouldWriteOutput() const + { + return + this->timeLoop().timeStepIndex() == 0 || + this->timeLoop().timeStepIndex() % 10000 == 0 || + this->timeLoop().episodeWillBeFinished() || + this->timeLoop().willBeFinished(); + } + + void postTimeStep() + { + const Scalar time = this->timeLoop().time(); + const Scalar dt = this->timeLoop().timeStepSize(); + + // Calculate storage terms + PrimaryVariables storage; + this->model().globalStorage(storage); + + // Write mass balance information for rank 0 + if (this->gridView().comm().rank() == 0) + { + std::cout<<"Storage: " << storage << " Time: " << time+dt << std::endl; + massBalance.open ("massBalanceCyclicHyst.txt", std::ios::out | std::ios::app ); + massBalance << " Storage " << storage + << " Time " << time+dt + << std::endl; + massBalance.close(); + } + + + this->spatialParams().getMaxSaturation(*this); + this->spatialParams().trappedSat(*this); + + //mass of Oil + const Scalar newMassProducedOil_ = massProducedOil_; + + totalMassProducedOil_ += newMassProducedOil_; + //mass of Water + Scalar newMassProducedWater_ = massProducedWater_; + + totalMassProducedWater_ += newMassProducedWater_; + + const int timeStepIndex = this->timeLoop().timeStepIndex(); + if (timeStepIndex == 0 || + timeStepIndex % 100 == 0 || //after every 1000000 secs + this->timeLoop().episodeWillBeFinished() || + this->timeLoop().willBeFinished()) + + { + std::cout<<" totalMassProducedOil_ : "<< totalMassProducedOil_ << " Time: " << time+dt << std::endl; + std::cout<<" totalMassProducedWater_ : "<< totalMassProducedWater_ << " Time: " << time+dt << std::endl; + } + } + + void episodeEnd() + { + int indexEpisode = this->timeLoop().episodeIndex(); + this->timeLoop().startNextEpisode(3600*12); //episode length sent to 12 hours + std::cout<<"Episode index is set to: "< 40 - eps_) + bcTypes.setAllNeumann(); + + else if (globalPos[0] > 60 - eps_) + bcTypes.setAllDirichlet(); + + // on left + else + bcTypes.setAllNeumann(); + + return bcTypes; + } + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * boundary segment. + * + * \param values The dirichlet values for the primary variables + * \param globalPos The position for which the bc type should be evaluated + * + * For this method, the \a values parameter stores primary variables. + */ + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const + { + PrimaryVariables values; + initial_(values, globalPos); + + return values; + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations + * \param element The finite element + * \param fvGeomtry 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 solDependentNeumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx, + const ElementVolumeVariables &elemVolVars) const + { + values = 0.0; + const int indexEpisode = this->timeLoop().episodeIndex(); + + GlobalPosition globalPos; + if (isBox) + globalPos = element.geometry().corner(scvIdx); + + else + globalPos = is.geometry().center(); + + if (globalPos[1] > 8.5 + eps_ && globalPos[1] < 9.5 - eps_ ) + { + + if (indexEpisode % 2 == 0) // if episodeIndex is even >> noInjection Phase + { + values[Indices::contiNEqIdx] = 0.0; + values[Indices::contiWEqIdx] = 0.0; + values[Indices::energyEqIdx] = 0.0; + } + + else + {// if episodeIndex is odd >> Injection Phase + values[Indices::contiNEqIdx] = -0.0; + values[Indices::contiWEqIdx] = -0.193*2; // (55.5 mol*47.6)/3600 mol/s m = 0.733833333333 + values[Indices::energyEqIdx] = -9132*2; // J/sec m 34788.3611111 + } + } + + else if (globalPos[1] > 2.5 + eps_ && globalPos[1] < 3.5 - eps_) // production well + { + const Scalar elemPressW = elemVolVars[scvIdx].pressure(wPhaseIdx); //Pressures + // const Scalar elemPressG = elemVolVars[scvIdx].pressure(gPhaseIdx); + const Scalar elemPressN = elemVolVars[scvIdx].pressure(nPhaseIdx); + + const Scalar densityW = elemVolVars[scvIdx].fluidState().density(wPhaseIdx); //Densities + // const Scalar densityG = elemVolVars[scvIdx].fluidState().density(gPhaseIdx); + const Scalar densityN = elemVolVars[scvIdx].fluidState().density(nPhaseIdx); + + const Scalar elemMobW = elemVolVars[scvIdx].mobility(wPhaseIdx); //Mobilities + // const Scalar elemMobG = elemVolVars[scvIdx].mobility(gPhaseIdx); + const Scalar elemMobN = elemVolVars[scvIdx].mobility(nPhaseIdx); + + const Scalar enthW = elemVolVars[scvIdx].enthalpy(wPhaseIdx); //Mobilities + // const Scalar enthG = elemVolVars[scvIdx].enthalpy(gPhaseIdx); + const Scalar enthN = elemVolVars[scvIdx].enthalpy(nPhaseIdx); + + const Scalar wellRadius = 0.50 * 0.3048; // 0.50 ft as specified by SPE9 + // const Scalar wellArea = M_PI*std::pow(wellRadius,2); // [m^2] + + const Scalar gridHeight_ = 0.5; + const Scalar effectiveRadius_ = 0.208 * gridHeight_; //Peaceman's Well Model + + const Scalar qW = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * //divided by molarMass() of water to convert from kg/m s to mol/m s + densityW * elemMobW * ( elemPressW-pOut_))/0.01801528; + + const Scalar qN = (((2*3.1415*0.5*4e-14)/(std::log(effectiveRadius_/wellRadius))) * + densityN * elemMobN * (elemPressN-pOut_))/0.35; //divided by molarMass() of HeavyOil to convert from kg/m s to mol/m s + + //without cooling: + // const Scalar qE = qW*0.018*enthW + qN*enthN*0.350; + + //with cooling: see Diplomarbeit Stefan Roll, Sept. 2015 + Scalar wT = elemVolVars[scvIdx].temperature(); // well temperature + Scalar qE; + if ( wT > 495. ) + { + qE = qW*0.018*enthW + qN*enthN*0.350 + (wT-495.)*5000.; // ~3x injected enthalpy + std::cout<< "Cooling now! Extracted enthalpy: " << qE << std::endl; + } + + else + { + qE = qW*0.018*enthW + qN*enthN*0.350; + } + + values[Indices::contiWEqIdx] = qW; + values[Indices::contiNEqIdx] = qN; + values[Indices::energyEqIdx] = qE; + + massProducedOil_ = qN; + massProducedWater_ = qW; + } + } + + // \} + + /*! + * \name Volume terms + */ + // \{ + + /*! + * \brief Evaluate the initial value for a control volume. + * + * \param values The initial values for the primary variables + * \param globalPos The position for which the initial condition should be evaluated + * + * For this method, the \a values parameter stores primary + * variables. + */ + void initialAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + { + initial_(values, globalPos); + } + + /*! + * \brief Return the initial phase state inside a control volume. + * + * \param globalPos The global position + */ + int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const + { + return wnPhaseOnly; + } + +private: + // internal method for the initial condition (reused for the dirichlet conditions!) + void initial_(PrimaryVariables &values, const GlobalPosition &globalPos) const + { + Scalar densityW = 1000.0; + values[pressureIdx] = 101300. + (maxDepth_ - globalPos[1])*densityW*9.81; + + values[switch1Idx] = 295.13; // temperature + values[switch2Idx] = 0.3; //NAPL saturation + } + + Scalar maxDepth_; + static constexpr Scalar eps_ = 1e-6 + Scalar pIn_; + Scalar pOut_; + //int vectorSize; + Scalar episodeLength_; + std::string name_; + Scalar totalMassProducedOil_; + Scalar totalMassProducedWater_; + + // TODO very evil hack + mutable Scalar massProducedOil_; + mutable Scalar massProducedWater_; + std::ofstream massBalance; + +}; + +} //end namespace Dumux + +#endif // DUMUX_SAGDCYCLICHYSTPROBLEM_HH diff --git a/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.cc b/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.cc index 9386aef41f28c61c7a483514b40bc3403040ffde..c595ccc812c504182c763b5479a407aa55a0301f 100644 --- a/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.cc +++ b/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.cc @@ -18,19 +18,41 @@ *****************************************************************************/ /*! * \file - * - * \brief test for the 3p3cni box model + * \ingroup OnePTests + * \brief Test for the three-phase three-component box model */ -#include "config.h" -#include "problem.hh" -#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include "problem.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. + * \ingroup OnePTests * \param errorMsg The error message that was issued by the start function. * Comprises the thing that went wrong and a general help message. */ @@ -42,19 +64,183 @@ void usage(const char *progName, const std::string &errorMsg) 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 DGF format\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 DGF format\n"; std::cout << errorMessageOut << "\n"; } } -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(ThreePWaterOilSagdCyclicHystBoxTypeTag); + + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + VtkOutputFields::init(vtkWriter); //! Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto episodeIdx = 0.0; + auto timeLoop = std::make_shared>(0, dt, tEnd); + problem->setEpisodeIdx(episodeIdx); + timeLoop->setMaxTimeStepSize(maxDt); + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength", std::numeric_limits::max())); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = Dumux::AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + PrimaryVariables storage(0); + const auto& localResidual = assembler->localResidual(); + for (const auto& element : elements(leafGridView, Dune::Partitions::interior)) + { + auto storageVec = localResidual.evalStorage(*problem, element, *fvGridGeometry, *gridVariables, x); + storage += storageVec[0]; + } + problem->postTimeStep(*timeLoop, storage, x); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // report and count episodes and tell the problem when advancing to the next episode + if (hasParam("TimeLoop.EpisodeLength")) + if(timeLoop->isCheckPoint()) + { + std::cout << "\n Episode " << episodeIdx << " done \n" << std::endl; + + episodeIdx++; + problem->setEpisodeIdx(episodeIdx); + } + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + +} + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(SagdCyclicHystBoxProblem) ProblemTypeTag; - //typedef TTAG(SagdCCProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.cc.SAVE.cc b/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.cc.SAVE.cc new file mode 100644 index 0000000000000000000000000000000000000000..4cccd86fbb7f677b4cc2b1c205ace344b74a648f --- /dev/null +++ b/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.cc.SAVE.cc @@ -0,0 +1,231 @@ +// -*- 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 . * + *****************************************************************************/ +/*! + * \file + * + * \brief test for the 3p3cni box model + */ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include "problem.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 DGF format\n"; + + std::cout << errorMessageOut + << "\n"; + } +} + +//////////////////////// +// the main function +//////////////////////// +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(SagdCyclicHystBoxProblemTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) // Fehlt ebenso + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*problem, *fvGridGeometry, *gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = Dumux::NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the Newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + + +} + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) +{ + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; +} diff --git a/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.input b/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.input index 465d21917f9e1911d939632a09b82a01d58cbb1b..01d16effc40ee5dee31fa1eb0dc3913c5e3098d2 100644 --- a/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.input +++ b/lecture/mm/heavyoil/sagdcyclichyst/sagd_cyclic_hyst.input @@ -1,4 +1,4 @@ -[TimeManager] +[TimeLoop] DtInitial = 100 # [s] TEnd = 31467600 # [s] a year MaxTimeStepSize = 1800 # [s] @@ -17,3 +17,8 @@ MaxTimeStepDivisions = 100 RelTolerance = 1e-02 MaxSteps = 8 WriteConvergence = 0 + +[Component] +SolidDensity = 2650 +SolidThermalConductivity = 2.8 +SolidHeatCapacity = 850 diff --git a/lecture/mm/heavyoil/sagdcyclichyst/spatialparams.hh b/lecture/mm/heavyoil/sagdcyclichyst/spatialparams.hh index 8bac29b9a7ca40bacbb148b2bf06fbef17e86b51..9ce2402796e080d446ca593135ac9a285af833b1 100644 --- a/lecture/mm/heavyoil/sagdcyclichyst/spatialparams.hh +++ b/lecture/mm/heavyoil/sagdcyclichyst/spatialparams.hh @@ -18,242 +18,160 @@ *****************************************************************************/ /*! * \file - * + * \ingroup ThreePWaterOilTests * \brief Definition of the spatial parameters for the SAGD problem. */ -#ifndef DUMUX_SAGDCYCLICHYST_SPATIAL_PARAMS_HH -#define DUMUX_SAGDCYCLICHYST_SPATIAL_PARAMS_HH -#include +#ifndef DUMUX_SAGD_SPATIAL_PARAMS_HH +#define DUMUX_SAGD_SPATIAL_PARAMS_HH + +#include +#include #include + #include "../3p/parkervangenuchtenzerohysteresis.hh" #include "../3p/parkervangenuchtenzerohysteresisparams.hh" -namespace Dumux -{ -//forward declaration -template -class SagdCyclicHystSpatialParams; -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(SagdCyclicHystSpatialParams); -// Set the spatial parameters -SET_TYPE_PROP(SagdCyclicHystSpatialParams, SpatialParams, Dumux::SagdCyclicHystSpatialParams); -// Set the material Law -SET_PROP(SagdCyclicHystSpatialParams, MaterialLaw) -{ - private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - public: - typedef ParkerVanGenZeroHyst3P type; -}; -} + +namespace Dumux { + /*! * \ingroup ThreePThreeCNIModel * * \brief Definition of the spatial parameters for the SAGD problem */ -template -class SagdCyclicHystSpatialParams : public ImplicitSpatialParams +template +class SagdSpatialParams +: public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; - typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - - numPhases = GET_PROP_VALUE(TypeTag, NumPhases), - numComponents = GET_PROP_VALUE(TypeTag, NumComponents), - - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - gPhaseIdx = Indices::gPhaseIdx - }; - - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldVector DimVector; + using GridView = typename FVGridGeometry::GridView; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + enum { dimWorld=GridView::dimensionworld }; - typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + using GlobalPosition = typename SubControlVolume::GlobalPosition; - typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; - - typedef typename GridView::template Codim<0>::Iterator ElementIterator; - typedef typename GridView::template Codim::Iterator VertexIterator; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - - enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; - enum { dofCodim = isBox ? dim : 0 }; + using Element = typename GridView::template Codim<0>::Entity; + using ParentType = FVSpatialParams>; + static constexpr bool isBox = FVGridGeometry::discMethod == DiscretizationMethod::box; + static constexpr int dofCodim = isBox ? GridView::dimension : 0; + // using EffectiveLaw = RegularizedParkerVanGen3P; public: - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; - typedef std::vector MaterialLawHystParamsVector; + //using MaterialLaw = EffToAbsLaw; + using MaterialLaw = ParkerVanGenZeroHyst3P; + using MaterialLawParams = typename MaterialLaw::Params; + using PermeabilityType = Scalar; /*! * \brief The constructor * * \param gridView The grid view */ - SagdCyclicHystSpatialParams(const GridView &gridView) - : ParentType(gridView) + SagdSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry), eps_(1e-6) { layerBottom_ = 35.0; + // intrinsic permeabilities fineK_ = 1e-16; coarseK_ = 4e-14; + // porosities finePorosity_ = 0.10; coarsePorosity_ = 0.1; - // heat conductivity of granite - lambdaSolid_ = 2.8; - // specific heat capacities - fineHeatCap_ = 850.; - coarseHeatCap_ = 850; + // residual saturations fineMaterialParams_.setSwr(0.1); - fineMaterialParams_.setSwrx(0.12); //Total liquid Residual Saturation fineMaterialParams_.setSnr(0.09); //Residual of NAPL if there is no water fineMaterialParams_.setSgr(0.01); coarseMaterialParams_.setSwr(0.1); - coarseMaterialParams_.setSwrx(0.12); coarseMaterialParams_.setSnr(0.09); coarseMaterialParams_.setSgr(0.01); // parameters for the 3phase van Genuchten law fineMaterialParams_.setVgn(4.0); coarseMaterialParams_.setVgn(4.0); - fineMaterialParams_.setVgAlpha(0.0005); - coarseMaterialParams_.setVgAlpha(0.0015); + fineMaterialParams_.setVgAlpha(1.); + coarseMaterialParams_.setVgAlpha(1.); + coarseMaterialParams_.setKrRegardsSnr(false); fineMaterialParams_.setKrRegardsSnr(false); - } /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. + * \brief Function for defining the (intrinsic) permeability \f$[m^2]\f$ + * \note It is possibly solution dependent. * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element. + * \return permeability */ - Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + template + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { return permeabilityAtPos(scv.dofPosition());} + + /*! + * \brief Returns the intrinsic permeability tensor \f$[m^2]\f$ + * + * \param globalPos The global position + */ + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) + if (isFineMaterial_(globalPos)) return fineK_; return coarseK_; } /*! - * \brief Define the porosity \f$[-]\f$ of the spatial parameters + * \brief Returns the porosity \f$[-]\f$ * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined + * \param globalPos The global position */ - Scalar porosity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + Scalar porosityAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) + if (isFineMaterial_(globalPos)) return finePorosity_; else return coarsePorosity_; } - /*! - * \brief return the parameter object for the Brooks-Corey material law which depends on the position + * \brief Function for defining the parameters needed by constitutive relationships (kr-sw, pc-sw, etc.). * - * \param element The current finite element - * \param fvGeometry The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param element The current element + * \param scv The sub-control volume inside the element. + * \param elemSol The solution at the dofs connected to the element. + * \return the material parameters object */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + template + const MaterialLawParams& materialLawParams(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineMaterialParams_; - else - return coarseMaterialParams_; - - + return materialLawParamsAtPos(scv.dofPosition()); } /*! - * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. + * \brief Returns the parameter object for the capillary-pressure/ + * saturation material law * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume + * \param globalPos The global position */ - Scalar solidHeatCapacity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { - const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; - if (isFineMaterial_(pos)) - return fineHeatCap_; + if (isFineMaterial_(globalPos)) + return fineMaterialParams_; else - return coarseHeatCap_; - } - - /*! - * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidDensity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return 2650; // density of sand [kg/m^3] - } - - /*! - * \brief Returns the thermal conductivity \f$\mathrm{[W/(m K)]}\f$ of the solid - * - * This is only required for non-isothermal models. - * - * \param element The finite element - * \param fvGeometry The finite volume geometry of the element - * \param scvIdx The local index of the sub-control volume - */ - Scalar solidThermalConductivity(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const - { - return lambdaSolid_; + return coarseMaterialParams_; } struct MaxSaturations @@ -268,63 +186,57 @@ public: Scalar trappedSatN; }; - void getMaxSaturation(Problem &problem) + template + void getMaxSaturation(Problem &problem, const SolutionVector& x) { // get the number of degrees of freedom - FVElementGeometry fvGeometry; + FVElementGeometry fvGeometry(this->fvGridGeometry()); VolumeVariables volVars; - maxSats_.resize(problem.model().numDofs()); + maxSats_.resize(this->fvGridGeometry().numDofs()); - for (const auto& element : elements(problem.gridView())) + for (const auto& element : elements(this->fvGridGeometry().gridView())) { - fvGeometry.update(problem.gridView(), element); + fvGeometry.bindElement(element); + auto elemSol = elementSolution(element, x, this->fvGridGeometry()); - for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) + for (auto&& scv : scvs(fvGeometry)) { - const unsigned int dofIdxGlobal = problem.model().dofMapper().subIndex(element, scvIdx, dofCodim); - volVars.update(problem.model().curSol()[dofIdxGlobal], - problem, - element, - fvGeometry, - scvIdx, - false); - - if (volVars.saturation(wPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatW) - maxSats_[dofIdxGlobal].MaxSatW = volVars.saturation(wPhaseIdx); + const auto dofIdxGlobal = scv.dofIndex(); + volVars.update(elemSol, problem, element, scv); - if (volVars.saturation(wPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatW) - maxSats_[dofIdxGlobal].MaxSatW = volVars.saturation(wPhaseIdx); + if (volVars.saturation(FluidSystem::wPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatW) + maxSats_[dofIdxGlobal].MaxSatW = volVars.saturation(FluidSystem::wPhaseIdx); - if (volVars.saturation(nPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatN) - maxSats_[dofIdxGlobal].MaxSatN = volVars.saturation(nPhaseIdx); + if (volVars.saturation(FluidSystem::nPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatN) + maxSats_[dofIdxGlobal].MaxSatN = volVars.saturation(FluidSystem::nPhaseIdx); - if (volVars.saturation(gPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatG) - maxSats_[dofIdxGlobal].MaxSatG = volVars.saturation(gPhaseIdx); + if (volVars.saturation(FluidSystem::gPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatG) + maxSats_[dofIdxGlobal].MaxSatG = volVars.saturation(FluidSystem::gPhaseIdx); } } } - void trappedSat(Problem &problem) + template + void trappedSat(Problem &problem, const SolutionVector& x) { // get the number of degrees of freedom - FVElementGeometry fvGeometry; + FVElementGeometry fvGeometry(this->fvGridGeometry()); VolumeVariables volVars; - trapSats_.resize(problem.model().numDofs()); + trapSats_.resize(this->fvGridGeometry().numDofs()); - for (const auto& element : elements(problem.gridView())) + for (const auto& element : elements(this->fvGridGeometry().gridView())) { - fvGeometry.update(problem.gridView(), element); - for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) + fvGeometry.bindElement(element); + auto elemSol = elementSolution(element, x, this->fvGridGeometry()); + + for (auto&& scv : scvs(fvGeometry)) { - const unsigned int dofIdxGlobal = problem.model().dofMapper().subIndex(element, scvIdx, dofCodim); - volVars.update(problem.model().curSol()[dofIdxGlobal], - problem, - element, - fvGeometry, - scvIdx, - false); + const auto dofIdxGlobal = scv.dofIndex(); + volVars.update(elemSol, problem, element, scv); + + static const Scalar landParameter = 9.0; // according to Land, 1968 trapSats_[dofIdxGlobal].trappedSatN = - (maxSats_[dofIdxGlobal].MaxSatN)/(1+9*maxSats_[dofIdxGlobal].MaxSatN); + (maxSats_[dofIdxGlobal].MaxSatN)/(1.0 + landParameter*maxSats_[dofIdxGlobal].MaxSatN); coarseMaterialParams_.setTrappedSatN(trapSats_[dofIdxGlobal].trappedSatN); fineMaterialParams_.setTrappedSatN(trapSats_[dofIdxGlobal].trappedSatN); @@ -335,13 +247,13 @@ public: private: bool isFineMaterial_(const GlobalPosition &pos) const { - return pos[dim-1] > layerBottom_ - eps_; - } + return pos[dimWorld-1] > layerBottom_ - eps_; + }; std::vector trapSats_; + std::vector maxSats_; Scalar layerBottom_; - Scalar lambdaSolid_; Scalar fineK_; Scalar coarseK_; @@ -349,18 +261,12 @@ private: Scalar finePorosity_; Scalar coarsePorosity_; - Scalar fineHeatCap_; - Scalar coarseHeatCap_; - MaterialLawParams fineMaterialParams_; MaterialLawParams coarseMaterialParams_; - std::vector maxSats_; - - MaterialLawHystParamsVector materialLawHystParams_; - static constexpr Scalar eps_ = 1.5e-7; + Scalar eps_; }; -} // end namespace Dumux +} #endif diff --git a/lecture/mm/heavyoil/sagdcyclichyst/spatialparams.hh.SAVE.hh b/lecture/mm/heavyoil/sagdcyclichyst/spatialparams.hh.SAVE.hh new file mode 100644 index 0000000000000000000000000000000000000000..f3d99d839dda73612f53f9e48056ac48a1cfb94e --- /dev/null +++ b/lecture/mm/heavyoil/sagdcyclichyst/spatialparams.hh.SAVE.hh @@ -0,0 +1,350 @@ +// -*- 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 . * + *****************************************************************************/ +/*! + * \file + * + * \brief Definition of the spatial parameters for the SAGD problem. + */ +#ifndef DUMUX_SAGDCYCLICHYST_SPATIAL_PARAMS_HH +#define DUMUX_SAGDCYCLICHYST_SPATIAL_PARAMS_HH + +#include + +#include +#include +#include "../3p/parkervangenuchtenzerohysteresis.hh" +#include "../3p/parkervangenuchtenzerohysteresisparams.hh" + +namespace Dumux { + +//forward declaration +template +class SagdCyclicHystSpatialParams; + +namespace Properties { + +// The spatial parameters TypeTag +NEW_TYPE_TAG(SagdCyclicHystSpatialParamsTypeTag); + +// Set the spatial parameters +SET_TYPE_PROP(SagdCyclicHystSpatialParamsTypeTag, SpatialParams, Dumux::SagdCyclicHystSpatialParams); + +} // end namespace Properties + +/*! + * \ingroup ThreePThreeCNIModel + * + * \brief Definition of the spatial parameters for the SAGD problem + */ +template +class SagdCyclicHystSpatialParams : public FVSpatialParams> +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using ParentType = FVSpatialParams >; + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Grid = typename GET_PROP_TYPE(TypeTag, Grid); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using CoordScalar = typename Grid::ctype; + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld=GridView::dimensionworld; + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using ModelTraits = typename GET_PROP_TYPE(TypeTag, ModelTraits); + static constexpr int numPhases = ModelTraits::numPhases; + static constexpr int numComponents = ModelTraits::NumComponents; + static constexpr int wPhaseIdx = Indices::Phase0Idx; + static constexpr int nPhaseIdx = Indices::Phase1Idx; + static constexpr int gPhaseIdx = Indices::Phase2Idx; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordiante; +// TODO: What is this used for? Is this correct? + using DimVetor = Dune::FieldVector; + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + using FluxVariables = typename GET_PROP_TYPE(TypeTag, FluxVariables); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, GridVolumeVariables)::LocalView; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using ElementIterator = typename GridView::template Codim<0>::Iterator; + using VertexIterator = typename GridView::template Codim::Iterator; + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + static constexpr auto isBox = (FVGridGeometry::discMethod == DiscretizationMethod::box); + static constexpr auto dofCodim = isBox ? dim : 0; + +public: + using EffectiveLaw = ParkerVanGenZeroHyst3P; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; + using MaterialLawHystParamsVector = std::vector; + + /*! + * \brief The constructor + * + * \param gridView The grid view + */ + SagdCyclicHystSpatialParams(std::shared_ptr fvGridGeometry) : ParentType(fvGridGeometry) + { + layerBottom_ = 35.0; + // intrinsic permeabilities + fineK_ = 1e-16; + coarseK_ = 4e-14; + // porosities + finePorosity_ = 0.10; + coarsePorosity_ = 0.1; + // heat conductivity of granite + lambdaSolid_ = 2.8; + // specific heat capacities + fineHeatCap_ = 850.; + coarseHeatCap_ = 850; + // residual saturations + fineMaterialParams_.setSwr(0.1); + fineMaterialParams_.setSwrx(0.12); //Total liquid Residual Saturation + fineMaterialParams_.setSnr(0.09); //Residual of NAPL if there is no water + fineMaterialParams_.setSgr(0.01); + coarseMaterialParams_.setSwr(0.1); + coarseMaterialParams_.setSwrx(0.12); + coarseMaterialParams_.setSnr(0.09); + coarseMaterialParams_.setSgr(0.01); + + // parameters for the 3phase van Genuchten law + fineMaterialParams_.setVgn(4.0); + coarseMaterialParams_.setVgn(4.0); + fineMaterialParams_.setVgAlpha(0.0005); + coarseMaterialParams_.setVgAlpha(0.0015); + coarseMaterialParams_.setKrRegardsSnr(false); + fineMaterialParams_.setKrRegardsSnr(false); + } + + /*! + * \brief Apply the intrinsic permeability tensor to a pressure + * potential gradient. + * + * \param element The current finite element + * \param fvElemGeom The current finite volume geometry of the element + * \param scvIdx The index of the sub-control volume + */ + Scalar intrinsicPermeability(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return fineK_; + + return coarseK_; + } + + /*! + * \brief Define the porosity \f$[-]\f$ of the spatial parameters + * + * \param element The finite element + * \param fvGeometry The finite volume geometry + * \param scvIdx The local index of the sub-control volume where + * the porosity needs to be defined + */ + Scalar porosity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return finePorosity_; + + else + return coarsePorosity_; + } + + /*! + * \brief return the parameter object for the Brooks-Corey material law which depends on the position + * + * \param element The current finite element + * \param fvGeometry The current finite volume geometry of the element + * \param scvIdx The index of the sub-control volume + */ + const MaterialLawParams& materialLawParams(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return fineMaterialParams_; + + else + return coarseMaterialParams_; + } + + /*! + * \brief Returns the heat capacity \f$[J / (kg K)]\f$ of the rock matrix. + * + * This is only required for non-isothermal models. + * + * \param element The finite element + * \param fvGeometry The finite volume geometry + * \param scvIdx The local index of the sub-control volume + */ + Scalar solidHeatCapacity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + const GlobalPosition &pos = fvGeometry.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return fineHeatCap_; + + else + return coarseHeatCap_; + } + + /*! + * \brief Returns the mass density \f$[kg / m^3]\f$ of the rock matrix. + * + * This is only required for non-isothermal models. + * + * \param element The finite element + * \param fvGeometry The finite volume geometry + * \param scvIdx The local index of the sub-control volume + */ + Scalar solidDensity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + return 2650; // density of sand [kg/m^3] + } + + /*! + * \brief Returns the thermal conductivity \f$\mathrm{[W/(m K)]}\f$ of the solid + * + * This is only required for non-isothermal models. + * + * \param element The finite element + * \param fvGeometry The finite volume geometry of the element + * \param scvIdx The local index of the sub-control volume + */ + Scalar solidThermalConductivity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + return lambdaSolid_; + } + + struct MaxSaturations + { + Scalar MaxSatW; + Scalar MaxSatN; + Scalar MaxSatG; + }; + + struct trappedSaturations + { + Scalar trappedSatN; + }; + + void getMaxSaturation(Problem &problem) + { + // get the number of degrees of freedom + FVElementGeometry fvGeometry; + VolumeVariables volVars; + maxSats_.resize(problem.model().numDofs()); + + for (const auto& element : elements(problem.gridView())) + { + fvGeometry.update(problem.gridView(), element); + + for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) + { + const unsigned int dofIdxGlobal = problem.model().dofMapper().subIndex(element, scvIdx, dofCodim); + volVars.update(problem.model().curSol()[dofIdxGlobal], + problem, + element, + fvGeometry, + scvIdx, + false); + + if (volVars.saturation(wPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatW) + maxSats_[dofIdxGlobal].MaxSatW = volVars.saturation(wPhaseIdx); + + if (volVars.saturation(wPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatW) + maxSats_[dofIdxGlobal].MaxSatW = volVars.saturation(wPhaseIdx); + + if (volVars.saturation(nPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatN) + maxSats_[dofIdxGlobal].MaxSatN = volVars.saturation(nPhaseIdx); + + if (volVars.saturation(gPhaseIdx)>maxSats_[dofIdxGlobal].MaxSatG) + maxSats_[dofIdxGlobal].MaxSatG = volVars.saturation(gPhaseIdx); + } + } + } + + void trappedSat(Problem &problem) + { + // get the number of degrees of freedom + FVElementGeometry fvGeometry; + VolumeVariables volVars; + trapSats_.resize(problem.model().numDofs()); + + for (const auto& element : elements(problem.gridView())) + { + fvGeometry.update(problem.gridView(), element); + for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) + { + const unsigned int dofIdxGlobal = problem.model().dofMapper().subIndex(element, scvIdx, dofCodim); + volVars.update(problem.model().curSol()[dofIdxGlobal], + problem, + element, + fvGeometry, + scvIdx, + false); + trapSats_[dofIdxGlobal].trappedSatN = + (maxSats_[dofIdxGlobal].MaxSatN)/(1+9*maxSats_[dofIdxGlobal].MaxSatN); + + coarseMaterialParams_.setTrappedSatN(trapSats_[dofIdxGlobal].trappedSatN); + fineMaterialParams_.setTrappedSatN(trapSats_[dofIdxGlobal].trappedSatN); + } + } + } + +private: + bool isFineMaterial_(const GlobalPosition &pos) const + { + return pos[dim-1] > layerBottom_ - eps_; + } + + std::vector trapSats_; + + Scalar layerBottom_; + Scalar lambdaSolid_; + + Scalar fineK_; + Scalar coarseK_; + + Scalar finePorosity_; + Scalar coarsePorosity_; + + Scalar fineHeatCap_; + Scalar coarseHeatCap_; + + MaterialLawParams fineMaterialParams_; + MaterialLawParams coarseMaterialParams_; + std::vector maxSats_; + + MaterialLawHystParamsVector materialLawHystParams_; + + static constexpr Scalar eps_ = 1.5e-7; +}; + +} // end namespace Dumux + +#endif // DUMUX_SAGDCYCLIC_SPATIAL_PARAMS_HH diff --git a/lecture/mm/henryproblem/henry1p2c/henry1p2c.cc b/lecture/mm/henryproblem/henry1p2c/henry1p2c.cc index 147c10dfba29aa446921a6cfdf592f8564720fa6..9a1696d1966b37f10d9e10e9de22077b0f736b33 100644 --- a/lecture/mm/henryproblem/henry1p2c/henry1p2c.cc +++ b/lecture/mm/henryproblem/henry1p2c/henry1p2c.cc @@ -21,12 +21,30 @@ * * \brief test for the 1p2c box model */ -#include "config.h" - +#include +#include +#include #include "henry1p2cproblem.hh" +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include -#include +#include +#include +#include + +#include +#include void usage(const char *progName, const std::string &errorMsg) { @@ -51,9 +69,151 @@ void usage(const char *progName, const std::string &errorMsg) } } -int main(int argc, char** argv) +//////////////////////// +// the main function +//////////////////////// +int main(int argc, char** argv) try { - typedef TTAG(Henry1p2cProblem) TypeTag; - return Dumux::start(argc, argv, usage); + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(Henry1p2cProblemTypeTag); + + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // initialize parameter tree + Parameters::init(argc, argv); + + ////////////////////////////////////////////////////////////////////// + // try to create a grid (from the given grid file or the input file) + ///////////////////////////////////////////////////////////////////// + + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + auto tEnd = getParam("TimeLoop.TEnd"); + auto dt = getParam("TimeLoop.DtInitial"); + auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + + // intialize the vtk output module + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(0.0, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = ILU0BiCGSTABBackend; + auto linearSolver = std::make_shared(); + // the non-linear solver + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/false); + + return 0; +} + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) +{ + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/henryproblem/henry1p2c/henry1p2c.input b/lecture/mm/henryproblem/henry1p2c/henry1p2c.input index 0ace05741e757dcde4bd9f2d495eb7539d83030f..066f4df018af6de4c670e35a3875080af739d32a 100644 --- a/lecture/mm/henryproblem/henry1p2c/henry1p2c.input +++ b/lecture/mm/henryproblem/henry1p2c/henry1p2c.input @@ -1,11 +1,15 @@ +[TimeLoop] +DtInitial = 2.5# [s] +TEnd = 1e12# [s] -TimeManager.DtInitial = 2.5# [s] -TimeManager.TEnd = 1e12# [s] -Grid.File = ./grids/henry.dgf +[Grid] +File = ./grids/henry.dgf -Problem.freshWaterFluxRate = 6.6E-2 # [kg/sm^2] fresh water flux rate at the left boundary -Problem.diffusionCoefficient = 6.6e-6 # 1.2e-9 #[m2/s] Diffusion coefficient - -SpatialParams.dispersivityL=0.1 # longitual dispersivity -SpatialParams.dispersivityT=0.01 # transversial dispersivity +[Problem] +freshWaterFluxRate = 3.66667 # [mol/sm^2] fresh water flux rate at the left boundary +diffusionCoefficient = 6.6e-6 # 1.2e-9 #[m2/s] Diffusion coefficient +Name = Henry1p2c +[SpatialParams] +dispersivityL=0.1 # longitual dispersivity +dispersivityT=0.01 # transversal dispersivity diff --git a/lecture/mm/henryproblem/henry1p2c/henry1p2cproblem.hh b/lecture/mm/henryproblem/henry1p2c/henry1p2cproblem.hh index 0cc2f994381d5459a330e1dacb7b0d6b3d9885e1..e790646027b7a1ef4f34daa6ab05289b7a7a0a49 100644 --- a/lecture/mm/henryproblem/henry1p2c/henry1p2cproblem.hh +++ b/lecture/mm/henryproblem/henry1p2c/henry1p2cproblem.hh @@ -24,128 +24,92 @@ #ifndef DUMUX_HENRY1P2C_PROBLEM_HH #define DUMUX_HENRY1P2C_PROBLEM_HH -#include -#include +#include +#include +#include #include "watersaltfluidsystem.hh" #include "henry1p2cspatialparameters.hh" -namespace Dumux -{ +namespace Dumux { template class Henry1p2cProblem; -namespace Properties -{ -NEW_TYPE_TAG(Henry1p2cProblem, INHERITS_FROM(BoxOnePTwoC)); +namespace Properties { +NEW_TYPE_TAG(Henry1p2cProblemTypeTag, INHERITS_FROM(OnePNC, BoxModel)); // Set the grid type -SET_TYPE_PROP(Henry1p2cProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(Henry1p2cProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(Henry1p2cProblem, Problem, Henry1p2cProblem); +SET_TYPE_PROP(Henry1p2cProblemTypeTag, Problem, Henry1p2cProblem); -SET_TYPE_PROP(Henry1p2cProblem, FluidSystem, WaterSaltFluidSystem); +SET_TYPE_PROP(Henry1p2cProblemTypeTag, FluidSystem, WaterSaltFluidSystem); // Set the spatial parameters -SET_TYPE_PROP(Henry1p2cProblem, +SET_TYPE_PROP(Henry1p2cProblemTypeTag, SpatialParams, Henry1p2cSpatialParams); //Define whether mole(true) or mass (false) fractions are used -SET_BOOL_PROP(Henry1p2cProblem, UseMoles, false); -SET_BOOL_PROP(Henry1p2cProblem, ProblemEnableGravity, true); -} +SET_BOOL_PROP(Henry1p2cProblemTypeTag, UseMoles, true); +} // end namespace Properties template -class Henry1p2cProblem : public ImplicitPorousMediaProblem +class Henry1p2cProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - 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 GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using Element = typename GridView::template Codim<0>::Entity; + using ElementIterator = typename GridView::template Codim<0>::Iterator; + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld, - - // indices of the primary variables - pressureIdx = Indices::pressureIdx, - massOrMoleFracIdx = Indices::massOrMoleFracIdx, - - // indices of the equations - conti0EqIdx = Indices::conti0EqIdx, - transportEqIdx = Indices::transportEqIdx - }; - - - typedef typename GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim<0>::Iterator ElementIterator; - typedef typename GridView::template Codim::Entity Vertex; - typedef typename GridView::Intersection Intersection; - - typedef Dune::FieldVector GlobalPosition; + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using ParentType = PorousMediumFlowProblem; + // Grid and world dimension + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + // indices of the equations + static constexpr int conti0EqIdx = Indices::conti0EqIdx; + static constexpr int transportEqIdx = Indices::transportEqIdx; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using Vertex = typename GridView::template Codim::Entity; + using Intersection = typename GridView::Intersection; public: - Henry1p2cProblem(TimeManager &timeManager, - const GridView &gridView) - : ParentType(timeManager, gridView) - { - -// FVElementGeometry fvGeom; -// ElementIterator elemIt = gridView.template begin<0>(); -// const ElementIterator endIt = gridView.template end<0>(); - - freshWaterFluxRate_= GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.freshWaterFluxRate); - } + Henry1p2cProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) + { + freshWaterFluxRate_= getParam("Problem.freshWaterFluxRate"); + } bool shouldWriteRestartFile() const - { - return false; - } - -// bool shouldWriteOutput() const -// { -// return -// this->timeManager().timeStepIndex() == 0 || -// this->timeManager().timeStepIndex() %5 ==0 || -// this->timeManager().willBeFinished(); -// } - - - + { + return false; + } /*! * \name Problem parameters */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { return "Henry1p2c"; } - /*! * \brief Returns the temperature within the domain. * * This problem assumes a temperature of 36 degrees Celsius. */ Scalar temperature() const - { return 273.15 + 20; }; // in [K] + { + return 273.15 + 20; // in [K] + }; // \} @@ -158,17 +122,18 @@ public: * \brief Specifies which kind of boundary condition should be * used for which equation on a given boundary segment. */ - void boundaryTypes(BoundaryTypes &values, const Vertex &vertex) const + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition globalPos = vertex.geometry().center(); + BoundaryTypes values; values.setAllDirichlet(); - if(globalPos[0]1-eps_) + if (globalPos[0]1-eps_) { values.setAllNeumann(); } + return values; } /*! @@ -177,16 +142,17 @@ public: * * For this method, the \a values parameter stores primary variables. */ - void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition globalPos = vertex.geometry().center(); + PrimaryVariables values; initial_(values, globalPos); - if(globalPos[0]>2-eps_ && globalPos[1]<0.8+eps_) + if (globalPos[0]>2-eps_ && globalPos[1]<0.8+eps_) { - values[massOrMoleFracIdx] = 0.03922; + values[FluidSystem::SaltIdx] = 0.0124171; } + return values; } /*! @@ -197,21 +163,16 @@ public: * in normal direction of each component. Negative values mean * influx. */ - void neumann(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvElemGeom, - const Intersection &is, - int scvIdx, - int boundaryFaceIdx) const + PrimaryVariables neumannAtPos(const GlobalPosition &globalPos) const { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - values = 0; + PrimaryVariables values(0.0); - if(globalPos[0] +#include #include #include #include -namespace Dumux -{ +namespace Dumux { /*! * \ingroup OnePTwoCBoxModel @@ -41,47 +40,40 @@ namespace Dumux * Henry problem */ template -class Henry1p2cSpatialParams : public ImplicitSpatialParamsOneP +class Henry1p2cSpatialParams : public FVSpatialParams > { - typedef ImplicitSpatialParamsOneP ParentType; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - typedef Dune::FieldVector Vector; - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef Dune::FieldVector GlobalPosition; - - - typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; - - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; - + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using ParentType = FVSpatialParams >; + using Grid = typename GET_PROP_TYPE(TypeTag, Grid); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using CoordScalar = typename Grid::ctype; + using Vector = Dune::FieldVector; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + using Element = typename GridView::template Codim<0>::Entity; + static constexpr int dim=GridView::dimension; + static constexpr int dimWorld=GridView::dimensionworld; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); public: - Henry1p2cSpatialParams(const GridView &gv) - : ParentType(gv) + using PermeabilityType = Scalar; + + Henry1p2cSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { // intrinsic permeabilities perm_ = 1.019368e-9; porosity_ = 0.35; - - dispersivity_[0]= GET_RUNTIME_PARAM(TypeTag, Scalar, SpatialParams.dispersivityL); - dispersivity_[1]= GET_RUNTIME_PARAM(TypeTag, Scalar, SpatialParams.dispersivityT); - + dispersivity_[0]= getParam("SpatialParams.dispersivityL"); + dispersivity_[1]= getParam("SpatialParams.dispersivityT"); } ~Henry1p2cSpatialParams() {} - /*! * \brief Update the spatial parameters with the flow solution * after a timestep. @@ -90,32 +82,24 @@ public: */ void update(const SolutionVector &globalSolution) { - }; + } /*! - * \brief Define the intrinsic permeability \f$[m^2]\f$. + * \brief Define the intrinsic permeability \f$\mathrm{[m^2]}\f$. * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume + * \param globalPos The global position */ - const Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const { return perm_; } /*! - * \brief Define the porosity \f$[-]\f$. + * \brief Define the porosity \f$\mathrm{[-]}\f$. * - * \param element The finite element - * \param fvElemGeom The finite volume geometry - * \param scvIdx The local index of the sub-control volume where + * \param globalPos The global position */ - double porosity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + double porosityAtPos(const GlobalPosition& globalPos) const { return porosity_; } @@ -128,8 +112,8 @@ public: * \param scvIdx The local index of the sub-control volume where */ const Vector dispersivity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + const FVElementGeometry &fvElemGeom, + int scvIdx) const { return dispersivity_; } @@ -142,8 +126,6 @@ public: } private: - - Scalar perm_; Scalar porosity_; Scalar tortuosityTumor_; @@ -152,6 +134,6 @@ private: Dune::FieldVector dispersivity_; }; -} +} // end namespace Dumux -#endif +#endif // DUMUX_HENRY1P2C_SPATIAL_PARAMETERS_HH diff --git a/lecture/mm/henryproblem/henry1p2c/watersaltfluidsystem.hh b/lecture/mm/henryproblem/henry1p2c/watersaltfluidsystem.hh index b922ba93d95b21b21a9d22566c88f5f0fde2f23b..bb902d767ce8f25bc8b57f33c5659263ee619257 100644 --- a/lecture/mm/henryproblem/henry1p2c/watersaltfluidsystem.hh +++ b/lecture/mm/henryproblem/henry1p2c/watersaltfluidsystem.hh @@ -1,4 +1,4 @@ -// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + // -*- 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. * @@ -28,18 +28,16 @@ #include +#include + #include #include #include -#ifdef DUMUX_PROPERTIES_HH -#include -#endif -namespace Dumux -{ -namespace FluidSystems -{ +namespace Dumux { + +namespace FluidSystems { /*! * \ingroup Fluidsystems @@ -53,13 +51,11 @@ namespace FluidSystems * used in conjunction the 1p2c model. */ template -class WaterSalt -: public BaseFluidSystem > +class WaterSalt : public Base > { - typedef WaterSalt ThisType; - typedef BaseFluidSystem Base; - typedef Dumux::H2O H2O_IAPWS; - typedef H2O_IAPWS H2O; + using ThisType = WaterSalt; + using BaseT = Base; + using H2O = Dumux::Components::H2O; public: /**************************************** @@ -73,16 +69,16 @@ public: static constexpr int lPhaseIdx = 1; /*! - * \brief Return the human readable name of a fluid phase + * \brief Return the hum1.2e-9an readable name of a fluid phase * * \param phaseIdx The index of the fluid phase to consider */ static std::string phaseName(int phaseIdx) { - static const std::string name[] = { - std::string("l") + static const std::string name[] = + { + std::string("liq") }; - assert(0 <= phaseIdx && phaseIdx < numPhases); return name[phaseIdx]; } @@ -142,7 +138,6 @@ public: //! Number of components in the fluid system static constexpr int numComponents = 2; - //! Index of component representing the interstitial fluid static constexpr int WaterIdx = 0; //! Index of component representing TRAIL @@ -159,7 +154,6 @@ public: std::string("Water"), std::string("Salt") }; - assert(0 <= compIdx && compIdx < numComponents); return name[compIdx]; } @@ -193,6 +187,31 @@ public: static void init() {} + using BaseT::molarDensity; + /*! + * \brief The molar density \f$\rho_{mol,\alpha}\f$ + * of a fluid phase \f$\alpha\f$ in \f$\mathrm{[mol/m^3]}\f$ + * + * The molar density for the simple relation is defined by the + * mass density \f$\rho_\alpha\f$ and the molar mass of the main component + * + * The molar density for the complrex relation is defined by the + * mass density \f$\rho_\alpha\f$ and the mean molar mass \f$\overline M_\alpha\f$: + * + * \f[\rho_{mol,\alpha} = \frac{\rho_\alpha}{\overline M_\alpha} \;.\f] + */ + template + static Scalar molarDensity(const FluidState &fluidState, int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx < numPhases); + + Scalar T = fluidState.temperature(phaseIdx); + Scalar p = fluidState.pressure(phaseIdx); + + // assume pure water or that each gas molecule displaces exactly one + // molecule in the liquid. + return H2O::liquidMolarDensity(T, p); + } /*! * \brief Return the phase density [kg/m^3]. @@ -200,17 +219,16 @@ public: * \param fluidState An abitrary fluid state * \param phaseIdx The index of the fluid phase to consider */ - using Base::density; + using BaseT::density; template - static Scalar density(const FluidState &fluidState, - int phaseIdx) + static Scalar density(const FluidState &fluidState, int phaseIdx) { assert(0 <= phaseIdx && phaseIdx < numPhases); Scalar temperature = fluidState.temperature(phaseIdx); Scalar pressure = fluidState.pressure(phaseIdx); - return liquidDensity1_(temperature, + return liquidDensity1_(temperature, pressure, fluidState.massFraction(phaseIdx, SaltIdx)); } @@ -229,7 +247,7 @@ public: * \param phaseIdx The index of the fluid phase to consider * \param compIdx The index of the component to consider */ - using Base::fugacityCoefficient; + using BaseT::fugacityCoefficient; template static Scalar fugacityCoefficient(const FluidState &fluidState, int phaseIdx, @@ -246,18 +264,11 @@ public: * \param fluidState An abitrary fluid state * \param phaseIdx The index of the fluid phase to consider */ - using Base::viscosity; + using BaseT::viscosity; template - static Scalar viscosity(const FluidState &fluidState, - int phaseIdx) + static Scalar viscosity(const FluidState &fluidState, int phaseIdx) { assert(0 <= phaseIdx && phaseIdx < numPhases); - // Scalar temperature = fluidState.temperature(phaseIdx); - // Scalar pressure = fluidState.pressure(phaseIdx); - -// return liquidViscosity_(temperature, -// pressure, -// fluidState.massFraction(phaseIdx, SaltIdx)); return 0.001; // [Pa*s] } @@ -284,7 +295,7 @@ public: * \param phaseIdx The index of the fluid phase to consider * \param compIdx The index of the component to consider */ - using Base::diffusionCoefficient; + using BaseT::diffusionCoefficient; template static Scalar diffusionCoefficient(const FluidState &fluidState, int phaseIdx, @@ -294,7 +305,6 @@ public: DUNE_THROW(Dune::NotImplemented, "Diffusion coefficients"); } - /*! * \brief Given a phase's composition, temperature and pressure, * return the binary diffusion coefficient for components @@ -305,21 +315,16 @@ public: * \param compIIdx The index of the first component to consider * \param compJIdx The index of the second component to consider */ - using Base::binaryDiffusionCoefficient; + using BaseT::binaryDiffusionCoefficient; template static Scalar binaryDiffusionCoefficient(const FluidState &fluidState, int phaseIdx, int compIIdx, int compJIdx) - { assert(0 <= phaseIdx && phaseIdx < numPhases); assert(0 <= compIIdx && compIIdx < numComponents); assert(0 <= compJIdx && compJIdx < numComponents); -// Scalar diffusionCoefficient_; -// diffusionCoefficient_ = GET_RUNTIME_PARAM(TypeTag, Scalar, FluidSystem.diffusionCoefficient); - // 3.7378e-12 - // Scalar diffusionCoefficient_ = problem().diffusionCoefficient(element, fvGeometry, scvIdx); return 1.2e-9;//6.6e-6; // in [m^2/s] from Mufte modell } @@ -330,10 +335,9 @@ public: * \param fluidState An abitrary fluid state * \param phaseIdx for which phase to give back the heat capacity */ - using Base::enthalpy; + using BaseT::enthalpy; template - static Scalar enthalpy(const FluidState &fluidState, - int phaseIdx) + static Scalar enthalpy(const FluidState &fluidState, int phaseIdx) { assert(0 <= phaseIdx && phaseIdx < numPhases); @@ -346,10 +350,9 @@ public: * \param fluidState An abitrary fluid state * \param phaseIdx for which phase to give back the heat capacity */ - using Base::thermalConductivity; + using BaseT::thermalConductivity; template - static Scalar thermalConductivity(const FluidState &fluidState, - int phaseIdx) + static Scalar thermalConductivity(const FluidState &fluidState, int phaseIdx) { assert(0 <= phaseIdx && phaseIdx < numPhases); @@ -363,7 +366,7 @@ public: * \param fluidState An abitrary fluid state * \param phaseIdx The index of the fluid phase to consider */ - using Base::heatCapacity; + using BaseT::heatCapacity; template static Scalar heatCapacity(const FluidState &fluidState, int phaseIdx) @@ -376,56 +379,52 @@ public: private: /***********************************************************************/ - /* */ - /* water density with dissolved salt */ - /* rho_{b} = rho_w + contribution(salt) */ - /* */ + /* water density with dissolved salt + /* rho_{b} = rho_w + contribution(salt) /***********************************************************************/ - static Scalar liquidDensity1_(Scalar T, - Scalar pl, - Scalar Xsalt) + static Scalar liquidDensity1_(Scalar T, Scalar pl, Scalar Xsalt) { Valgrind::CheckDefined(T); Valgrind::CheckDefined(pl); Valgrind::CheckDefined(Xsalt); - - if(T < 273.15) { + if (T < 273.15) + { DUNE_THROW(NumericalProblem, "Liquid density for Brine and CO2 is only " "defined above 273.15K (is" << T << ")"); } - if(pl >= 2.5e8) { + + if (pl >= 2.5e8) + { DUNE_THROW(NumericalProblem, "Liquid density for Brine and CO2 is only " "defined below 250MPa (is" << pl << ")"); } - Scalar salinity=Xsalt; - Scalar TempC = T - 273.15; - Scalar pMPa = pl/1.0E6; - - Scalar rhow = H2O::liquidDensity(T, pl);//1000; - Scalar rho_brine= - rhow + - 1000*salinity*( - 0.668 + - 0.44*salinity + - 1.0E-6*( - 300*pMPa - - 2400*pMPa*salinity + - TempC*( - 80.0 - - 3*TempC - - 3300*salinity - - 13*pMPa + - 47*pMPa*salinity))); - + Scalar salinity=Xsalt; + Scalar TempC = T - 273.15; + Scalar pMPa = pl/1.0E6; + + Scalar rhow = H2O::liquidDensity(T, pl);//1000; + Scalar rho_brine= + rhow + + 1000*salinity*( + 0.668 + + 0.44*salinity + + 1.0E-6*( + 300*pMPa - + 2400*pMPa*salinity + + TempC*( + 80.0 - + 3*TempC - + 3300*salinity - + 13*pMPa + + 47*pMPa*salinity))); return rho_brine; } - /*! * \brief The dynamic viscosity \f$\mathrm{[Pa*s]}\f$ of pure brine. * @@ -436,35 +435,27 @@ private: * - cited by: Bachu & Adams (2002) * "Equations of State for basin geofluids" */ - static Scalar liquidViscosity_(Scalar temperature, Scalar pressure, Scalar Xsalt) - { - if(temperature <= 275.) // regularisation - { temperature = 275; } - Scalar T_C = temperature - 273.15; - Scalar salinity=Xsalt; - Scalar A = (0.42*pow((pow(salinity, 0.8)-0.17), 2) + 0.045)*pow(T_C, 0.8); - Scalar mu_brine = 0.1 + 0.333*salinity + (1.65+91.9*salinity*salinity*salinity)*exp(-A); - - return mu_brine/1000.0; /* unit: Pa s */ - } - + static Scalar liquidViscosity_(Scalar temperature, Scalar pressure, Scalar Xsalt) + { + // regularisation + if (temperature <= 275.) + { + temperature = 275; + } - }; + Scalar T_C = temperature - 273.15; + Scalar salinity=Xsalt; + Scalar A = (0.42*pow((pow(salinity, 0.8)-0.17), 2) + 0.045)*pow(T_C, 0.8); + Scalar mu_brine = 0.1 + 0.333*salinity + (1.65+91.9*salinity*salinity*salinity)*exp(-A); -} // end namepace + return mu_brine/1000.0; /* unit: Pa s */ + } +}; +} // end namepace FluidSystems #ifdef DUMUX_PROPERTIES_HH -// forward defintions of the property tags -//namespace Properties -//{ -//NEW_PROP_TAG(Scalar); -//// Set Co2 tables -//SET_TYPE_PROP(NumericModel, CO2Table, Dumux::CO2Tables); -//// Set salinity defaults -//SET_SCALAR_PROP(NumericModel, Salinity, 1e-3); -//}; /*! * \brief A pure single-phase fluid system. @@ -477,17 +468,11 @@ template class WaterSaltFluidSystem : public FluidSystems::WaterSalt { - typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; - typedef WaterSaltFluidSystem ThisType; - // typedef typename FluidSystems::WaterSalt WaterSalt1; - // typedef BaseFluidSystem Base; - -public: typedef Dumux::NullParameterCache ParameterCache; - - // typedef BaseFluidSystem Base; + using Scalar = typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)); + using ThisType = WaterSaltFluidSystem; +public: + using ParameterCache = Dumux::NullParameterCache; /* * \brief Given a phase's composition, temperature and pressure, @@ -499,28 +484,22 @@ public: typedef Dumux::NullParameterCache ParameterCache; * \param compIIdx The index of the first component to consider * \param compJIdx The index of the second component to consider */ +template + static Scalar binaryDiffusionCoefficient(const FluidState &fluidState, + const ParameterCache ¶mCache, + int phaseIdx, + int compIIdx, + int compJIdx) + { + Scalar diffusionCoefficient_; + diffusionCoefficient_ = getParam("Problem.diffusionCoefficient"); - - // using Base::binaryDiffusionCoefficient; - template - static Scalar binaryDiffusionCoefficient(const FluidState &fluidState, - const ParameterCache ¶mCache, - int phaseIdx, - int compIIdx, - int compJIdx) - - { -// assert(0 <= phaseIdx && phaseIdx < numPhases); -// assert(0 <= compIIdx && compIIdx < numComponents); -// assert(0 <= compJIdx && compJIdx < numComponents); - Scalar diffusionCoefficient_; - diffusionCoefficient_ = GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.diffusionCoefficient); - - return diffusionCoefficient_;//1.2e-9;//6.6e-6; // in [m^2/s] from Mufte modell - } + return diffusionCoefficient_;//1.2e-9;//6.6e-6; // in [m^2/s] from Mufte modell + } }; -#endif -} // end namepace -#endif +#endif // DUMUX_PROPERTIES_HH +} // end namespace Dumux + +#endif // DUMUX_WATER_SALT_FLUID_SYSTEM_HH diff --git a/lecture/mm/henryproblem/henry2p/henry2p.cc b/lecture/mm/henryproblem/henry2p/henry2p.cc index cdf7c5369689f5fb44c3641fe302d9ce95638984..fa408ed94c03fc4fa82fd1f0c1cfe79155ca79b1 100644 --- a/lecture/mm/henryproblem/henry2p/henry2p.cc +++ b/lecture/mm/henryproblem/henry2p/henry2p.cc @@ -21,9 +21,31 @@ * * \brief test for the two-phase box model */ -#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include #include "henry2pproblem.hh" -#include /*! * \brief Provides an interface for customizing error messages associated with @@ -59,8 +81,149 @@ void usage(const char *progName, const std::string &errorMsg) //////////////////////// // the main function //////////////////////// -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(Henry2pProblemTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) // Fehlt ebenso + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend; + auto linearSolver = std::make_shared(leafGridView, fvGridGeometry->dofMapper()); + + // the non-linear solver + using NewtonSolver = Dumux::NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } +} + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(Henry2pProblem) TypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/henryproblem/henry2p/henry2p.input b/lecture/mm/henryproblem/henry2p/henry2p.input index 69e23502a27bb6241bf75336584441895f2e9154..6ae6974c8d0f9076798d0b73a71a8312c28155e0 100644 --- a/lecture/mm/henryproblem/henry2p/henry2p.input +++ b/lecture/mm/henryproblem/henry2p/henry2p.input @@ -1,9 +1,7 @@ - - -[TimeManager] +[TimeLoop] DtInitial = 2.5 # [s] TEnd =1e12 # [s] -#MaxTimeStepSize = 1e9 +MaxTimeStepSize = 1e9 EpisodeLength = 1.e9 # [s] [Grid] @@ -11,4 +9,4 @@ File = ./grids/henry.dgf [Problem] freshWaterFluxRate = 6.6E-2 # [kg/sm^2] fresh water flux rate at the left boundary (6.6E-2) - +Name = Henry2p diff --git a/lecture/mm/henryproblem/henry2p/henry2pproblem.hh b/lecture/mm/henryproblem/henry2p/henry2pproblem.hh index b3ed961f27d7c68c6731d12fe5413bd2d2249911..da7a8439e6ce0c3eeffa0895065b56261c9e6b4e 100644 --- a/lecture/mm/henryproblem/henry2p/henry2pproblem.hh +++ b/lecture/mm/henryproblem/henry2p/henry2pproblem.hh @@ -23,21 +23,26 @@ * water saturated medium. */ +/* + * Note: In the new Dumux this problem uses much more time steps, it seems like the solver does not converge very good and therefor it makes the time steps smaller. + * The end results resp. the stationary state do look the same in both versions. In the new version one can see how the systems approaches the steady state so maybe in the new version a few errors are fixed. + */ + #ifndef DUMUX_HENRY2PPROBLEM_HH #define DUMUX_HENRY2PPROBLEM_HH #include #include "simplesaltwater.hh" -#include -#include +#include +#include +#include #include #include "henry2pspatialparams.hh" -namespace Dumux -{ +namespace Dumux { template class Henry2pProblem; @@ -45,95 +50,55 @@ class Henry2pProblem; ////////// // Specify the properties for the Henry2p problem ////////// -namespace Properties -{ -NEW_TYPE_TAG(Henry2pProblem, INHERITS_FROM(BoxTwoP, Henry2pSpatialParams)); +namespace Properties { + +NEW_TYPE_TAG(Henry2pProblemTypeTag, INHERITS_FROM(TwoP, BoxModel)); // Set the grid type -SET_TYPE_PROP(Henry2pProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(Henry2pProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(Henry2pProblem, Problem, Henry2pProblem); +SET_TYPE_PROP(Henry2pProblemTypeTag, Problem, Henry2pProblem); -// TODO: remove this macro switch -#if 1 -// Set the wetting phase -SET_PROP(Henry2pProblem, WettingPhase) -{ -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef Dumux::FluidSystems::LiquidPhase > type; -}; +// Set the spatial parameters +SET_TYPE_PROP(Henry2pProblemTypeTag, SpatialParams, Dumux::Henry2pSpatialParams); -// Set the non-wetting phase -SET_PROP(Henry2pProblem, NonwettingPhase) +// Set the fluid system +SET_PROP(Henry2pProblemTypeTag, FluidSystem) { -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef FluidSystems::LiquidPhase > type; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using WettingPhase = FluidSystems::OnePLiquid >; + using NonwettingPhase = FluidSystems::OnePLiquid >; + using type = FluidSystems::TwoPImmiscible; }; -#else -// OR: set the fluid system -SET_TYPE_PROP(Henry2pProblem, FluidSystem, H2ON2FluidSystem); -#endif - -// Enable partial reassembly of the jacobian matrix? -SET_BOOL_PROP(Henry2pProblem, ImplicitEnablePartialReassemble, true); - -// Enable reuse of jacobian matrices? -SET_BOOL_PROP(Henry2pProblem, ImplicitEnableJacobianRecycling, true); - -// Write the solutions of individual newton iterations? -SET_BOOL_PROP(Henry2pProblem, NewtonWriteConvergence, false); - -// Use forward differences instead of central differences -SET_INT_PROP(Henry2pProblem, ImplicitNumericDifferenceMethod, +1); -SET_SCALAR_PROP(Henry2pProblem, ImplicitMassUpwindWeight, 0.5); - -// Enable gravity -SET_BOOL_PROP(Henry2pProblem, ProblemEnableGravity, true); -} +} // end namespace Properties template -class Henry2pProblem : public ImplicitPorousMediaProblem +class Henry2pProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; - typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; - - enum { - - // primary variable indices - pressureIdx = Indices::pressureIdx, - snIdx = Indices::snIdx, - - // equation indices - contiNEqIdx = Indices::contiNEqIdx, - contiWEqIdx = Indices::contiWEqIdx, - // phase indices - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - - - // Grid and world dimension - dim = GridView::dimension, - dimWorld = GridView::dimensionworld - }; - - - 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 GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef Dune::FieldVector GlobalPosition; + using ParentType = PorousMediumFlowProblem; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + // primary variable indices + static constexpr int pressureIdx = Indices::pressureIdx; + static constexpr int saturationIdx = Indices::saturationIdx; + // equation indices + static constexpr int conti0EqIdx = Indices::conti0EqIdx; + // phase indices + static constexpr int wPhaseIdx = Indices::Phase0Idx; + static constexpr int nPhaseIdx = Indices::Phase1Idx; + // Grid and world dimension + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; public: /*! @@ -142,48 +107,19 @@ public: * \param timeManager The time manager * \param gridView The grid view */ - Henry2pProblem(TimeManager &timeManager, - const GridView &gridView) - : ParentType(timeManager, gridView) + Henry2pProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { eps_ = 3e-6; temperature_ = 273.15 + 20; // -> 20°C - freshWaterFluxRate_= GET_RUNTIME_PARAM(TypeTag, Scalar, Problem.freshWaterFluxRate); - - episodeLength_ = GET_RUNTIME_PARAM(TypeTag, Scalar, TimeManager.EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); - + freshWaterFluxRate_= getParam("Problem.freshWaterFluxRate"); } - - bool shouldWriteRestartFile() const - { - return false; - } - - -// bool shouldWriteOutput() const -// { -// return -// this->timeManager().timeStepIndex() == 0 || -// this->timeManager().timeStepIndex() %10 ==0 || -// // this->timeManager().time() %31536000 ==0 -// this->timeManager().willBeFinished(); -// } - /*! * \name Problem parameters */ // \{ - /*! - * \brief The problem name. - * - * This is used as a prefix for files generated by the simulation. - */ - std::string name() const - { return "Henry2p"; } - /*! * \brief Called directly after the time integration. */ @@ -194,37 +130,26 @@ public: this->model().globalStorage(storage); // Write mass balance information for rank 0 - if (this->gridView().comm().rank() == 0) { + if (this->gridView().comm().rank() == 0) + { std::cout<<"Storage: " << storage << std::endl; } } - bool shouldWriteOutput() const - { - return - this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } - - void episodeEnd() - { - this->timeManager().startNextEpisode(episodeLength_); - } - /*! * \brief Returns the temperature within the domain. * * This problem assumes a uniform temperature of 10 degrees Celsius. */ Scalar temperature() const - { return temperature_; }; + { + return temperature_; + } - void sourceAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const + NumEqVector sourceAtPos( const GlobalPosition &globalPos) const { - values = 0; + return NumEqVector(0.0); } // \} @@ -241,17 +166,21 @@ public: * \param values The boundary types for the conservation equations * \param globalPos The position of the center of the finite volume */ - void boundaryTypesAtPos(BoundaryTypes &values, - const GlobalPosition &globalPos) const + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { + BoundaryTypes values; + if (globalPos[0]>2-eps_) { values.setAllDirichlet(); } + else { values.setAllNeumann(); } + + return values; } /*! @@ -263,30 +192,29 @@ public: * * For this method, the \a values parameter stores primary variables. */ - void dirichletAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { + PrimaryVariables values; if (globalPos[0]>2-eps_)// if(onRightBoundary_(globalPos)) - { - - if(globalPos[1]<0.8+eps_) - { - Scalar densityB = 1025; - values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1]-0.2)*densityB*9.81+1000*9.81*0.2; - values[snIdx] = 1.0; - } - else { -// Scalar densityW = 1000; -// // hydrostatic pressure -// values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1])*densityW*9.81; -// values[snIdx] = 0.0; - Scalar densityB = 1025; - values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1]-0.2)*densityB*9.81+1000*9.81*0.2; - values[snIdx] = 0.0; - } - } - + { + + if(globalPos[1]<0.8+eps_) + { + Scalar densityB = 1025; + values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1]-0.2)*densityB*9.81+1000*9.81*0.2; + values[saturationIdx] = 1.0; + } + + else + { + Scalar densityB = 1025; + values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1]-0.2)*densityB*9.81+1000*9.81*0.2; + values[saturationIdx] = 0.0; + } + } + + return values; } /*! @@ -299,13 +227,17 @@ public: * 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 &globalPos) const + PrimaryVariables neumannAtPos(const GlobalPosition &globalPos) const { + PrimaryVariables values; + values = 0.0; - if (onLeftBoundary_(globalPos)) { - values[contiWEqIdx] = -freshWaterFluxRate_;//-6.6E-2;// kg / (m * s) + if (onLeftBoundary_(globalPos)) + { + values[conti0EqIdx] = -freshWaterFluxRate_;//-6.6E-2;// kg / (m * s) } + + return values; } // \} @@ -314,7 +246,6 @@ public: */ // \{ - /*! * \brief Evaluate the initial value for a control volume. * @@ -324,22 +255,25 @@ public: * For this method, the \a values parameter stores primary * variables. */ - void initialAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initialAtPos( const GlobalPosition &globalPos) const { - if(globalPos[1]<0.8+eps_) - { - Scalar densityB = 1025; - values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1]-0.2)*densityB*9.81+1000*9.81*0.2; - values[snIdx] = 0.0; - } - else { - Scalar densityW = 1000; - values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1])*densityW*9.81; - values[snIdx] = 0.0; - } - + PrimaryVariables values; + if(globalPos[1]<0.8+eps_) + { + Scalar densityB = 1025; + values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1]-0.2)*densityB*9.81+1000*9.81*0.2; + values[saturationIdx] = 0.0; + } + + else + { + Scalar densityW = 1000; + values[pressureIdx] = 1.0133e5+(depthBOR_-globalPos[1])*densityW*9.81; + values[saturationIdx] = 0.0; + } + + return values; } // \} @@ -347,30 +281,30 @@ private: bool onLeftBoundary_(const GlobalPosition &globalPos) const { - return globalPos[0] < this->bBoxMin()[0] + eps_; + return globalPos[0] < this->fvGridGeometry().bBoxMin()[0] + eps_; } bool onRightBoundary_(const GlobalPosition &globalPos) const { - return globalPos[0] > this->bBoxMax()[0] - eps_; + return globalPos[0] > this->fvGridGeometry().bBoxMax()[0] - eps_; } bool onLowerBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] < this->bBoxMin()[1] + eps_; + return globalPos[1] < this->fvGridGeometry().bBoxMin()[1] + eps_; } bool onUpperBoundary_(const GlobalPosition &globalPos) const { - return globalPos[1] > this->bBoxMax()[1] - eps_; + return globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_; } static constexpr Scalar depthBOR_ = 1.0; Scalar temperature_; Scalar eps_; - Scalar episodeLength_; Scalar freshWaterFluxRate_; }; -} //end namespace -#endif +} //end namespace Dumux + +#endif // DUMUX_HENRY2PPROBLEM_HH diff --git a/lecture/mm/henryproblem/henry2p/henry2pspatialparams.hh b/lecture/mm/henryproblem/henry2p/henry2pspatialparams.hh index 5c2238e6604f68ca3e41cdc6cf0d43b9e33ca79f..b8069c2cfd15130fda0759cfc41f9627a6e3988b 100644 --- a/lecture/mm/henryproblem/henry2p/henry2pspatialparams.hh +++ b/lecture/mm/henryproblem/henry2p/henry2pspatialparams.hh @@ -25,41 +25,15 @@ #ifndef DUMUX_LENS_SPATIAL_PARAMS_HH #define DUMUX_LENS_SPATIAL_PARAMS_HH -#include +#include #include #include #include -#include +#include -namespace Dumux -{ - -//forward declaration -template -class Henry2pSpatialParams; +namespace Dumux { -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(Henry2pSpatialParams); - -// Set the spatial parameters -SET_TYPE_PROP(Henry2pSpatialParams, SpatialParams, Dumux::Henry2pSpatialParams); - -// Set the material Law -SET_PROP(Henry2pSpatialParams, MaterialLaw) -{ -private: - // define the material law which is parameterized by effective - // saturations - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef LinearMaterial EffectiveLaw; -public: - // define the material law parameterized by absolute saturations - typedef EffToAbsLaw type; -}; -} /*! * \ingroup TwoPBoxModel * \ingroup BoxTestProblems @@ -67,31 +41,30 @@ public: * twophase box model */ template -class Henry2pSpatialParams : public ImplicitSpatialParams +class Henry2pSpatialParams : public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename Grid::ctype CoordScalar; - - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld - }; - - typedef Dune::FieldVector GlobalPosition; - - typedef typename GridView::template Codim<0>::Entity Element; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + using ThisType = Henry2pSpatialParams; + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using ParentType = FVSpatialParams; + using Grid = typename GET_PROP_TYPE(TypeTag, Grid); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using CoordScalar = typename Grid::ctype; + using Element = typename GridView::template Codim<0>::Entity; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry)::LocalView; + static constexpr int dim=GridView::dimension; + static constexpr int dimWorld=GridView::dimensionworld; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; public: + using PermeabilityType = Scalar; //get the material law from the property system - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; + using EffectiveLaw = LinearMaterial; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; - Henry2pSpatialParams(const GridView& gridView) - : ParentType(gridView) + Henry2pSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { try { @@ -119,32 +92,24 @@ public: } /*! - * \brief Intrinsic permability + * \brief Define the intrinsic permeability \f$\mathrm{[m^2]}\f$. * - * \param element The current element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume. - * \return Intrinsic permeability + * \param globalPos The global position */ - Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const { return K_; } /*! - * \brief Porosity + * \brief Define the porosity \f$\mathrm{[-]}\f$. * - * \param element The current element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume. - * \return Porosity + * \param globalPos The global position */ - Scalar porosity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { return porosity_; } + double porosityAtPos(const GlobalPosition& globalPos) const + { + return porosity_; + } /*! * \brief Function for defining the parameters needed by constitutive relationships (kr-sw, pc-sw, etc.). @@ -154,22 +119,29 @@ public: * \param scvIdx The index of the sub-control volume. * \return the material parameters object */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { return materialParams_; } + /*! + * \brief Function for defining which phase is to be considered as the wetting phase. + * + * \return the wetting phase index + * \param globalPos The global position + */ + template + int wettingPhaseAtPos(const GlobalPosition& globalPos) const + { + return FluidSystem::phase0Idx; + } private: - Scalar K_; Scalar porosity_; MaterialLawParams materialParams_; }; -} // end namespace -#endif - +} // end namespace Dumux +#endif // DUMUX_LENS_SPATIAL_PARAMS_HH diff --git a/lecture/mm/henryproblem/henry2p/simplesaltwater.hh b/lecture/mm/henryproblem/henry2p/simplesaltwater.hh index 0b7c2b6b952e74651a765083eca2fc30552eae37..7e13df0843a72d025b73cc4fbb62f8e298b5105f 100644 --- a/lecture/mm/henryproblem/henry2p/simplesaltwater.hh +++ b/lecture/mm/henryproblem/henry2p/simplesaltwater.hh @@ -26,13 +26,10 @@ #define DUMUX_SIMPLE_SALTWATER_HH #include - -#include - +#include #include -namespace Dumux -{ +namespace Dumux { /*! * \ingroup Components * @@ -42,9 +39,11 @@ namespace Dumux * \tparam Scalar The type used for scalar values */ template -class SimpleSaltwater : public Component > +class SimpleSaltwater : public Dumux::Components::Base > +, public Components::Liquid > +, public Components::Gas > { - typedef Dumux::IdealGas IdealGas; + using IdealGas = Dumux::IdealGas; static const Scalar R; // specific gas constant of water @@ -53,37 +52,49 @@ public: * \brief A human readable name for the water. */ static std::string name() - { return "H2O"; } + { + return "H2O"; + } /*! * \brief The molar mass in \f$\mathrm{[kg/mol]}\f$ of water. */ static Scalar molarMass() - { return 18e-3; } + { + return 18e-3; + } /*! * \brief Returns the critical temperature \f$\mathrm{[K]}\f$ of water. */ static Scalar criticalTemperature() - { return 647.096; /* [K] */ } + { + return 647.096; /* [K] */ + } /*! * \brief Returns the critical pressure \f$\mathrm{[Pa]}\f$ of water. */ static Scalar criticalPressure() - { return 22.064e6; /* [N/m^2] */ } + { + return 22.064e6; /* [N/m^2] */ + } /*! * \brief Returns the temperature \f$\mathrm{[K]}\f$ at water's triple point. */ static Scalar tripleTemperature() - { return 273.16; /* [K] */ } + { + return 273.16; /* [K] */ + } /*! * \brief Returns the pressure \f$\mathrm{[Pa]}\f$ at water's triple point. */ static Scalar triplePressure() - { return 611.657; /* [N/m^2] */ } + { + return 611.657; /* [N/m^2] */ + } /*! * \brief The vapor pressure in \f$\mathrm{[Pa]}\f$ of pure water @@ -101,10 +112,12 @@ public: { if (T > criticalTemperature()) return criticalPressure(); + if (T < tripleTemperature()) return 0; // water is solid: We don't take sublimation into account - static const Scalar n[10] = { + static const Scalar n[10] = + { 0.11670521452767e4, -0.72421316703206e6, -0.17073846940092e2, 0.12020824702470e5, -0.32325550322333e7, 0.14915108613530e2, -0.48232657361591e4, 0.40511340542057e6, -0.23855557567849, @@ -112,7 +125,6 @@ public: }; Scalar sigma = T + n[8]/(T - n[9]); - Scalar A = (sigma + n[0])*sigma + n[1]; Scalar B = (n[2]*sigma + n[3])*sigma + n[4]; Scalar C = (n[5]*sigma + n[6])*sigma + n[7]; @@ -130,9 +142,10 @@ public: * \param temperature temperature of component in \f$\mathrm{[K]}\f$ * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ */ - static const Scalar gasEnthalpy(Scalar temperature, - Scalar pressure) - { return 1976*(temperature - 293.15) + 2.45e6; } + static const Scalar gasEnthalpy(Scalar temperature, Scalar pressure) + { + return 1976*(temperature - 293.15) + 2.45e6; + } /*! * \brief Specific enthalpy of liquid water \f$\mathrm{[J/kg]}\f$. @@ -140,8 +153,7 @@ public: * \param temperature temperature of component in \f$\mathrm{[K]}\f$ * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ */ - static const Scalar liquidEnthalpy(Scalar temperature, - Scalar pressure) + static const Scalar liquidEnthalpy(Scalar temperature, Scalar pressure) { return 4180*(temperature - 293.15); } @@ -159,8 +171,7 @@ public: * \param temperature temperature of component in \f$\mathrm{[K]}\f$ * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ */ - static const Scalar gasInternalEnergy(Scalar temperature, - Scalar pressure) + static const Scalar gasInternalEnergy(Scalar temperature, Scalar pressure) { return gasEnthalpy(temperature, pressure) - @@ -174,23 +185,28 @@ public: * \param temperature temperature of component in \f$\mathrm{[K]}\f$ * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ */ - static const Scalar liquidInternalEnergy(Scalar temperature, - Scalar pressure) - { return + static const Scalar liquidInternalEnergy(Scalar temperature, Scalar pressure) + { + return liquidEnthalpy(temperature, pressure) - - pressure/liquidDensity(temperature, pressure); } + pressure/liquidDensity(temperature, pressure); + } /*! * \brief Returns true iff the gas phase is assumed to be compressible */ static bool gasIsCompressible() - { return true; } + { + return true; + } /*! * \brief Returns true iff the liquid phase is assumed to be compressible */ static bool liquidIsCompressible() - { return false; } + { + return false; + } /*! * \brief The density \f$\mathrm{[kg/m^3]}\f$ of steam at a given pressure and temperature. @@ -208,7 +224,9 @@ public: * \brief Returns true iff the gas phase is assumed to be ideal */ static bool gasIsIdeal() - { return true; } + { + return true; + } /*! * \brief The pressure of steam in \f$\mathrm{[Pa]}\f$ at a given density and temperature. @@ -233,6 +251,11 @@ public: return 1025.; } + static Scalar liquidMolarDensity(Scalar temperature, Scalar pressure) + { + return liquidDensity(temperature, pressure)/molarMass(); + } + /*! * \brief The pressure of water in \f$\mathrm{[Pa]}\f$ at a given density and temperature. * @@ -252,7 +275,7 @@ public: * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ * \param regularize defines, if the functions is regularized or not, set to true by default */ - static Scalar gasViscosity(Scalar temperature, Scalar pressure, bool regularize=true) + static Scalar gasViscosity(Scalar temperature, Scalar pressure, bool regularize = true) { return 1e-05; } @@ -285,6 +308,6 @@ public: template const Scalar SimpleSaltwater::R = Dumux::Constants::R / 18e-3; -} // end namepace +} // end namespace Dumux -#endif +#endif // DUMUX_SIMPLE_SALTWATER_HH diff --git a/lecture/mm/mcwhorter/mcwhorteranalyticsolution.hh b/lecture/mm/mcwhorter/mcwhorteranalyticsolution.hh index e4db69f4e693c1584b8e81025afb2d0778a6fbc0..15b8dc2a559a59dafb139822765b7ecb894baaa5 100644 --- a/lecture/mm/mcwhorter/mcwhorteranalyticsolution.hh +++ b/lecture/mm/mcwhorter/mcwhorteranalyticsolution.hh @@ -28,8 +28,7 @@ #include -namespace Dumux -{ +namespace Dumux { /** * @brief Analytic solution of * the McWhorter problem @@ -42,45 +41,28 @@ namespace Dumux template class McWhorterAnalytic { - typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; -// typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - - typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; - typedef typename SpatialParams::MaterialLaw MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; - - typedef typename GET_PROP(TypeTag, SolutionTypes)::PrimaryVariables PrimaryVariables; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - - enum - { - dimworld = GridView::dimensionworld //dim = GridView::dimension, - }; - enum - { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, -// pressureIdx = Indices::pressureIdx, - saturationIdx = Indices::saturationIdx//, -// pressEqIdx = Indices::pressEqIdx, -// satEqIdx = Indices::satEqIdx - }; - - typedef Dune::BlockVector > BlockVector; - typedef typename GridView::Traits::template Codim<0>::Entity Element; - typedef typename GridView::template Codim<0>::Iterator ElementIterator; - typedef Dune::FieldVector GlobalPosition; + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using SpatialParams = typename GET_PROP_TYPE(TypeTag, SpatialParams); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); + using PrimaryVariables = typename GET_PROP(TypeTag, SolutionTypes)::PrimaryVariables; + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); + static constexpr int dimworld = GridView::dimensionworld; + static constexpr int wPhaseIdx = Indices::wPhaseIdx; + static constexpr int nPhaseIdx = Indices::nPhaseIdx; + static constexpr int saturationIdx = Indices::saturationIdx; + using GlobalPosition = Dune::FieldVector; + using BlockVector = Dune::BlockVector >; + using Element = typename GridView::Traits::template Codim<0>::Entity; + using ElementIterator = typename GridView::template Codim<0>::Iterator; public: + using MaterialLaw = typename SpatialParams::MaterialLaw; + using MaterialLawParams = typename MaterialLaw::Params; // functions needed for analytical solution - void initializeAnalytic() { analyticSolution_.resize(size_); @@ -89,8 +71,6 @@ public: error_=0; elementVolume_.resize(size_); elementVolume_=0; - - return; } void calcSatError() @@ -106,38 +86,17 @@ public: const Element& element = *eIt; int index = problem_.variables().index(*eIt); elementVolume_[index]= element.geometry().volume(); - // std::cout<<"elementVolume_ = "< satVec_; Dune::FieldVector fractionalW_; Dune::FieldVector dpcdsw_; @@ -430,5 +347,7 @@ private: Dune::FieldVector fp_; }; -} -#endif + +} // end namespace Dumux + +#endif // DUMUX_MCWHORTER_ANALYTIC_HH diff --git a/lecture/mm/mcwhorter/mcwhorterexercise.cc b/lecture/mm/mcwhorter/mcwhorterexercise.cc index 56188f2f9d6e6af5b192c5f7708b5437680f4f63..750f4dc42521235806e9485e59d2a2eba4aa2442 100644 --- a/lecture/mm/mcwhorter/mcwhorterexercise.cc +++ b/lecture/mm/mcwhorter/mcwhorterexercise.cc @@ -65,6 +65,6 @@ void usage(const char *progName, const std::string &errorMsg) int main(int argc, char** argv) { - typedef TTAG(McWhorterProblem) TypeTag; + typedef TTAG(McWhorterProblemTypeTag) TypeTag; return Dumux::start(argc, argv, usage); } diff --git a/lecture/mm/mcwhorter/mcwhorterexercise.input b/lecture/mm/mcwhorter/mcwhorterexercise.input index e87b7a590947aca89fef434153cb16d8589b9845..3793d0c6787b4d83a7e4c35702fb851ba04b2f86 100644 --- a/lecture/mm/mcwhorter/mcwhorterexercise.input +++ b/lecture/mm/mcwhorter/mcwhorterexercise.input @@ -2,6 +2,9 @@ TEnd = 5e6 # end time of the simulation [s] DtInitial = 1e3 # initial time step for the simulation [s] +[Problem] +EnableGravity = false + [SpatialParams] Permeability = 1.01936799e-13 # intrinsic permeability of the porous medium [m^2] Porosity = 0.2 # porosity of the porous medium [-] @@ -24,3 +27,6 @@ Cells = 20 1 # grid resolution in (x, y) directio [Output] Interval = 10 # output only every n-th time step ParaviewOutput = true # create output for either Paraview or ViPLab + +[Impet] +CFLFactor = 0.8 diff --git a/lecture/mm/mcwhorter/mcwhorterproblem.hh b/lecture/mm/mcwhorter/mcwhorterproblem.hh index 32d2b97877f600f45bcd81da829266eb137b4be0..2f8adeee4bc20d912ec8e3b3741969ad60a7a037 100644 --- a/lecture/mm/mcwhorter/mcwhorterproblem.hh +++ b/lecture/mm/mcwhorter/mcwhorterproblem.hh @@ -19,7 +19,10 @@ #ifndef DUMUX_MCWHORTERPROBLEM_HH #define DUMUX_MCWHORTERPROBLEM_HH -#include +#include + +#include +#include #include #include @@ -33,97 +36,67 @@ #include "mcwhorteranalyticsolution.hh" #include -namespace Dumux -{ +namespace Dumux { + template class McWhorterProblem; ////////// // Specify the properties ////////// -namespace Properties -{ -NEW_TYPE_TAG(McWhorterProblem, INHERITS_FROM(FVPressureTwoP, FVTransportTwoP, IMPESTwoP, BuckleyLeverettSpatialParams)); +namespace Properties { + +NEW_TYPE_TAG(McWhorterProblemTypeTag, INHERITS_FROM(FVPressureTwoP, FVTransportTwoP, IMPESTwoP, BuckleyLeverettSpatialParamsTypeTag)); // Set the grid type -SET_TYPE_PROP(McWhorterProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(McWhorterProblemTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(McWhorterProblem, Problem, McWhorterProblem); +SET_TYPE_PROP(McWhorterProblemTypeTag, Problem, McWhorterProblem); -SET_INT_PROP(McWhorterProblem, Formulation, SequentialTwoPCommonIndices::pnsw); +SET_INT_PROP(McWhorterProblemTypeTag, Formulation, SequentialTwoPCommonIndices::pnsw); // Set the wetting phase -SET_PROP(McWhorterProblem, WettingPhase) -{ -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef FluidSystems::LiquidPhase > type; -}; - -// Set the non-wetting phase -SET_PROP(McWhorterProblem, NonwettingPhase) +SET_PROP(McWhorterProblemTypeTag, FluidSystem) { -private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; -public: - typedef FluidSystems::LiquidPhase > type; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using WettingPhase = FluidSystems::OnePLiquid >; + using NonwettingPhase = FluidSystems::OnePLiquid >; + using type = FluidSystems::TwoPImmiscible; }; -// Disable gravity -SET_BOOL_PROP(McWhorterProblem, ProblemEnableGravity, false); +SET_TYPE_PROP(McWhorterProblemTypeTag, EvalCflFluxFunction, Dumux::EvalCflFluxCoats); -SET_TYPE_PROP(McWhorterProblem, EvalCflFluxFunction, Dumux::EvalCflFluxCoats); -SET_SCALAR_PROP(McWhorterProblem, ImpetCFLFactor, 0.8); -} +} // end namespace Properties /*! * \ingroup DecoupledProblems * \brief McWhorter diffusion problem */ -template +template class McWhorterProblem: public IMPESProblem2P { - typedef IMPESProblem2P ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; - - typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; - typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; - - typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; - - typedef typename GET_PROP_TYPE(TypeTag, CellData) CellData; - - typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; - typedef typename GET_PROP(TypeTag, SolutionTypes)::PrimaryVariables PrimaryVariables; - - enum - { - dim = GridView::dimension, - dimWorld = GridView::dimensionworld - }; - enum - { - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx, - pnIdx = Indices::pnIdx, - swIdx = Indices::swIdx, - pressEqIdx = Indices::pressureEqIdx, - satEqIdx = Indices::satEqIdx - }; - - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - - typedef typename GridView::Traits::template Codim<0>::Entity Element; - typedef typename GridView::Intersection Intersection; - typedef Dune::FieldVector GlobalPosition; - typedef typename GET_PROP(TypeTag, ParameterTree) Params; + using ParentType = IMPESProblem2P; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using TimeManager = typename GET_PROP_TYPE(TypeTag, TimeManager); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); + using CellData = typename GET_PROP_TYPE(TypeTag, CellData); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using PrimaryVariables = typename GET_PROP(TypeTag, SolutionTypes)::PrimaryVariables; + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + static constexpr int wPhaseIdx = Indices::wPhaseIdx; + static constexpr int nPhaseIdx = Indices::nPhaseIdx; + static constexpr int pnIdx = Indices::pnIdx; + static constexpr int swIdx = Indices::swIdx; + static constexpr int pressEqIdx = Indices::pressureEqIdx; + static constexpr int satEqIdx = Indices::satEqIdx; + using GlobalPosition = Dune::FieldVector; + using Element = typename GridView::Traits::template Codim<0>::Entity; + using Intersection = typename GridView::Intersection; public: /*! @@ -131,22 +104,25 @@ public: * \param timeManager DOC ME! * \param gridView DOC ME! */ - McWhorterProblem(TimeManager& timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-8), pLeftBc_ ( 1.0e5 ), - analyticSolution_(*this), viplabOutput_(*this) + McWhorterProblem(TimeManager& timeManager, typename GridView::Grid &grid) + : ParentType(timeManager, grid) + , eps_(1e-8) + , pLeftBc_ ( 1.0e5 ) + , analyticSolution_(*this) + , viplabOutput_(*this) { - this->setOutputInterval(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, Interval)); + this->setOutputInterval( getParam("Output.Interval") ); - WettingPhase::Component::setViscosity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, ViscosityW)); - NonwettingPhase::Component::setViscosity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, ViscosityNW)); + PseudoOil::setViscosity( getParam("Fluid.ViscosityW") ); + PseudoH2O::setViscosity( getParam("Fluid.ViscosityNW") ); - WettingPhase::Component::setDensity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, DensityW)); - NonwettingPhase::Component::setDensity(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Fluid, DensityNW)); + PseudoOil::setDensity( getParam("Fluid.DensityW") ); + PseudoH2O::setDensity( getParam("Fluid.DensityNW") ); - swr_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, ResidualSaturationWetting); - snr_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, ResidualSaturationNonWetting); + swr_ = getParam("SpatialParams.ResidualSaturationWetting"); + snr_ = getParam("SpatialParams.ResidualSaturationNonWetting"); - paraviewOutput_ = Params::tree().template get("Output.paraviewOutput", true); + paraviewOutput_ = getParam("Output.paraviewOutput", true); } void init() @@ -161,10 +137,14 @@ public: * This is used as a prefix for files generated by the simulation. */ const std::string name() const - { return "McWhorter"; } + { + return "McWhorter"; + } bool shouldWriteRestartFile() const - { return false; } + { + return false; + } void postTimeStep() { @@ -198,7 +178,8 @@ public: * \param values DOC ME! * \param globalPos DOC ME! */ - void sourceAtPos(PrimaryVariables &values,const GlobalPosition& globalPos) const + void sourceAtPos(PrimaryVariables &values, + const GlobalPosition& globalPos) const { values = 0; } @@ -207,9 +188,11 @@ public: * \param bcTypes DOC ME! * \param globalPos DOC ME! */ - void boundaryTypesAtPos(BoundaryTypes &bcTypes, const GlobalPosition& globalPos) const + void boundaryTypesAtPos(BoundaryTypes &bcTypes, + const GlobalPosition& globalPos) const { - if (globalPos[0] < eps_) //west + //west + if (globalPos[0] < eps_) bcTypes.setAllDirichlet(); // the other boundary (east) @@ -221,7 +204,8 @@ public: * \param values DOC ME! * \param globalPos DOC ME! */ - void dirichletAtPos(PrimaryVariables &values, const GlobalPosition& globalPos) const + void dirichletAtPos(PrimaryVariables &values, + const GlobalPosition& globalPos) const { values[pnIdx] = pLeftBc_; values[swIdx] = 1.0 - snr_; @@ -231,7 +215,8 @@ public: * \param values DOC ME! * \param globalPos DOC ME! */ - void neumannAtPos(PrimaryVariables &values, const GlobalPosition& globalPos) const + void neumannAtPos(PrimaryVariables &values, + const GlobalPosition& globalPos) const { values = 0; //homogeneous Neumann } @@ -241,7 +226,7 @@ public: * \param globalPos DOC ME! */ void initialAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const + const GlobalPosition &globalPos) const { values[pnIdx] = pLeftBc_; values[swIdx] = swr_; @@ -252,6 +237,7 @@ public: { if (paraviewOutput_) ParentType::writeOutput(false); + else { std::size_t numElements = this->gridView().size(0); @@ -278,6 +264,9 @@ private: McWhorterAnalytic analyticSolution_; ViplabOutput viplabOutput_; bool paraviewOutput_; - }; -} -#endif + +}; + +} // end namespace Dumux + +#endif // DUMUX_MCWHORTERPROBLEM_HH diff --git a/lecture/mm/naplinfiltration/3p/CMakeLists.txt b/lecture/mm/naplinfiltration/3p/CMakeLists.txt index 8b9fc27e0bb3636f6072dc3c5462160e6bc4bc93..f3fcc003b5d90b25e9a90a70ddc87a5992229e16 100644 --- a/lecture/mm/naplinfiltration/3p/CMakeLists.txt +++ b/lecture/mm/naplinfiltration/3p/CMakeLists.txt @@ -4,9 +4,9 @@ add_input_file_links() add_dumux_test(naplinfiltration3p naplinfiltration3p naplinfiltration3p.cc python ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py --script fuzzy - --files ${CMAKE_SOURCE_DIR}/lecture/references/infiltration3p-reference.vtu - ${CMAKE_CURRENT_BINARY_DIR}/infiltration3p-00003.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/naplinfiltration3p -TimeManager.TEnd 604800 -TimeManager.EpisodeLength 302400") + --files ${CMAKE_SOURCE_DIR}/lecture/references/naplinfiltration3p-reference.vtu + ${CMAKE_CURRENT_BINARY_DIR}/naplinfiltration3p-00003.vtu + --command "${CMAKE_CURRENT_BINARY_DIR}/naplinfiltration3p -TimeLoop.TEnd 604800 -TimeLoop.EpisodeLength 302400") # headers for installation and headercheck install(FILES diff --git a/lecture/mm/naplinfiltration/3p/mymesitylene.hh b/lecture/mm/naplinfiltration/3p/mymesitylene.hh new file mode 100644 index 0000000000000000000000000000000000000000..c6fbc5d249b93d0793c1c75ef0b9b49caf497a0e --- /dev/null +++ b/lecture/mm/naplinfiltration/3p/mymesitylene.hh @@ -0,0 +1,391 @@ +// -*- 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 . * + *****************************************************************************/ +/*! + * \file + * \ingroup Components + * \brief Properties of mesitylene. + * + */ +#ifndef DUMUX_MYMESITYLENE_HH +#define DUMUX_MYMESITYLENE_HH + +#include +#include + +#include +#include +#include + +namespace Dumux { +namespace Components { + +/*! + * \ingroup Components + * \brief mesitylene + * + * \tparam Scalar The type used for scalar values + */ +template +class MyMesitylene +: public Components::Base > +, public Components::Liquid > +, public Components::Gas > +{ + using Consts = Constants; + using IdealGas = Dumux::IdealGas; +public: + /*! + * \brief A human readable name for the mesitylene + */ + static std::string name() + { return "mesitylene"; } + + /*! + * \brief The molar mass in \f$\mathrm{[kg/mol]}\f$ of mesitylene + */ + constexpr static Scalar molarMass() + { return 0.120; } + + /*! + * \brief Returns the critical temperature \f$\mathrm{[K]}\f$ of mesitylene + */ + constexpr static Scalar criticalTemperature() + { return 637.3; } + + /*! + * \brief Returns the critical pressure \f$\mathrm{[Pa]}\f$ of mesitylene + */ + constexpr static Scalar criticalPressure() + { return 31.3e5; } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ at mesitylene's boiling point (1 atm). + */ + constexpr static Scalar boilingTemperature() + { return 437.9; } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ at mesitylene's triple point. + */ + static Scalar tripleTemperature() + { + DUNE_THROW(Dune::NotImplemented, "tripleTemperature for mesitylene"); + } + + /*! + * \brief Returns the pressure \f$\mathrm{[Pa]}\f$ at mesitylene's triple point. + */ + static Scalar triplePressure() + { + DUNE_THROW(Dune::NotImplemented, "triplePressure for mesitylene"); + } + + /*! + * \brief The saturation vapor pressure in \f$\mathrm{[Pa]}\f$ of + * pure mesitylene at a given temperature according to + * Antoine after Betz 1997, see Gmehling et al 1980 + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + */ + static Scalar vaporPressure(Scalar temperature) + { + const Scalar A = 7.07638; + const Scalar B = 1571.005; + const Scalar C = 209.728; + + const Scalar T = temperature - 273.15; + + using std::pow; + return 100 * 1.334 * pow(Scalar(10.0), Scalar(A - (B / (T + C)))); + } + + + /*! + * \brief Specific enthalpy of liquid mesitylene \f$\mathrm{[J/kg]}\f$. + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + */ + static Scalar liquidEnthalpy(const Scalar temperature, + const Scalar pressure) + { + // Gauss quadrature rule: + // Interval: [0K; temperature (K)] + // Gauss-Legendre-Integration with variable transformation: + // \int_a^b f(T) dT \approx (b-a)/2 \sum_i=1^n \alpha_i f( (b-a)/2 x_i + (a+b)/2 ) + // with: n=2, legendre -> x_i = +/- \sqrt(1/3), \apha_i=1 + // here: a=273.15K, b=actual temperature in Kelvin + // \leadsto h(T) = \int_273.15^T c_p(T) dT + // \approx 0.5 (T-273.15) * (cp( 0.5(temperature-273.15)sqrt(1/3) ) + cp(0.5(temperature-273.15)(-1)sqrt(1/3)) + + // Enthalpy may have arbitrary reference state, but the empirical/fitted heatCapacity function needs Kelvin as input and is + // fit over a certain temperature range. This suggests choosing an interval of integration being in the actual fit range. + // I.e. choosing T=273.15K as reference point for liquid enthalpy. + using std::sqrt; + const Scalar sqrt1over3 = sqrt(1./3.); + // evaluation points according to Gauss-Legendre integration + const Scalar TEval1 = 0.5*(temperature-273.15)* sqrt1over3 + 0.5*(273.15+temperature); + // evaluation points according to Gauss-Legendre integration + const Scalar TEval2 = 0.5*(temperature-273.15)* (-1)* sqrt1over3 + 0.5*(273.15+temperature); + + const Scalar h_n = 0.5 * (temperature-273.15) * ( liquidHeatCapacity(TEval1, pressure) + liquidHeatCapacity(TEval2, pressure) ); + + return h_n; + } + + /*! + * \brief Latent heat of vaporization for mesitylene \f$\mathrm{[J/kg]}\f$. + * + * source : Reid et al. (1987, Chen method (chap. 7-11, Delta H_v = Delta H_v (T) according to chap. 7-12)) \cite reid1987 + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + */ + static Scalar heatVap(Scalar temperature, + const Scalar pressure) + { + using std::min; + using std::max; + temperature = min(temperature, criticalTemperature()); // regularization + temperature = max(temperature, 0.0); // regularization + + constexpr Scalar T_crit = criticalTemperature(); + constexpr Scalar Tr1 = boilingTemperature()/criticalTemperature(); + constexpr Scalar p_crit = criticalPressure(); + + // Chen method, eq. 7-11.4 (at boiling) + using std::log; + const Scalar DH_v_boil = Consts::R * T_crit * Tr1 * (3.978 * Tr1 - 3.958 + 1.555*log(p_crit * 1e-5 /*Pa->bar*/ ) ) + / (1.07 - Tr1); /* [J/mol] */ + + /* Variation with temp according to Watson relation eq 7-12.1*/ + using std::pow; + const Scalar Tr2 = temperature/criticalTemperature(); + const Scalar n = 0.375; + const Scalar DH_vap = DH_v_boil * pow(((1.0 - Tr2)/(1.0 - Tr1)), n); + + return (DH_vap/molarMass()); // we need [J/kg] + } + + + /*! + * \brief Specific enthalpy of mesitylene vapor \f$\mathrm{[J/kg]}\f$. + * + * This relation is true on the vapor pressure curve, i.e. as long + * as there is a liquid phase present. + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + */ + static Scalar gasEnthalpy(Scalar temperature, Scalar pressure) + { + return liquidEnthalpy(temperature,pressure) + heatVap(temperature, pressure); + } + + /*! + * \brief The density of mesitylene at a given pressure and temperature \f$\mathrm{[kg/m^3]}\f$ . + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + */ + static Scalar gasDensity(Scalar temperature, Scalar pressure) + { + return IdealGas::density(molarMass(), + temperature, + pressure); + } + + /*! + * \brief The molar density of mesitylene in \f$\mathrm{[mol/m^3]}\f$, + * depending on pressure and temperature. + * \param temperature The temperature of the gas + * \param pressure The pressure of the gas + */ + static Scalar gasMolarDensity(Scalar temperature, Scalar pressure) + { return IdealGas::molarDensity(temperature, pressure); } + + /*! + * \brief The density of pure mesitylene at a given pressure and temperature \f$\mathrm{[kg/m^3]}\f$. + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + */ + static Scalar liquidDensity(Scalar temperature, Scalar pressure) + { + Scalar fact = getParam("Problem.MultiplierNAPLDensity"); + return fact * liquidMolarDensity(temperature, pressure)*molarMass(); + } + + /*! + * \brief The molar density of pure mesitylene at a given pressure and temperature + * \f$\mathrm{[mol/m^3]}\f$. + * + * source : Reid et al. (1987, Modified Racket technique (chap. 3-11, eq. 3-11.9)) \cite reid1987 + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + */ + static Scalar liquidMolarDensity(Scalar temperature, Scalar pressure) + { + using std::min; + using std::max; + temperature = min(temperature, 500.0); // regularization + temperature = max(temperature, 250.0); + + const Scalar Z_RA = 0.2556; // from equation + + using std::pow; + const Scalar expo = 1.0 + pow(1.0 - temperature/criticalTemperature(), 2.0/7.0); + Scalar V = Consts::R*criticalTemperature()/criticalPressure()*pow(Z_RA, expo); // liquid molar volume [cm^3/mol] + + return 1.0/V; // molar density [mol/m^3] + } + + /*! + * \brief Returns true if the gas phase is assumed to be compressible + */ + static constexpr bool gasIsCompressible() + { return true; } + + /*! + * \brief Returns true if the gas phase is assumed to be ideal + */ + static constexpr bool gasIsIdeal() + { return true; } + + /*! + * \brief Returns true if the liquid phase is assumed to be compressible + */ + static constexpr bool liquidIsCompressible() + { return false; } + + /*! + * \brief The dynamic viscosity \f$\mathrm{[Pa*s]}\f$ of mesitylene vapor + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + * \param regularize defines, if the functions is regularized or not, set to true by default + */ + static Scalar gasViscosity(Scalar temperature, Scalar pressure, bool regularize=true) + { + using std::min; + using std::max; + temperature = min(temperature, 500.0); // regularization + temperature = max(temperature, 250.0); + + // reduced temperature + Scalar Tr = temperature/criticalTemperature(); + + Scalar Fp0 = 1.0; + Scalar xi = 0.00474; + + using std::pow; + using std::exp; + Scalar eta_xi = + Fp0*(0.807*pow(Tr,0.618) + - 0.357*exp(-0.449*Tr) + + 0.34*exp(-4.058*Tr) + + 0.018); + + return eta_xi/xi/1e7; // [Pa s] + } + + /*! + * \brief The dynamic viscosity \f$\mathrm{[Pa*s]}\f$ of pure mesitylene. + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + */ + static Scalar liquidViscosity(Scalar temperature, Scalar pressure) + { + using std::min; + using std::max; + temperature = min(temperature, 500.0); // regularization + temperature = max(temperature, 250.0); + + const Scalar A = -6.749; + const Scalar B = 2010.0; + + using std::exp; + Scalar fact = getParam("Problem.MultiplierNAPLViscosity"); + return fact * exp(A + B/temperature)*1e-3; // [Pa s] + } + + /*! + * \brief Specific heat capacity of liquid mesitylene \f$\mathrm{[J/(kg*K)]}\f$. + * + * source : Reid et al. (1987, Missenard group contrib. method (chap 5-7, Table 5-11, s. example 5-8)) \cite reid1987 + * + * \param temperature temperature of component in \f$\mathrm{[K]}\f$ + * \param pressure pressure of component in \f$\mathrm{[Pa]}\f$ + * + */ + static Scalar liquidHeatCapacity(const Scalar temperature, + const Scalar pressure) + { + /* according Reid et al. : Missenard group contrib. method (s. example 5-8) */ + /* Mesitylen: C9H12 : 3* CH3 ; 1* C6H5 (phenyl-ring) ; -2* H (this was to much!) */ + /* linear interpolation between table values [J/(mol K)]*/ + Scalar H, CH3, C6H5; + if(temperature<298.) { + // extrapolation for Temperature<273 */ + H = 13.4+1.2*(temperature-273.0)/25.; // 13.4 + 1.2 = 14.6 = H(T=298K) i.e. interpolation of table values 273=298.0)&&(temperature<323.)){ // i.e. interpolation of table values 298=323.0)&&(temperature<348.)){// i.e. interpolation of table values 323=348.0); + + /* take care: extrapolation for Temperature>373 */ + H = 16.7+2.1*(temperature-348.0)/25.; /* leads probably to underestimates */ + CH3 = 45.8+2.5*(temperature-348.0)/25.; + C6H5 = 129.7+6.3*(temperature-348.0)/25.; + } + + return (C6H5 + 3*CH3 - 2*H)/molarMass(); // J/(mol K) -> J/(kg K) + } + + /*! + * \brief Thermal conductivity \f$\mathrm{[[W/(m*K)]}\f$ of mesitylene + * + * see: http://pubs.acs.org/doi/pdf/10.1021/ci000139t + * + * \param temperature absolute temperature in \f$\mathrm{[K]}\f$ + * \param pressure of the phase in \f$\mathrm{[Pa]}\f$ + */ + static Scalar liquidThermalConductivity( Scalar temperature, Scalar pressure) + { + return 0.1351; + } +}; + +} // end namespace Components + +} // end namespace Dumux + +#endif diff --git a/lecture/mm/naplinfiltration/3p/naplinfiltration3p.cc b/lecture/mm/naplinfiltration/3p/naplinfiltration3p.cc index 5de829adaa1ab7886fc315bc9309ba6cbe054764..25b061174a1848cda5cf37bc3062cbc7cfd70a46 100644 --- a/lecture/mm/naplinfiltration/3p/naplinfiltration3p.cc +++ b/lecture/mm/naplinfiltration/3p/naplinfiltration3p.cc @@ -23,7 +23,34 @@ */ #include "config.h" #include "problem.hh" -#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + + /*! * \brief Provides an interface for customizing error messages associated with @@ -51,8 +78,167 @@ void usage(const char *progName, const std::string &errorMsg) } } -int main(int argc, char** argv) +//////////////////////// +// the main function +//////////////////////// +int main(int argc, char** argv) try +{ + + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(InfiltrationThreePBoxTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) // Fehlt ebenso + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = ILU0BiCGSTABBackend; + auto linearSolver = std::make_shared(); + + // the non-linear solver + using NewtonSolver = Dumux::NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + //! set some check points for the time loop + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength")); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // set the end of the next time step as time in the problem to control + // the boundary conditions for the implicit Euler scheme + problem->setTime(timeLoop->time()+timeLoop->timeStepSize()); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // write vtk output on check points + if (timeLoop->isCheckPoint() || timeLoop->timeStepIndex() == 1 || timeLoop->finished()) + vtkWriter.write(timeLoop->time()); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + return 0; +} // end main + +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(InfiltrationProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/naplinfiltration/3p/naplinfiltration3p.input b/lecture/mm/naplinfiltration/3p/naplinfiltration3p.input index ea4efe94aa3736413ede10f851b415c10ba302e3..8b9da4e1abf7397961c365ade2dda71eae315826 100644 --- a/lecture/mm/naplinfiltration/3p/naplinfiltration3p.input +++ b/lecture/mm/naplinfiltration/3p/naplinfiltration3p.input @@ -1,4 +1,4 @@ -[TimeManager] +[TimeLoop] DtInitial = 60 # [s] TEnd = 31536000 # [s] #TEnd = 315360000000 # [s] @@ -9,11 +9,19 @@ UpperRight = 500 10 # coordinates of upper right grid corner [m] Cells = 250 10 # number of cells in (x, y) direction [-] [SpatialParams] -Permeability = 1.e-11 # [m^2] -Porosity = 0.40 # [-] -VanGenuchtenAlpha = 0.0005 # [-] -VanGenuchtenN = 4.0 # [-] -MultiplierNaplDensity = 1.0 # 1.0 means Mesitylene (an LNAPL) [-] +permeability = 1.e-11 # [m^2] +porosity = 0.40 # [-] +vanGenuchtenAlpha = 0.0005 # [-] +vanGenuchtenN = 4.0 # [-] + +[Problem] +Name = naplinfiltration3p +MultiplierNAPLDensity = 1.0 # 1.0 corresponds to Mesitylene (an LNAPL) [-] +MultiplierNAPLViscosity = 1.0 # 1.0 corresponds to Mesitylene [-] + +[Output] +PlotFluidMatrixInteractions = true [Newton] -MaxSteps = 6 \ No newline at end of file +MaxRelativeShift = 1.0e-4 +MaxSteps = 6 diff --git a/lecture/mm/naplinfiltration/3p/problem.hh b/lecture/mm/naplinfiltration/3p/problem.hh index 325b6dc10667243011a108f9561ede8160adc620..c4ff523ae42bec73898f7385f3a7f40b198fa8ec 100644 --- a/lecture/mm/naplinfiltration/3p/problem.hh +++ b/lecture/mm/naplinfiltration/3p/problem.hh @@ -18,58 +18,76 @@ *****************************************************************************/ /*! * \file - * - * \brief Isothermal NAPL infiltration problem: LNAPL contaminates + * \ingroup ThreePTests + * \brief Isothermal NAPL infiltration problem: L- or DNAPL contaminates * the unsaturated and the saturated groundwater zone. */ #ifndef DUMUX_NAPLINFILTRATIONPROBLEM_3P_HH #define DUMUX_NAPLINFILTRATIONPROBLEM_3P_HH -#include -#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mymesitylene.hh" +#include -#include #include namespace Dumux { +/*! + * \ingroup ThreePTests + * \brief Isothermal NAPL infiltration problem: LNAPL contaminates + * the unsaturated and the saturated groundwater zone. + */ template -class InfiltrationProblem; +class InfiltrationThreePProblem; namespace Properties { -NEW_TYPE_TAG(InfiltrationProblem, INHERITS_FROM(BoxThreeP, InfiltrationSpatialParams)); +NEW_TYPE_TAG(InfiltrationThreePTypeTag, INHERITS_FROM(ThreeP)); +NEW_TYPE_TAG(InfiltrationThreePBoxTypeTag, INHERITS_FROM(BoxModel, InfiltrationThreePTypeTag)); // Set the grid type -SET_TYPE_PROP(InfiltrationProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(InfiltrationThreePTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(InfiltrationProblem, Problem, Dumux::InfiltrationProblem); +SET_TYPE_PROP(InfiltrationThreePTypeTag, Problem, InfiltrationThreePProblem); // Set the fluid system -SET_TYPE_PROP(InfiltrationProblem, - FluidSystem, - FluidSystems::H2OAirNAPL); - -// Enable gravity? -SET_BOOL_PROP(InfiltrationProblem, ProblemEnableGravity, true); - -// Write newton convergence? -SET_BOOL_PROP(InfiltrationProblem, NewtonWriteConvergence, false); - -// Maximum tolerated relative error in the Newton method -SET_SCALAR_PROP(InfiltrationProblem, NewtonMaxRelativeShift, 1e-4); +SET_PROP(InfiltrationThreePTypeTag, FluidSystem) +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Water = Components::TabulatedComponent>; + using WettingFluid = FluidSystems::OnePLiquid; + using NonwettingFluid = FluidSystems::OnePLiquid>; + using Gas = FluidSystems::OnePGas>; +public: + using type = FluidSystems::ThreePImmiscible; +}; -// -1 backward differences, 0: central differences, +1: forward differences -SET_INT_PROP(InfiltrationProblem, ImplicitNumericDifferenceMethod, 1); +// Set the spatial parameters +SET_PROP(InfiltrationThreePTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = InfiltrationSpatialParams; +}; -// Set the linear solver -SET_TYPE_PROP(InfiltrationProblem, LinearSolver, ILU0BiCGSTABBackend); -} +}// end namespace Properties /*! - * \ingroup ThreePThreeCBoxModel - * \ingroup BoxTestProblems + * \ingroup ThreePModel + * \ingroup ImplicitTestProblems * \brief Isothermal NAPL infiltration problem: LNAPL contaminates * the unsaturated and the saturated groundwater zone. * @@ -88,53 +106,41 @@ SET_TYPE_PROP(InfiltrationProblem, LinearSolver, ILU0BiCGSTABBackend); * Top and bottom are Neumann boundaries, all no-flow except for the small * infiltration zone in the upper left part. * - * This problem uses the \ref ThreePThreeCModel or the \ref ThreePModel. + * This problem uses the \ref ThreePModel. * * This problem should typically be simulated for 30 days. * A good choice for the initial time step size is 60 s. - * To adjust the simulation time it is necessary to edit the file test_naplfiltrationexercise.input + * To adjust the simulation time it is necessary to edit the file naplinfiltrationexercise.input * * To run the simulation execute the following line in shell: - * ./test_naplfiltrationexercise -ParameterFile test_naplfiltrationexercise.input + * ./naplinfiltrationexercise -parameterFile naplinfiltrationexercise.input * */ template -class InfiltrationProblem : public ImplicitPorousMediaProblem +class InfiltrationThreePProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; + using ParentType = PorousMediumFlowProblem; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; - // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; enum { pressureIdx = Indices::pressureIdx, - switch1Idx = Indices::swIdx, - switch2Idx = Indices::snIdx, + swIdx = Indices::swIdx, + snIdx = Indices::snIdx, - contiWEqIdx = Indices::contiWEqIdx, - contiNEqIdx = Indices::contiNEqIdx, - contiGEqIdx = Indices::contiGEqIdx, - - // Grid and world dimension - dim = GridView::dimension, + // world dimension dimWorld = GridView::dimensionworld }; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); - 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 GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - 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 Dune::FieldVector GlobalPosition; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; public: /*! @@ -143,49 +149,43 @@ public: * \param timeManager The time manager * \param gridView The grid view */ - InfiltrationProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-5) + InfiltrationThreePProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { temperature_ = 273.15 + 10.0; // -> 10 degrees Celsius + FluidSystem::init(282.15, 284.15, 3, 8e4, 3e5, 200); - episodeLength_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); + name_ = getParam("Problem.Name"); - FluidSystem::init(/*tempMin=*/temperature_ - 1, - /*tempMax=*/temperature_ + 1, - /*nTemp=*/3, - /*pressMin=*/0.8*1e5, - /*pressMax=*/3*1e5, - /*nPress=*/200); + this->spatialParams().plotMaterialLaw(); + time_ = 0.0; } /*! * \name Problem parameters */ // \{ + void setTime(Scalar time) + { time_ = time; } /*! * \brief The problem name. * * This is used as a prefix for files generated by the simulation. */ - std::string name() const - { return "infiltration3p"; } + const std::string& name() const + { return name_; } /*! * \brief Returns the temperature within the domain. * + * \param globalPos The position + * * This problem assumes a temperature of 10 degrees Celsius. */ - Scalar temperature() const + Scalar temperatureAtPos(const GlobalPosition &globalPos) const { return temperature_; - }; - - void sourceAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - values = 0; } // \} @@ -199,66 +199,59 @@ public: * \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 globalPos The position of the finite volume in global coordinates + * \param globalPos The position for which the bc type should be evaluated */ - void boundaryTypesAtPos(BoundaryTypes &values, - const GlobalPosition &globalPos) const + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { - if(globalPos[0] > this->bBoxMax()[0] - eps_) - values.setAllDirichlet(); - else if(globalPos[0] < this->bBoxMin()[0] + eps_) + BoundaryTypes values; + + if(globalPos[0] > this->fvGridGeometry().bBoxMax()[0] - eps_ + || globalPos[0] < this->fvGridGeometry().bBoxMin()[0] + eps_) values.setAllDirichlet(); else values.setAllNeumann(); + + return values; } /*! * \brief Evaluate the boundary conditions for a dirichlet - * control volume. + * boundary segment. * * \param values The dirichlet values for the primary variables - * \param globalPos The position of the center of the finite volume - * for which the dirichlet condition ought to be - * set in global coordinates + * \param globalPos The position for which the bc type should be evaluated * * For this method, the \a values parameter stores primary variables. */ - void dirichletAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - initial_(values, globalPos); + return initial_(globalPos); } - /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * \param values The neumann values for the conservation equations in units of - * \f$ [ \textnormal{unit of conserved quantity} / (m^2 \cdot s )] \f$ - * \param globalPos The position of the boundary face's integration point in global coordinates + * \param globalPos The position of the integration point of the boundary segment. * * 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 &globalPos) const + NumEqVector neumannAtPos(const GlobalPosition &globalPos) const { - values = 0; + NumEqVector values(0.0); // negative values for injection - if (this->timeManager().time() < 2592000) + if (time_ < 2592000.0 - eps_) { - if (globalPos[0] < 175.0 + eps_ - && globalPos[0] > 150.0 - eps_ - && globalPos[1] > this->bBoxMax()[1] - eps_) // upper boundary + if ((globalPos[0] < 175.0 + eps_) && (globalPos[0] > 150.0 - eps_) + && (globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_)) { - values[contiWEqIdx] = 0.0; - // mole flux conversion via M(mesit.) = 0,120 kg/mol --> 1.2e-4 kg/(s*m) - values[contiNEqIdx] = -0.001*0.12; // 3p needs a mass fraction - values[contiGEqIdx] = 0.0; + // mol fluxes, convert with M(Mesit.)=0,120 kg/mol --> 1.2e-4 kg/(sm) + values[Indices::conti0EqIdx + FluidSystem::nCompIdx] = -0.001*0.12; } } + + return values; } // \} @@ -271,79 +264,85 @@ public: /*! * \brief Evaluate the initial value for a control volume. * - * \param values The dirichlet values for the primary variables - * \param globalPos The position of the center of the finite volume - * for which the initial values ought to be - * set (in global coordinates) + * \param globalPos The position for which the initial condition should be evaluated * - * For this method, the \a values parameter stores primary variables. + * For this method, the \a values parameter stores primary + * variables. */ - void initialAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { - initial_(values, globalPos); + return initial_(globalPos); } - bool shouldWriteOutput() const - { - return this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } + /*! + * \brief Returns the temperature within the domain. + * + * This problem assumes a uniform temperature of 10 degrees Celsius. + */ + Scalar temperature() const + { return temperature_; } + - void episodeEnd() - { - this->timeManager().startNextEpisode(episodeLength_); - } private: // internal method for the initial condition (reused for the // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const + PrimaryVariables initial_(const GlobalPosition &globalPos) const { - const auto& materialLawParams = this->spatialParams().materialLawParams(); - const Scalar swr = materialLawParams.swr(); - const Scalar sgr = materialLawParams.sgr(); + PrimaryVariables values(0.0); + + Scalar y = globalPos[1]; + Scalar x = globalPos[0]; - if(globalPos[1] >= -1e-3*globalPos[0] + 5) + const auto& materialLawParams = this->spatialParams().materialLawParamsAtPos(globalPos); + Scalar swr = materialLawParams.swr(); + Scalar sgr = materialLawParams.sgr(); + + if(y > (-1e-3*x+5) - eps_) { - const Scalar pc = std::max(0.0, 9.81*1000.0*(globalPos[1] + 5e-4*globalPos[0] - 5)); - const Scalar sw = std::min(1.0-sgr, std::max(swr, invertPcGW_(pc, materialLawParams))); + Scalar pc = 9.81 * 1000.0 * (y - (-5e-4*x+5)); + if (pc < 0.0) pc = 0.0; - values[pressureIdx] = 1.0e5; - values[switch1Idx] = sw; - values[switch2Idx] = 0.0; - } - else + Scalar sw = invertPcgw_(pc, this->spatialParams().materialLawParamsAtPos(globalPos)); + if(sw < swr) sw = swr; + if(sw > 1.-sgr) sw = 1.-sgr; + + values[pressureIdx] = 1e5; + values[swIdx] = sw; + values[snIdx] = 0.; + }else { - values[pressureIdx] = 1.0e5 + 9.81*1000.0*(5 - 5.0e-4*globalPos[0] - globalPos[1]); - values[switch1Idx] = 1.0 - sgr; - values[switch2Idx] = 0.0; + values[pressureIdx] = 1e5 + 9.81 * 1000.0 * ((-5e-4*x+5) - y); + values[swIdx] = 1.-sgr; + values[snIdx] = 0.; } + + return values; } - static Scalar invertPcGW_(const Scalar pcIn, - const MaterialLawParams &pcParams) + // small solver inverting the pc curve + template + static Scalar invertPcgw_(Scalar pcIn, const MaterialLawParams &pcParams) { + using MaterialLaw = typename ParentType::SpatialParams::MaterialLaw; Scalar lower(0.0); Scalar upper(1.0); - const unsigned int maxIterations = 25; + const unsigned int maxIter = 25; const Scalar bisLimit = 1.0; - Scalar sw, pcGW; - for (unsigned int k = 1; k <= maxIterations; k++) + Scalar sw, pcgw; + for (unsigned int k = 1; k <= maxIter; k++) { sw = 0.5*(upper + lower); - pcGW = MaterialLaw::pcgw(pcParams, sw); - const Scalar delta = std::abs(pcGW - pcIn); + pcgw = MaterialLaw::pcgw(pcParams, sw); + const Scalar delta = std::abs(pcgw - pcIn); if (delta < bisLimit) return sw; - if (k == maxIterations) + if (k == maxIter) return sw; - if (pcGW > pcIn) + if (pcgw > pcIn) lower = sw; else upper = sw; @@ -352,9 +351,11 @@ private: } Scalar temperature_; - const Scalar eps_; - Scalar episodeLength_; + static constexpr Scalar eps_ = 1e-6; + std::string name_; + Scalar time_; }; -} //end namespace + +} // end namespace Dumux #endif diff --git a/lecture/mm/naplinfiltration/3p3c/CMakeLists.txt b/lecture/mm/naplinfiltration/3p3c/CMakeLists.txt index 44b9d227c1be4d6522e6a7b4074aa52208864063..0de6c3ec0edbcb63e25564100cf1f14c11e99594 100644 --- a/lecture/mm/naplinfiltration/3p3c/CMakeLists.txt +++ b/lecture/mm/naplinfiltration/3p3c/CMakeLists.txt @@ -4,10 +4,10 @@ add_input_file_links() add_dumux_test(naplinfiltration3p3c naplinfiltration3p3c naplinfiltration3p3c.cc python ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py --script fuzzy - --files ${CMAKE_SOURCE_DIR}/lecture/references/infiltration3p3c-reference.vtu - ${CMAKE_CURRENT_BINARY_DIR}/infiltration3p3c-00003.vtu + --files ${CMAKE_SOURCE_DIR}/lecture/references/naplinfiltration3p3c-reference.vtu + ${CMAKE_CURRENT_BINARY_DIR}/naplinfiltration3p3c-00003.vtu --zeroThreshold {"Sn":5e-7,"x^mesitylene_g":1e-8,"x^mesitylene_n":1e-5,"x^mesitylene_w":5e-11} - --command "${CMAKE_CURRENT_BINARY_DIR}/naplinfiltration3p3c -TimeManager.TEnd 604800 -TimeManager.EpisodeLength 302400") + --command "${CMAKE_CURRENT_BINARY_DIR}/naplinfiltration3p3c -TimeLoop.TEnd 604800 -TimeLoop.EpisodeLength 302400") # headers for installation and headercheck install(FILES diff --git a/lecture/mm/naplinfiltration/h2oairnaplfluidsystem.hh b/lecture/mm/naplinfiltration/3p3c/myh2oairmesitylene.hh similarity index 53% rename from lecture/mm/naplinfiltration/h2oairnaplfluidsystem.hh rename to lecture/mm/naplinfiltration/3p3c/myh2oairmesitylene.hh index bd62bb4218da2b96ef1750cad157f4e45e684ffe..e6845139186e97a393fda65e0605edb68c4fbe45 100644 --- a/lecture/mm/naplinfiltration/h2oairnaplfluidsystem.hh +++ b/lecture/mm/naplinfiltration/3p3c/myh2oairmesitylene.hh @@ -18,50 +18,48 @@ *****************************************************************************/ /*! * \file - * - * \brief A fluid system with water, gas and NAPL as phases and - * \f$H_2O\f$ and \f$Air\f$ and \f$NAPL (contaminant)\f$ as components. - * It uses Mesitylene Properties, but allows for a scaling of density - * thus enabling an artifical DNAPL if desired + * \ingroup Fluidsystems + * \brief @copybrief Dumux::FluidSystems::MyH2OAirMesitylene */ -#ifndef DUMUX_H2O_AIR_NAPL_FLUID_SYSTEM_HH -#define DUMUX_H2O_AIR_NAPL_FLUID_SYSTEM_HH +#ifndef DUMUX_MY_H2O_AIR_MESITYLENE_FLUID_SYSTEM_HH +#define DUMUX_MY_H2O_AIR_MESITYLENE_FLUID_SYSTEM_HH #include #include #include #include -#include #include +#include #include #include #include #include -#include -namespace Dumux -{ -namespace FluidSystems -{ +namespace Dumux { +namespace FluidSystems { /*! - * \brief A compositional fluid with water and molecular nitrogen as - * components in both, the liquid and the gas phase. + * \ingroup Fluidsystems + * \brief A three-phase fluid system featuring gas, NAPL and water as phases and + * distilled water \f$(\mathrm{H_2O})\f$ and air (Pseudo component composed of + * \f$\mathrm{79\%\;N_2}\f$, \f$\mathrm{20\%\;O_2}\f$ and Mesitylene \f$(\mathrm{C_6H_3(CH_3)_3})\f$ as components. + * + * It assumes all phases to be ideal mixtures. */ -template > > -class H2OAirNAPL - : public BaseFluidSystem > +template > > +class MyH2OAirMesitylene + : public Base > { - typedef H2OAirNAPL ThisType; - typedef BaseFluidSystem Base; + using ThisType = MyH2OAirMesitylene; + using Base = Dumux::FluidSystems::Base; public: - typedef Dumux::Mesitylene NAPL; - typedef Dumux::Air Air; - typedef H2OType H2O; + using NAPL = Components::Mesitylene; + using Air = Dumux::Components::Air; + using H2O = H2OType; static const int numPhases = 3; @@ -73,14 +71,14 @@ public: static const int H2OIdx = 0; static const int NAPLIdx = 1; - static const int airIdx = 2; + static const int AirIdx = 2; // export 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 = H2OIdx; static const int nCompIdx = NAPLIdx; - static const int gCompIdx = airIdx; + static const int gCompIdx = AirIdx; /*! * \brief Initialize the fluid system's static parameters generically @@ -102,38 +100,46 @@ public: * \brief Initialize the fluid system's static parameters using * problem specific temperature and pressure ranges * - * \param tempMin The minimum temperature used for tabulation of water [K] - * \param tempMax The maximum temperature used for tabulation of water [K] + * \param tempMin The minimum temperature used for tabulation of water \f$\mathrm{[K]}\f$ + * \param tempMax The maximum temperature used for tabulation of water \f$\mathrm{[K]}\f$ * \param nTemp The number of ticks on the temperature axis of the table of water - * \param pressMin The minimum pressure used for tabulation of water [Pa] - * \param pressMax The maximum pressure used for tabulation of water [Pa] + * \param pressMin The minimum pressure used for tabulation of water \f$\mathrm{[Pa]}\f$ + * \param pressMax The maximum pressure used for tabulation of water \f$\mathrm{[Pa]}\f$ * \param nPress The number of ticks on the pressure axis of the table of water */ static void init(Scalar tempMin, Scalar tempMax, unsigned nTemp, Scalar pressMin, Scalar pressMax, unsigned nPress) { - if (H2O::isTabulated) { - std::cout << "Initializing tables for the H2O fluid properties (" - << nTemp*nPress - << " entries).\n"; - + if (H2O::isTabulated) + { H2O::init(tempMin, tempMax, nTemp, pressMin, pressMax, nPress); } } + /*! + * \brief Returns whether the fluids are miscible + */ + static constexpr bool isMiscible() + { return true; } /*! - * \brief Return whether a phase is liquid + * \brief Return whether a phase is gaseous * * \param phaseIdx The index of the fluid phase to consider */ - static bool isLiquid(int phaseIdx) + static constexpr bool isGas(int phaseIdx) { assert(0 <= phaseIdx && phaseIdx < numPhases); - return phaseIdx != gPhaseIdx; + return phaseIdx == gPhaseIdx; } + /*! + * \brief Returns true if and only if a fluid phase is assumed to + * be an ideal gas. + * + * \param phaseIdx The index of the fluid phase to consider + */ static bool isIdealGas(int phaseIdx) { return phaseIdx == gPhaseIdx && H2O::gasIsIdeal() && Air::gasIsIdeal() && NAPL::gasIsIdeal(); } @@ -143,8 +149,8 @@ public: * * We define an ideal mixture as a fluid phase where the fugacity * coefficients of all components times the pressure of the phase - * are indepent on the fluid composition. This assumtion is true - * if Henry's law and Rault's law apply. If you are unsure what + * are independent on the fluid composition. This assumption is true + * if Henry's law and Raoult's law apply. If you are unsure what * this function should return, it is safe to return false. The * only damage done will be (slightly) increased computation times * in some cases. @@ -154,7 +160,7 @@ public: static bool isIdealMixture(int phaseIdx) { assert(0 <= phaseIdx && phaseIdx < numPhases); - // we assume Henry's and Rault's laws for the water phase and + // we assume Henry's and Raoult's laws for the water phase and // and no interaction between gas molecules of different // components, so all phases are ideal mixtures! return true; @@ -169,7 +175,7 @@ public: * * \param phaseIdx The index of the fluid phase to consider */ - static bool isCompressible(int phaseIdx) + static constexpr bool isCompressible(int phaseIdx) { assert(0 <= phaseIdx && phaseIdx < numPhases); // gases are always compressible @@ -188,11 +194,13 @@ public: */ static std::string phaseName(int phaseIdx) { - switch (phaseIdx) { - case wPhaseIdx: return "w"; - case nPhaseIdx: return "n"; - case gPhaseIdx: return "g";; - }; + assert(0 <= phaseIdx && phaseIdx < numPhases); + switch (phaseIdx) + { + case wPhaseIdx: return "aq"; + case nPhaseIdx: return "napl"; + case gPhaseIdx: return "gas"; + } DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); } @@ -202,52 +210,56 @@ public: static std::string componentName(int compIdx) { switch (compIdx) { - case H2OIdx: return H2O::name(); - case airIdx: return Air::name(); - case NAPLIdx: return NAPL::name(); - }; + case H2OIdx: return H2O::name(); + case AirIdx: return Air::name(); + case NAPLIdx: return NAPL::name(); + } DUNE_THROW(Dune::InvalidStateException, "Invalid component index " << compIdx); } /*! - * \brief Return the molar mass of a component in [kg/mol]. + * \brief Return the molar mass of a component in \f$\mathrm{[kg/mol]}\f$. + * \param compIdx The index of the component */ static Scalar molarMass(int compIdx) { switch (compIdx) { - case H2OIdx: return H2O::molarMass(); - case airIdx: return Air::molarMass(); - case NAPLIdx: return NAPL::molarMass(); - }; + case H2OIdx: return H2O::molarMass(); + case AirIdx: return Air::molarMass(); + case NAPLIdx: return NAPL::molarMass(); + } DUNE_THROW(Dune::InvalidStateException, "Invalid component index " << compIdx); } + using Base::density; /*! - * \brief Given all mole fractions in a phase, return the phase - * density [kg/m^3]. + * \brief Given a phase's composition, temperature, pressure, and + * the partial pressures of all components, return its + * density \f$\mathrm{[kg/m^3]}\f$. + * + * We apply Eq. (7) + * in Class et al. (2002a) \cite A3:class:2002b
+ * for the water density. + * + * \param fluidState The fluid state + * \param phaseIdx The index of the phase */ - using Base::density; template static Scalar density(const FluidState &fluidState, int phaseIdx) { if (phaseIdx == wPhaseIdx) { - // See: Ochs 2008 - // \todo: proper citation - Scalar rholH2O = H2O::liquidDensity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); - Scalar clH2O = rholH2O/H2O::molarMass(); - + // See: Eq. (7) in Class et al. (2002a) // this assumes each dissolved molecule displaces exactly one // water molecule in the liquid - return - clH2O*(H2O::molarMass()*fluidState.moleFraction(wPhaseIdx, H2OIdx) + - Air::molarMass()*fluidState.moleFraction(wPhaseIdx, airIdx) + - NAPL::molarMass()*fluidState.moleFraction(wPhaseIdx, NAPLIdx)); + return H2O::liquidMolarDensity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)) + * (H2O::molarMass()*fluidState.moleFraction(wPhaseIdx, H2OIdx) + + Air::molarMass()*fluidState.moleFraction(wPhaseIdx, AirIdx) + + NAPL::molarMass()*fluidState.moleFraction(wPhaseIdx, NAPLIdx)); } else if (phaseIdx == nPhaseIdx) { // assume pure NAPL for the NAPL phase Scalar pressure = NAPL::liquidIsCompressible()?fluidState.pressure(phaseIdx):1e100; - return GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, MultiplierNaplDensity) - * NAPL::liquidDensity(fluidState.temperature(phaseIdx), pressure); + return getParam("Problem.MultiplierNAPLDensity") * NAPL::liquidDensity(fluidState.temperature(phaseIdx), pressure); } assert (phaseIdx == gPhaseIdx); @@ -255,21 +267,56 @@ public: fluidState.moleFraction(gPhaseIdx, H2OIdx) * fluidState.pressure(gPhaseIdx); Scalar pAir = - fluidState.moleFraction(gPhaseIdx, airIdx) * + fluidState.moleFraction(gPhaseIdx, AirIdx) * fluidState.pressure(gPhaseIdx); Scalar pNAPL = fluidState.moleFraction(gPhaseIdx, NAPLIdx) * fluidState.pressure(gPhaseIdx); - return - H2O::gasDensity(fluidState.temperature(phaseIdx), pH2O) + - Air::gasDensity(fluidState.temperature(phaseIdx), pAir) + - NAPL::gasDensity(fluidState.temperature(phaseIdx), pNAPL); + return H2O::gasDensity(fluidState.temperature(phaseIdx), pH2O) + + Air::gasDensity(fluidState.temperature(phaseIdx), pAir) + + NAPL::gasDensity(fluidState.temperature(phaseIdx), pNAPL); } + using Base::molarDensity; /*! - * \brief Return the viscosity of a phase. + * \brief The molar density \f$\rho_{mol,\alpha}\f$ + * of a fluid phase \f$\alpha\f$ in \f$\mathrm{[mol/m^3]}\f$ + * + * The molar density is defined by the + * mass density \f$\rho_\alpha\f$ and the mean molar mass \f$\overline M_\alpha\f$: + * + * \f[\rho_{mol,\alpha} = \frac{\rho_\alpha}{\overline M_\alpha} \;.\f] */ + template + static Scalar molarDensity(const FluidState &fluidState, int phaseIdx) + { + Scalar temperature = fluidState.temperature(phaseIdx); + Scalar pressure = fluidState.pressure(phaseIdx); + + if (phaseIdx == nPhaseIdx) + { + // assume pure NAPL for the NAPL phase + return NAPL::liquidMolarDensity(temperature, pressure); + } + else if (phaseIdx == wPhaseIdx) + { + return H2O::liquidMolarDensity(temperature, pressure); + } + else + { + return H2O::gasMolarDensity(temperature, fluidState.partialPressure(gPhaseIdx, H2OIdx)) + + NAPL::gasMolarDensity(temperature, fluidState.partialPressure(gPhaseIdx, NAPLIdx)) + + Air::gasMolarDensity(temperature, fluidState.partialPressure(gPhaseIdx, AirIdx)); + } + } + using Base::viscosity; + /*! + * \brief Return the viscosity of a phase \f$\mathrm{[Pa s]}\f$. + * \param fluidState The fluid state + * \param phaseIdx The index of the phase + * \todo Check the parameter phiCAW for the mesitylene case and give a physical meaningful name + */ template static Scalar viscosity(const FluidState &fluidState, int phaseIdx) @@ -281,7 +328,8 @@ public: } else if (phaseIdx == nPhaseIdx) { // assume pure NAPL viscosity - return NAPL::liquidViscosity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + return NAPL::liquidViscosity(fluidState.temperature(phaseIdx), + fluidState.pressure(phaseIdx)); } assert (phaseIdx == gPhaseIdx); @@ -290,7 +338,7 @@ public: * * See: R. Reid, et al.: The Properties of Gases and Liquids, * 4th edition, McGraw-Hill, 1987, 407-410 - * 5th edition, McGraw-Hill, 20001, p. 9.21/22 + * 5th edition, McGraw-Hill, 2001, p. 9.21/22 * * in this case, we use a simplified version in order to avoid * computationally costly evaluation of sqrt and pow functions and @@ -300,7 +348,7 @@ public: Scalar muResult; const Scalar mu[numComponents] = { H2O::gasViscosity(fluidState.temperature(phaseIdx), H2O::vaporPressure(fluidState.temperature(phaseIdx))), - Air::simpleGasViscosity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)), + Air::gasViscosity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)), NAPL::gasViscosity(fluidState.temperature(phaseIdx), NAPL::vaporPressure(fluidState.temperature(phaseIdx))) }; // molar masses @@ -310,83 +358,98 @@ public: NAPL::molarMass() }; - Scalar muAW = mu[airIdx]*fluidState.moleFraction(gPhaseIdx, airIdx) - + mu[H2OIdx]*fluidState.moleFraction(gPhaseIdx, H2OIdx) - / (fluidState.moleFraction(gPhaseIdx, airIdx) - + fluidState.moleFraction(gPhaseIdx, H2OIdx)); - Scalar xAW = fluidState.moleFraction(gPhaseIdx, airIdx) - + fluidState.moleFraction(gPhaseIdx, H2OIdx); + Scalar muAW = mu[AirIdx]*fluidState.moleFraction(gPhaseIdx, AirIdx) + + mu[H2OIdx]*fluidState.moleFraction(gPhaseIdx, H2OIdx) + / (fluidState.moleFraction(gPhaseIdx, AirIdx) + + fluidState.moleFraction(gPhaseIdx, H2OIdx)); + Scalar xAW = fluidState.moleFraction(gPhaseIdx, AirIdx) + + fluidState.moleFraction(gPhaseIdx, H2OIdx); - Scalar MAW = (fluidState.moleFraction(gPhaseIdx, airIdx)*Air::molarMass() + Scalar MAW = (fluidState.moleFraction(gPhaseIdx, AirIdx)*Air::molarMass() + fluidState.moleFraction(gPhaseIdx, H2OIdx)*H2O::molarMass()) - / xAW; + / xAW; Scalar phiCAW = 0.3; // simplification for this particular system /* actually like this - * Scalar phiCAW = std::pow(1.+std::sqrt(mu[NAPLIdx]/muAW)*std::pow(MAW/M[NAPLIdx],0.25),2) - * / std::sqrt(8.*(1.+M[NAPLIdx]/MAW)); + * using std::sqrt; + * using std::pow; + * Scalar phiCAW = pow(1.+sqrt(mu[NAPLIdx]/muAW)*pow(MAW/M[NAPLIdx],0.25),2) + * / sqrt(8.*(1.+M[NAPLIdx]/MAW)); */ Scalar phiAWC = phiCAW * muAW*M[NAPLIdx]/(mu[NAPLIdx]*MAW); muResult = (xAW*muAW)/(xAW+fluidState.moleFraction(gPhaseIdx, NAPLIdx)*phiAWC) - + (fluidState.moleFraction(gPhaseIdx, NAPLIdx) * mu[NAPLIdx]) - / (fluidState.moleFraction(gPhaseIdx, NAPLIdx) + xAW*phiCAW); + + (fluidState.moleFraction(gPhaseIdx, NAPLIdx) * mu[NAPLIdx]) + / (fluidState.moleFraction(gPhaseIdx, NAPLIdx) + xAW*phiCAW); return muResult; } + using Base::diffusionCoefficient; /*! * \brief Given all mole fractions, return the diffusion - * coefficent of a component in a phase. + * coefficient in \f$\mathrm{[m^2/s]}\f$ of a component in a phase. + * \param fluidState The fluid state + * \param phaseIdx The index of the phase + * \param compIdx The index of the component */ - using Base::diffusionCoefficient; template static Scalar diffusionCoefficient(const FluidState &fluidState, int phaseIdx, int compIdx) { - Scalar diffCont; - - if (phaseIdx==gPhaseIdx) { - Scalar diffAC = Dumux::BinaryCoeff::Air_Mesitylene::gasDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); - Scalar diffWC = Dumux::BinaryCoeff::H2O_Mesitylene::gasDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); - Scalar diffAW = Dumux::BinaryCoeff::H2O_Air::gasDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); - - const Scalar xga = fluidState.moleFraction(gPhaseIdx, airIdx); - const Scalar xgw = fluidState.moleFraction(gPhaseIdx, H2OIdx); - const Scalar xgc = fluidState.moleFraction(gPhaseIdx, NAPLIdx); - - if (compIdx==NAPLIdx) return (1.- xgw)/(xga/diffAW + xgc/diffWC); - else if (compIdx==H2OIdx) return (1.- xgc)/(xgw/diffWC + xga/diffAC); - else if (compIdx==airIdx) DUNE_THROW(Dune::InvalidStateException, - "Diffusivity of air in the gas phase " - "is constraint by sum of diffusive fluxes = 0 !\n"); - } else if (phaseIdx==wPhaseIdx){ - Scalar diffACl = 1.e-9; // BinaryCoeff::Air_Mesitylene::liquidDiffCoeff(temperature, pressure); - Scalar diffWCl = 1.e-9; // BinaryCoeff::H2O_Mesitylene::liquidDiffCoeff(temperature, pressure); - Scalar diffAWl = 1.e-9; // BinaryCoeff::H2O_Air::liquidDiffCoeff(temperature, pressure); - - Scalar xwa = fluidState.moleFraction(wPhaseIdx, airIdx); - Scalar xww = fluidState.moleFraction(wPhaseIdx, H2OIdx); - Scalar xwc = fluidState.moleFraction(wPhaseIdx, NAPLIdx); - - switch (compIdx) { - case NAPLIdx: - diffCont = (1.- xww)/(xwa/diffAWl + xwc/diffWCl); - return diffCont; - case airIdx: - diffCont = (1.- xwc)/(xww/diffWCl + xwa/diffACl); - return diffCont; - case H2OIdx: - DUNE_THROW(Dune::InvalidStateException, - "Diffusivity of water in the water phase " - "is constraint by sum of diffusive fluxes = 0 !\n"); + switch (phaseIdx) + { + case gPhaseIdx: + { + switch (compIdx) + { + case NAPLIdx: + { + Scalar diffWC = BinaryCoeff::H2O_Mesitylene::gasDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + Scalar diffAW = BinaryCoeff::H2O_Air::gasDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + const Scalar xga = fluidState.moleFraction(gPhaseIdx, AirIdx); + const Scalar xgw = fluidState.moleFraction(gPhaseIdx, H2OIdx); + const Scalar xgc = fluidState.moleFraction(gPhaseIdx, NAPLIdx); + return (1.- xgw)/(xga/diffAW + xgc/diffWC); + } + case H2OIdx: + { + Scalar diffAC = BinaryCoeff::Air_Mesitylene::gasDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + Scalar diffWC = BinaryCoeff::H2O_Mesitylene::gasDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + const Scalar xga = fluidState.moleFraction(gPhaseIdx, AirIdx); + const Scalar xgw = fluidState.moleFraction(gPhaseIdx, H2OIdx); + const Scalar xgc = fluidState.moleFraction(gPhaseIdx, NAPLIdx); + return (1.- xgc)/(xgw/diffWC + xga/diffAC); + } + case AirIdx: + DUNE_THROW(Dune::InvalidStateException, "Diffusivity of Air in the gas phase is constraint by sum of diffusive fluxes = 0 !"); + } } - } else if (phaseIdx==nPhaseIdx) { - - DUNE_THROW(Dune::InvalidStateException, - "Diffusion coefficients of " - "substances in liquid phase are undefined!\n"); + case wPhaseIdx: + { + Scalar diffACl = BinaryCoeff::Air_Mesitylene::liquidDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + Scalar diffWCl = BinaryCoeff::H2O_Mesitylene::liquidDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + Scalar diffAWl = BinaryCoeff::H2O_Air::liquidDiffCoeff(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); + + Scalar xwa = fluidState.moleFraction(wPhaseIdx, AirIdx); + Scalar xww = fluidState.moleFraction(wPhaseIdx, H2OIdx); + Scalar xwc = fluidState.moleFraction(wPhaseIdx, NAPLIdx); + + switch (compIdx) + { + case NAPLIdx: + return (1.- xww)/(xwa/diffAWl + xwc/diffWCl); + case AirIdx: + return (1.- xwc)/(xww/diffWCl + xwa/diffACl); + case H2OIdx: + DUNE_THROW(Dune::InvalidStateException, + "Diffusivity of water in the water phase " + "is constraint by sum of diffusive fluxes = 0 !\n"); + } + } + case nPhaseIdx: + DUNE_THROW(Dune::InvalidStateException, "Diffusion coefficients of substances in non-wetting liquid phase are undefined!\n"); } return 0; } @@ -398,20 +461,23 @@ public: int compIIdx, int compJIdx) { - DUNE_THROW(Dune::NotImplemented, "FluidSystems::H2OAirNAPL::binaryDiffusionCoefficient()"); + DUNE_THROW(Dune::NotImplemented, "FluidSystems::MyH2OAirMesitylene::binaryDiffusionCoefficient()"); } + using Base::fugacityCoefficient; /*! - * \brief Returns the fugacity coefficient [-] of a component in a + * \brief Returns the fugacity coefficient \f$\mathrm{[-]}\f$ of a component in a * phase. * * In this case, things are actually pretty simple. We have an ideal * solution. Thus, the fugacity coefficient is 1 in the gas phase - * (fugacity equals the partial pressure of the component in the gas phase - * respectively in the liquid phases it is the inverse of the - * Henry coefficients scaled by pressure + * (fugacity equals the partial pressure of the component in the gas phase) + * respectively in the liquid phases it is the Henry coefficients divided + * by pressure. + * \param fluidState The fluid state + * \param phaseIdx The index of the phase + * \param compIdx The index of the component */ - using Base::fugacityCoefficient; template static Scalar fugacityCoefficient(const FluidState &fluidState, int phaseIdx, @@ -426,22 +492,22 @@ public: if (phaseIdx == wPhaseIdx) { if (compIdx == H2OIdx) return H2O::vaporPressure(T)/p; - else if (compIdx == airIdx) - return Dumux::BinaryCoeff::H2O_Air::henry(T)/p; + else if (compIdx == AirIdx) + return BinaryCoeff::H2O_Air::henry(T)/p; else if (compIdx == NAPLIdx) - return Dumux::BinaryCoeff::H2O_Mesitylene::henry(T)/p; + return BinaryCoeff::H2O_Mesitylene::henry(T)/p; } // for the NAPL phase, we assume currently that nothing is // dissolved. this means that the affinity of the NAPL // component to the NAPL phase is much higher than for the - // other components, i.e. the fugacity cofficient is much + // other components, i.e. the fugacity coefficient is much // smaller. if (phaseIdx == nPhaseIdx) { Scalar phiNapl = NAPL::vaporPressure(T)/p; if (compIdx == NAPLIdx) return phiNapl; - else if (compIdx == airIdx) + else if (compIdx == AirIdx) return 1e6*phiNapl; else if (compIdx == H2OIdx) return 1e6*phiNapl; @@ -458,18 +524,19 @@ public: const int phaseIdx, const int compIdx) { - DUNE_THROW(Dune::NotImplemented, "FluidSystems::H2OAirNAPL::kelvinVaporPressure()"); + DUNE_THROW(Dune::NotImplemented, "FluidSystems::MyH2OAirMesitylene::kelvinVaporPressure()"); } + using Base::enthalpy; /*! * \brief Given all mole fractions in a phase, return the specific - * phase enthalpy [J/kg]. - */ - /*! - * \todo This system neglects the contribution of gas-molecules in the liquid phase. + * phase enthalpy \f$\mathrm{[J/kg]}\f$. + * \param fluidState The fluid state + * \param phaseIdx The index of the phase + * + * \note This system neglects the contribution of gas-molecules in the liquid phase. * This contribution is probably not big. Somebody would have to find out the enthalpy of solution for this system. ... */ - using Base::enthalpy; template static Scalar enthalpy(const FluidState &fluidState, int phaseIdx) @@ -485,11 +552,12 @@ public: fluidState.pressure(phaseIdx)); Scalar hgw = H2O::gasEnthalpy(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); - Scalar hga = Air::gasEnthalpy(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); // pressure is only a dummy here (not dependent on pressure, just temperature) + // pressure is only a dummy here (not dependent on pressure, just temperature) + Scalar hga = Air::gasEnthalpy(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); Scalar result = 0; result += hgw * fluidState.massFraction(gPhaseIdx, H2OIdx); - result += hga * fluidState.massFraction(gPhaseIdx, airIdx); + result += hga * fluidState.massFraction(gPhaseIdx, AirIdx); result += hgc * fluidState.massFraction(gPhaseIdx, NAPLIdx); return result; @@ -498,19 +566,43 @@ public: } using Base::heatCapacity; + /*! + * \brief Return the heat capacity in \f$\mathrm{[J/(kg K)]}\f$. + * \param fluidState The fluid state + * \param phaseIdx The index of the phase + */ template static Scalar heatCapacity(const FluidState &fluidState, int phaseIdx) { - DUNE_THROW(Dune::NotImplemented, "FluidSystems::H2OAirNAPL::heatCapacity()"); + DUNE_THROW(Dune::NotImplemented, "FluidSystems::MyH2OAirMesitylene::heatCapacity()"); } using Base::thermalConductivity; + /*! + * \brief Return the thermal conductivity \f$\mathrm{[W/(m K)]}\f$. + * \param fluidState The fluid state + * \param phaseIdx The index of the phase + */ template static Scalar thermalConductivity(const FluidState &fluidState, int phaseIdx) { - DUNE_THROW(Dune::NotImplemented, "FluidSystems::H2OAirNAPL::thermalConductivity()"); + const Scalar temperature = fluidState.temperature(phaseIdx) ; + const Scalar pressure = fluidState.pressure(phaseIdx); + if (phaseIdx == wPhaseIdx) + { + return H2O::liquidThermalConductivity(temperature, pressure); + } + else if (phaseIdx == nPhaseIdx) + { + return NAPL::liquidThermalConductivity(temperature, pressure); + } + else if (phaseIdx == gPhaseIdx) + { + return Air::gasThermalConductivity(temperature, pressure); + } + DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); } private: @@ -521,65 +613,22 @@ private: // this assumes each dissolved molecule displaces exactly one // water molecule in the liquid - return - clH2O*(xww*H2O::molarMass() + xwa*Air::molarMass() + xwc*NAPL::molarMass()); + return clH2O*(xww*H2O::molarMass() + xwa*Air::molarMass() + xwc*NAPL::molarMass()); } static Scalar gasPhaseDensity_(Scalar T, Scalar pg, Scalar xgw, Scalar xga, Scalar xgc) { return H2O::gasDensity(T, pg*xgw) + Air::gasDensity(T, pg*xga) + NAPL::gasDensity(T, pg*xgc); - }; + } static Scalar NAPLPhaseDensity_(Scalar T, Scalar pn) { - return - NAPL::liquidDensity(T, pn); + return NAPL::liquidDensity(T, pn); } }; -} // end namespace FluidSystems - -#ifdef DUMUX_PROPERTIES_HH -// forward defintions of the property tags -namespace Properties { - NEW_PROP_TAG(Scalar); - NEW_PROP_TAG(Components); -} - -/*! - * \brief A threephase fluid system with water, air and mesitylene as components. - * - * This is an adapter to use Dumux::H2OAirMesityleneFluidSystem, as is - * done with most other classes in Dumux. - * This fluidsystem is applied by default with the tabulated version of - * water of the IAPWS-formulation. - * - * To change the component formulation (ie to use nontabulated or - * incompressible water), or to switch on verbosity of tabulation, - * use the property system and the property "Components": - * - * // Select desired version of the component - * SET_PROP(myApplicationProperty, Components) : public GET_PROP(TypeTag, DefaultComponents) - * { - * typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - * - * // Do not use the defaults ! - * // typedef Dumux::TabulatedComponent > H2O; - * - * // Apply e.g. untabulated water: - * typedef Dumux::H2O H2O; - * }; - * - * Also remember to initialize tabulated components (FluidSystem::init()), while this - * is not necessary for non-tabularized ones. - */ -template -class H2OAirNAPLFluidSystem -: public FluidSystems::H2OAirNAPL -{}; -#endif +} // end namespace FluidSystems } // end namespace Dumux #endif diff --git a/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.cc b/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.cc index 77f3e53fff89e030cde3aa4e1039548b40f1d222..dbc8eb92dacd1eefcd34dd8b742a391715da382a 100644 --- a/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.cc +++ b/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.cc @@ -21,9 +21,33 @@ * * \brief application, using the 3p3c box model */ -#include "config.h" +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + #include "problem.hh" -#include /*! * \brief Provides an interface for customizing error messages associated with @@ -51,8 +75,160 @@ void usage(const char *progName, const std::string &errorMsg) } } -int main(int argc, char** argv) +//////////////////////// +// the main function +//////////////////////// +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(InfiltrationThreePThreeCBoxTypeTag); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv); + + // try to create a grid (from the given grid file or the input file) + GridManager gridManager; + gridManager.init(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(fvGridGeometry->numDofs()); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam("TimeLoop.TEnd"); + const auto maxDt = getParam("TimeLoop.MaxTimeStepSize"); + auto dt = getParam("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) + restartTime = getParam("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule vtkWriter(*gridVariables, x, problem->name()); + using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput); + vtkWriter.addVelocityOutput(std::make_shared(*gridVariables)); + VtkOutputFields::init(vtkWriter); //!< Add model specific output fields + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = ILU0BiCGSTABBackend; + auto linearSolver = std::make_shared(); + + // the non-linear solver + using NewtonSolver = PriVarSwitchNewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); + + //! set some check points for the time loop + timeLoop->setPeriodicCheckPoint(getParam("TimeLoop.EpisodeLength")); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // solve the non-linear system with time step control + nonLinearSolver.solve(x, *timeLoop); + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // write vtk output on check points + if (timeLoop->isCheckPoint() || timeLoop->timeStepIndex() == 1 || timeLoop->finished()) + vtkWriter.write(timeLoop->time()); + + // set new dt as suggested by the newton solver + timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + return 0; +} // end main +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(InfiltrationProblem) ProblemTypeTag; - return Dumux::start(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.input b/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.input index ea4efe94aa3736413ede10f851b415c10ba302e3..fb9f99922e3abe1b2ec68a97b607b2e3ff160d8b 100644 --- a/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.input +++ b/lecture/mm/naplinfiltration/3p3c/naplinfiltration3p3c.input @@ -1,4 +1,4 @@ -[TimeManager] +[TimeLoop] DtInitial = 60 # [s] TEnd = 31536000 # [s] #TEnd = 315360000000 # [s] @@ -9,11 +9,18 @@ UpperRight = 500 10 # coordinates of upper right grid corner [m] Cells = 250 10 # number of cells in (x, y) direction [-] [SpatialParams] -Permeability = 1.e-11 # [m^2] -Porosity = 0.40 # [-] -VanGenuchtenAlpha = 0.0005 # [-] -VanGenuchtenN = 4.0 # [-] -MultiplierNaplDensity = 1.0 # 1.0 means Mesitylene (an LNAPL) [-] +permeability = 1.e-11 # [m^2] +porosity = 0.40 # [-] +vanGenuchtenAlpha = 0.0005 # [-] +vanGenuchtenN = 4.0 # [-] + +[Output] +PlotFluidMatrixInteractions = true + +[Problem] +Name = naplinfiltration3p3c +MultiplierNAPLDensity = 1.0 # 1.0 means Mesitylene (an LNAPL) [-] [Newton] -MaxSteps = 6 \ No newline at end of file +MaxRelativeShift = 1.0e-4 +MaxSteps = 6 diff --git a/lecture/mm/naplinfiltration/3p3c/problem.hh b/lecture/mm/naplinfiltration/3p3c/problem.hh index 6e0cffc705874de2f3a26f8d37d519615761385b..0980aface2b353aaae1d5a59743808457b0df2b5 100644 --- a/lecture/mm/naplinfiltration/3p3c/problem.hh +++ b/lecture/mm/naplinfiltration/3p3c/problem.hh @@ -18,58 +18,61 @@ *****************************************************************************/ /*! * \file - * + * \ingroup ThreePThreeCTests * \brief Isothermal NAPL infiltration problem: LNAPL contaminates * the unsaturated and the saturated groundwater zone. */ #ifndef DUMUX_NAPLINFILTRATIONPROBLEM_3P_3C_HH #define DUMUX_NAPLINFILTRATIONPROBLEM_3P_3C_HH -#include -#include +#include + +#include +#include +#include +#include +#include -#include +#include #include namespace Dumux { +/*! + * \ingroup ThreePThreeCTests + * \brief Isothermal NAPL infiltration problem: LNAPL contaminates + * the unsaturated and the saturated groundwater zone. + */ template -class InfiltrationProblem; +class InfiltrationThreePThreeCProblem; -namespace Properties -{ -NEW_TYPE_TAG(InfiltrationProblem, INHERITS_FROM(BoxThreePThreeC, InfiltrationSpatialParams)); +namespace Properties { +NEW_TYPE_TAG(InfiltrationThreePThreeCTypeTag, INHERITS_FROM(ThreePThreeC)); +NEW_TYPE_TAG(InfiltrationThreePThreeCBoxTypeTag, INHERITS_FROM(BoxModel, InfiltrationThreePThreeCTypeTag)); // Set the grid type -SET_TYPE_PROP(InfiltrationProblem, Grid, Dune::YaspGrid<2>); +SET_TYPE_PROP(InfiltrationThreePThreeCTypeTag, Grid, Dune::YaspGrid<2>); // Set the problem property -SET_TYPE_PROP(InfiltrationProblem, Problem, InfiltrationProblem); +SET_TYPE_PROP(InfiltrationThreePThreeCTypeTag, Problem, InfiltrationThreePThreeCProblem); + +// Set the spatial parameters +SET_PROP(InfiltrationThreePThreeCTypeTag, SpatialParams) +{ + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = InfiltrationSpatialParams; +}; // Set the fluid system -SET_TYPE_PROP(InfiltrationProblem, +SET_TYPE_PROP(InfiltrationThreePThreeCTypeTag, FluidSystem, - FluidSystems::H2OAirNAPL); - -// Enable gravity? -SET_BOOL_PROP(InfiltrationProblem, ProblemEnableGravity, true); - -// Write newton convergence? -SET_BOOL_PROP(InfiltrationProblem, NewtonWriteConvergence, false); - -// Maximum tolerated relative error in the Newton method -SET_SCALAR_PROP(InfiltrationProblem, NewtonMaxRelativeShift, 1e-4); - -// -1 backward differences, 0: central differences, +1: forward differences -SET_INT_PROP(InfiltrationProblem, ImplicitNumericDifferenceMethod, 1); - -// Set the linear solver -SET_TYPE_PROP(InfiltrationProblem, LinearSolver, ILU0BiCGSTABBackend); + FluidSystems::MyH2OAirMesitylene); } /*! - * \ingroup ThreePThreeCBoxModel - * \ingroup BoxTestProblems + * \ingroup ThreePThreeCModel + * \ingroup ImplicitTestProblems * \brief Isothermal NAPL infiltration problem: LNAPL contaminates * the unsaturated and the saturated groundwater zone. * @@ -88,55 +91,50 @@ SET_TYPE_PROP(InfiltrationProblem, LinearSolver, ILU0BiCGSTABBackend); * Top and bottom are Neumann boundaries, all no-flow except for the small * infiltration zone in the upper left part. * - * This problem uses the \ref ThreePThreeCModel or the \ref ThreePModel. + * This problem uses the \ref ThreePThreeCModel. * * This problem should typically be simulated for 30 days. * A good choice for the initial time step size is 60 s. - * To adjust the simulation time it is necessary to edit the file test_naplfiltrationexercise.input + * To adjust the simulation time it is necessary to edit the file test_box3p3c.input + * or test_cc3p3c.input. * * To run the simulation execute the following line in shell: - * ./test_naplfiltrationexercise -ParameterFile test_naplfiltrationexercise.input + * ./test_box3p3c test_box3p3c.input or + * ./test_cc3p3c test_cc3p3c.input * */ template -class InfiltrationProblem : public ImplicitPorousMediaProblem +class InfiltrationThreePThreeCProblem : public PorousMediumFlowProblem { - typedef ImplicitPorousMediaProblem ParentType; + using ParentType = PorousMediumFlowProblem; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Indices = typename GET_PROP_TYPE(TypeTag, ModelTraits)::Indices; + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); // copy some indices for convenience - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; enum { pressureIdx = Indices::pressureIdx, - switch1Idx = Indices::switch1Idx, switch2Idx = Indices::switch2Idx, + + // phase state wgPhaseOnly = Indices::wgPhaseOnly, - contiWEqIdx = Indices::contiWEqIdx, - contiNEqIdx = Indices::contiNEqIdx, - contiGEqIdx = Indices::contiGEqIdx, + //!< Index of the mass conservation equation for the contaminant component + contiNEqIdx = Indices::conti0EqIdx + FluidSystem::nCompIdx, - // Grid and world dimension - dim = GridView::dimension, + // world dimension dimWorld = GridView::dimensionworld }; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); - 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 GridView::template Codim<0>::Entity Element; - typedef typename GridView::template Codim::Entity Vertex; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - 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 Dune::FieldVector GlobalPosition; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; public: /*! @@ -145,49 +143,33 @@ public: * \param timeManager The time manager * \param gridView The grid view */ - InfiltrationProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView), eps_(1e-5) + InfiltrationThreePThreeCProblem(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { temperature_ = 273.15 + 10.0; // -> 10 degrees Celsius + FluidSystem::init(282.15, 284.15, 3, 8e4, 3e5, 200); - episodeLength_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, EpisodeLength); - this->timeManager().startNextEpisode(episodeLength_); - - FluidSystem::init(/*tempMin=*/temperature_ - 1, - /*tempMax=*/temperature_ + 1, - /*nTemp=*/3, - /*pressMin=*/0.8*1e5, - /*pressMax=*/3*1e5, - /*nPress=*/200); + name_ = getParam("Problem.Name"); } - /*! - * \name Problem parameters - */ - // \{ - /*! * \brief The problem name. * * This is used as a prefix for files generated by the simulation. */ - std::string name() const - { return "infiltration3p3c"; } + const std::string& name() const + { return name_; } /*! * \brief Returns the temperature within the domain. * + * \param globalPos The position + * * This problem assumes a temperature of 10 degrees Celsius. */ - Scalar temperature() const + Scalar temperatureAtPos(const GlobalPosition &globalPos) const { return temperature_; - }; - - void sourceAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - values = 0; } // \} @@ -201,66 +183,55 @@ public: * \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 globalPos The position of the finite volume in global coordinates + * \param globalPos The position for which the bc type should be evaluated */ - void boundaryTypesAtPos(BoundaryTypes &values, - const GlobalPosition &globalPos) const + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { - if(globalPos[0] > this->bBoxMax()[0] - eps_) - values.setAllDirichlet(); - else if(globalPos[0] < this->bBoxMin()[0] + eps_) + BoundaryTypes values; + + if(globalPos[0] > this->fvGridGeometry().bBoxMax()[0] - eps_ + || globalPos[0] < this->fvGridGeometry().bBoxMin()[0] + eps_) values.setAllDirichlet(); else values.setAllNeumann(); + + return values; } /*! * \brief Evaluate the boundary conditions for a dirichlet - * control volume. + * boundary segment. * - * \param values The dirichlet values for the primary variables - * \param globalPos The position of the center of the finite volume - * for which the dirichlet condition ought to be - * set in global coordinates + * \param globalPos The position for which the bc type should be evaluated * * For this method, the \a values parameter stores primary variables. */ - void dirichletAtPos(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - initial_(values, globalPos); - } + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const + { return initial_(globalPos); } /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * \param values The neumann values for the conservation equations in units of - * \f$ [ \textnormal{unit of conserved quantity} / (m^2 \cdot s )] \f$ - * \param globalPos The position of the boundary face's integration point in global coordinates + * \param globalPos The position of the integration point of the boundary segment. * * 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 &globalPos) const + NumEqVector neumannAtPos(const GlobalPosition &globalPos) const { - values = 0; + NumEqVector values(0.0); // negative values for injection - if (this->timeManager().time() < 2592000) + if ((globalPos[0] < 175.0 + eps_) && (globalPos[0] > 150.0 - eps_) + && (globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_)) { - if (globalPos[0] < 175.0 + eps_ - && globalPos[0] > 150.0 - eps_ - && globalPos[1] > this->bBoxMax()[1] - eps_) // upper boundary - { - values[contiWEqIdx] = 0.0; - // mole flux conversion via M(mesit.) = 0,120 kg/mol --> 1.2e-4 kg/(s*m) - values[contiNEqIdx] = -0.001; // 3p3c needs a mole fraction - values[contiGEqIdx] = 0.0; - } + //mole flow conversion to mass flow with molar mass M(Mesit.)=0,120 kg/mol --> 1.2e-4 kg/(sm) + //the 3p3c model uses mole fractions + values[contiNEqIdx] = -0.001; } + + return values; } // \} @@ -273,89 +244,70 @@ public: /*! * \brief Evaluate the initial value for a control volume. * - * \param values The dirichlet values for the primary variables - * \param globalPos 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 &globalPos) const - { - initial_(values, globalPos); - } - - /*! - * \brief Return the initial phase state inside a control volume. + * \param globalPos The position for which the initial condition should be evaluated * - * \param globalPos The global position + * For this method, the \a values parameter stores primary + * variables. */ - int initialPhasePresenceAtPos(const GlobalPosition &globalPos) const - { - return Indices::wgPhaseOnly; - } + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const + { return initial_(globalPos); } - bool shouldWriteOutput() const +private: + // internal method for the initial condition + PrimaryVariables initial_(const GlobalPosition &globalPos) const { - return this->timeManager().timeStepIndex() == 0 || - this->timeManager().episodeWillBeFinished() || - this->timeManager().willBeFinished(); - } + PrimaryVariables values(0.0); + values.setState(wgPhaseOnly); - void episodeEnd() - { - this->timeManager().startNextEpisode(episodeLength_); - } + Scalar y = globalPos[1]; + Scalar x = globalPos[0]; -private: - // internal method for the initial condition (reused for the - // dirichlet conditions!) - void initial_(PrimaryVariables &values, - const GlobalPosition &globalPos) const - { - const auto& materialLawParams = this->spatialParams().materialLawParams(); - const Scalar swr = materialLawParams.swr(); - const Scalar sgr = materialLawParams.sgr(); + const auto& materialLawParams = this->spatialParams().materialLawParamsAtPos(globalPos); + Scalar swr = materialLawParams.swr(); + Scalar sgr = materialLawParams.sgr(); - if(globalPos[1] >= -1e-3*globalPos[0] + 5) + if(y >(-1.E-3*x+5) - eps_) { - const Scalar pc = std::max(0.0, 9.81*1000.0*(globalPos[1] + 5e-4*globalPos[0] - 5)); - const Scalar sw = std::min(1.0-sgr, std::max(swr, invertPcGW_(pc, materialLawParams))); + Scalar pc = 9.81 * 1000.0 * (y - (-5E-4*x+5)); + if (pc < 0.0) pc = 0.0; - values[pressureIdx] = 1.0e5; + Scalar sw = invertPcgw_(pc, this->spatialParams().materialLawParamsAtPos(globalPos)); + if (sw < swr) sw = swr; + if (sw > 1.-sgr) sw = 1.-sgr; + + values[pressureIdx] = 1e5 ; values[switch1Idx] = sw; values[switch2Idx] = 0.0; - } - else - { - values[pressureIdx] = 1.0e5 + 9.81*1000.0*(5 - 5.0e-4*globalPos[0] - globalPos[1]); - values[switch1Idx] = 1.0 - sgr; + }else { + values[pressureIdx] = 1e5 + 9.81 * 1000.0 * ((-5E-4*x+5) - y); + values[switch1Idx] = 1.-sgr; values[switch2Idx] = 0.0; } + return values; } - static Scalar invertPcGW_(const Scalar pcIn, - const MaterialLawParams &pcParams) + template + static Scalar invertPcgw_(Scalar pcIn, const MaterialLawParams &pcParams) { + using MaterialLaw = typename ParentType::SpatialParams::MaterialLaw; Scalar lower(0.0); Scalar upper(1.0); - const unsigned int maxIterations = 25; + const unsigned int maxIter = 25; const Scalar bisLimit = 1.0; - Scalar sw, pcGW; - for (unsigned int k = 1; k <= maxIterations; k++) + Scalar sw, pcgw; + for (unsigned int k = 1; k <= maxIter; k++) { sw = 0.5*(upper + lower); - pcGW = MaterialLaw::pcgw(pcParams, sw); - const Scalar delta = std::abs(pcGW - pcIn); + pcgw = MaterialLaw::pcgw(pcParams, sw); + const Scalar delta = std::abs(pcgw - pcIn); if (delta < bisLimit) return sw; - if (k == maxIterations) + if (k == maxIter) return sw; - if (pcGW > pcIn) + if (pcgw > pcIn) lower = sw; else upper = sw; @@ -364,9 +316,10 @@ private: } Scalar temperature_; - const Scalar eps_; - Scalar episodeLength_; + static constexpr Scalar eps_ = 1e-6; + std::string name_; }; -} //end namespace + +} // end namespace Dumux #endif diff --git a/lecture/mm/naplinfiltration/description/naplinfiltrationexercise.tex b/lecture/mm/naplinfiltration/description/naplinfiltrationexercise.tex index e93b3aa30cc394ba6602f458093c9b83632768c8..aef9e9ae570cf3090a62ae6f6069d300f912f248 100644 --- a/lecture/mm/naplinfiltration/description/naplinfiltrationexercise.tex +++ b/lecture/mm/naplinfiltration/description/naplinfiltrationexercise.tex @@ -45,7 +45,6 @@ \setcounter{secnumdepth}{4} % allows numbers for sub-sub-section \input{problemdescription-infiltration} -\input{paraview-infiltration} %\cleardoublepage \printindex diff --git a/lecture/mm/naplinfiltration/description/paraview-files/infiltration-macro.py b/lecture/mm/naplinfiltration/description/paraview-files/infiltration-macro.py deleted file mode 100644 index 4fe787c1eb8c304a8fcb153c2987b1cb03256984..0000000000000000000000000000000000000000 --- a/lecture/mm/naplinfiltration/description/paraview-files/infiltration-macro.py +++ /dev/null @@ -1,119 +0,0 @@ -try: paraview.simple -except: from paraview.simple import * -paraview.simple._DisableFirstRenderCameraReset() - -infiltration_pvd = GetActiveSource() -RenderView1 = GetRenderView() -a1_Sw_PVLookupTable = GetLookupTableForArray( "Sw", 1, NanColor=[0.2470588235294118, 0.0, 0.0], RGBPoints=[0.11999999731779099, 0.2313725490196079, 0.2980392156862745, 0.7529411764705882, 0.9700000286102295, 0.7058823529411765, 0.01568627450980392, 0.1490196078431373], VectorMode='Magnitude', ColorSpace='Diverging', ScalarRangeInitialized=1.0 ) - -a1_Sw_PiecewiseFunction = CreatePiecewiseFunction() - -DataRepresentation1 = Show() -DataRepresentation1.EdgeColor = [0.0, 0.0, 0.5000076295109483] -DataRepresentation1.ScalarOpacityFunction = a1_Sw_PiecewiseFunction -DataRepresentation1.ColorArrayName = 'Sw' -DataRepresentation1.ScalarOpacityUnitDistance = 36.84768231274218 -DataRepresentation1.LookupTable = a1_Sw_PVLookupTable - -RenderView1.CenterOfRotation = [250.0, 5.0, 0.0] - -AnimationScene1 = GetAnimationScene() -RenderView1.CameraPosition = [250.0, 5.0, 966.1189921396724] -RenderView1.ViewTime = 25920000.0 -RenderView1.CameraFocalPoint = [250.0, 5.0, 0.0] -RenderView1.CameraClippingRange = [956.4578022182757, 980.6107770217675] -RenderView1.CameraParallelScale = 250.04999500099976 - -AnimationScene1.AnimationTime = 25920000.0 - -Clip1 = Clip( ClipType="Plane" ) - -a1_Sn_PVLookupTable = GetLookupTableForArray( "Sn", 1, RGBPoints=[0.0, 0.2313725490196079, 0.2980392156862745, 0.7529411764705882, 0.8, 0.7058823529411765, 0.01568627450980392, 0.1490196078431373], VectorMode='Magnitude', NanColor=[0.2470588235294118, 0.0, 0.0], NumberOfTableValues=16, ColorSpace='Diverging', ScalarRangeInitialized=1.0, LockScalarRange=1 ) - -a1_Sn_PiecewiseFunction = CreatePiecewiseFunction() - -ScalarBarWidgetRepresentation1 = CreateScalarBar( Title='Sn', LabelFontSize=12, Enabled=1, LookupTable=a1_Sn_PVLookupTable, TitleFontSize=12 ) -GetRenderView().Representations.append(ScalarBarWidgetRepresentation1) - -DataRepresentation1.Scale = [1.0, 5.0, 1.0] -DataRepresentation1.ScalarOpacityFunction = a1_Sn_PiecewiseFunction -DataRepresentation1.ColorArrayName = 'Sn' -DataRepresentation1.CubeAxesYAxisMinorTickVisibility = 0 -DataRepresentation1.LookupTable = a1_Sn_PVLookupTable -DataRepresentation1.CubeAxesVisibility = 1 -DataRepresentation1.CustomBounds = [0.0, 500.0, 0.0, 10.0, 0.0, 0.0] - -Clip1.Scalars = ['CELLS', 'pg'] -Clip1.ClipType.Origin = [250.0, 5.0, 0.0] -Clip1.ClipType = "Plane" - -DataRepresentation2 = Show() -DataRepresentation2.EdgeColor = [0.0, 0.0, 0.5000076295109483] -DataRepresentation2.Scale = [1.0, 5.0, 1.0] -DataRepresentation2.ColorAttributeType = 'CELL_DATA' -DataRepresentation2.ScalarOpacityFunction = a1_Sw_PiecewiseFunction -DataRepresentation2.ColorArrayName = 'Sw' -DataRepresentation2.ScalarOpacityUnitDistance = 19.95867601365872 -DataRepresentation2.CubeAxesYAxisMinorTickVisibility = 0 -DataRepresentation2.CubeAxesVisibility = 1 -DataRepresentation2.CustomBounds = [0.0, 500.0, 0.0, 10.0, 0.0, 0.0] -DataRepresentation2.LookupTable = a1_Sw_PVLookupTable - -RenderView1.CameraClippingRange = [949.7452448857599, 989.0605037142057] - -DataRepresentation1.Visibility = 0 - -a1_Sw_PVLookupTable.RGBPoints = [0.11999949812889099, 0.2313725490196079, 0.2980392156862745, 0.7529411764705882, 0.9730672836303711, 0.7058823529411765, 0.01568627450980392, 0.1490196078431373] - -Clip1.ClipType.Origin = [200.0, 0.0, 0.0] -Clip1.ClipType.Normal = [-1.0, 0.0, 0.0] - -RenderView1.CameraClippingRange = [956.4578022182757, 980.6107770217675] - -ScalarBarWidgetRepresentation1.Enabled = 0 -ScalarBarWidgetRepresentation1.Visibility = 0 - -Clip1.Scalars = ['POINTS', 'pg'] - -AnnotateTime1 = AnnotateTime() - -RenderView1.CameraPosition = [100.0, 25.0, 398.2614208301845] -RenderView1.CameraClippingRange = [394.2788066218826, 404.23534214263725] -RenderView1.CameraFocalPoint = [100.0, 25.0, 0.0] -RenderView1.CenterAxesVisibility = 0 -RenderView1.CameraParallelScale = 103.07764064044152 -RenderView1.CenterOfRotation = [100.0, 25.0, 0.0] - -ScalarBarWidgetRepresentation1.Enabled = 1 -ScalarBarWidgetRepresentation1.Visibility = 1 - -DataRepresentation2.ScalarOpacityFunction = a1_Sn_PiecewiseFunction -DataRepresentation2.ColorArrayName = 'Sn' -DataRepresentation2.LookupTable = a1_Sn_PVLookupTable -DataRepresentation2.ColorAttributeType = 'POINT_DATA' - -AnnotateTime1.Format = 'Time: %8.0f, y exaggerated by 5' - -DataRepresentation3 = Show() - -WriteImage('/temp/fetzer/results/aem-dumux/infiltration/infiltration.jpg') - - -RenderView1.Background = [0.3199969481956206, 0.3400015259021897, 0.4299992370489052] -RenderView1.OrientationAxesLabelColor = [1.0, 1.0, 1.0] - -DataRepresentation1.CubeAxesColor = [1.0, 1.0, 1.0] -DataRepresentation1.EdgeColor = [0.0, 0.0, 0.5000076295109483] -DataRepresentation1.AmbientColor = [1.0, 1.0, 1.0] - -ScalarBarWidgetRepresentation1.TitleColor = [1.0, 1.0, 1.0] -ScalarBarWidgetRepresentation1.LabelColor = [1.0, 1.0, 1.0] - -DataRepresentation2.CubeAxesColor = [1.0, 1.0, 1.0] -DataRepresentation2.EdgeColor = [0.0, 0.0, 0.5000076295109483] -DataRepresentation2.AmbientColor = [1.0, 1.0, 1.0] - -DataRepresentation3.Color = [1.0, 1.0, 1.0] -DataRepresentation3.FontFamily = 'Courier' - -Render() diff --git a/lecture/mm/naplinfiltration/description/paraview-infiltration.tex b/lecture/mm/naplinfiltration/description/paraview-infiltration.tex deleted file mode 100644 index c69e84d7631a9cd1570275d140193d5f2d78ea26..0000000000000000000000000000000000000000 --- a/lecture/mm/naplinfiltration/description/paraview-infiltration.tex +++ /dev/null @@ -1,48 +0,0 @@ -\section{Visualization} -\subsection{Instructions} -To visualize the simulation results, we use {\sc paraview}. There are several ways to achieve the same result with this software. Once you used to {\sc paraview}, you can discover your own way to produce nice illustrations. -\begin{enumerate} - \item Start {\sc paraview} from the commandline by typing {\small paraview \&}. - \item Open your results with {\small File / Open}. Navigate to your simulation folder and open the {\small .pvd}-file named in this case {\small infiltration.pvd}. - \item Now you can see the file on the left in the {\small Pipeline-Browser}. Your simulation results will be shown after clicking the {\small Apply}-Button in the {\small Object Inspector} below.\\ - \underline{Hint:} If the {\small Pipeline-Browser} or the {\it Object Inspector} should disappear, you can reanimate them by a mark in the {\small View}-tab. - \item You can switch between different time steps using the buttons \newline \includegraphics[scale=0.5]{eps/frameNavigation.eps}. You can also run a little animation with the button \includegraphics[scale=0.5]{eps/play.eps}. - \item Different variables can be displayed, e.g. the non-wetting phase saturation {\small Sn} by selecting from the dropdown list \includegraphics[scale=0.5]{eps/variable.eps}. - \item You can pan around by {\small Mouse Middle Click} and zoom by {\small Mouse Right click}. It is also possible to zoom to a specified area by using \includegraphics[scale=0.5]{eps/zoom.eps}.\\ - \underline{Hint:} Before getting lost in space use the \includegraphics[scale=0.5]{eps/reset.eps} button to reset the view and the \includegraphics[scale=0.5]{eps/orientation-z.eps} button to reset the rotation. - \item Reset the view by pressing these two buttons. We will now add a legend and adapt it to the data range. Add the legend with the \includegraphics[scale=0.5]{eps/legend.eps} button. Make sure that you are displaying the last time step by clicking \includegraphics[scale=0.5]{eps/lastTimeStep.eps}. This is important for rescaling the data range. You can automatically fit it using the rescale button: \includegraphics[scale=0.5]{eps/rescale.eps}. - \item Now we do the fine tuning. Activate the {\small Display}-tab in the {\small Object-Inspector} and click the {\small Edit Color Map...}-button in the {\small Color}-field. Many options can be set there, but they shall not be discussed right now. Unset {\small Automatically Rescale to Fit Data Range} and {\small Rescale Range} to a minimum of 0 and a maximum of 0.8. Using many discrete colors will lead to a smooth but fuzzy output. Set the discrete colors to a value of 16, so changes in the saturation will become more obvious. - \item Activate the axes by setting the cross at {\small Show cube axes} and deactivate the minor ticks for the y-axis with {\small Edit} in the {\small Annotation}-tab in the {\small Object Inspector}. - \item Because the modelled domain extends further in the x-direction than in the y-direction, we re-scale now the y-axis. This can be done by manipulating the values in the {\small Transformation}-field. As the columns correspond to the different axes, the second column equals the y-axis, which should be scaled e.g. by a factor of 5.\\ - \underline{Hint:} Maybe you noticed, that the values of the y-axis did also change. I don't know how to avoid this. - \item As the region of interest is much smaller than the model domain (we are interested mainly in the region where the infiltration occurs), we cut off a part of the output. Use {\small Filters / Common / Clip} for this. Go to {\small Properties} and set the {\small origin} to (200,0,0) and the {\small normal} to (-1,0,0) which causes only the region (x-normal=-1) left of point (200,0,0) to be shown. Click {\small Apply}. Now you can also hide the plane ({\small Show Plane}). - \item Select {\small Sn} again, make the legend visible, and click {\small Zoom to Data} in the {\small Display}-tab. - \item Disable the rotation center by clicking \includegraphics[scale=0.5]{eps/rotationCenter.eps}. - \item As a last correction we comment the result by {\small Sources / Annotate Time}. Insert {\small Time: \%8.0f, y exaggerated by 5}. You can also add some notes concerning changed input parameters, filename, etc. The {\small \%8.0f} oppresses decimal values and provides a length of 8 characters for the number (c-formatting style). Use {\small Courier} as the activated font in tab {\small Display}, so that the length of the characters is fixed. - \item Now it is time to export a screenshot by {\small File / Save Screenshot}, in the field {\small Override Color Palette} choose {\small Print}. Click {\small Ok}. Now specify a filename (e.g. infiltration) and change {\small Files of type} to jpg. - \item Reset the old color palette by {\small Edit / Settings ... / Colors}. Go to {\small Choose Palette} select {\small Screen} and then click {\small Ok}. - \item Finally, you can also save the current state. This means that the opened file, used filter and sources, perspectives, etc. will be saved. By saving the state {\small File / Save State} the current settings can be reloaded without repeating all steps, as described above.\\ - \underline{Hint:} Opening the state again by using {\small File / Load State}, you can choose the {\small pvd}-file you want to load, and it doesn't matter which one you choose. If you choose another simulation run, the same operations will be applied. - \item Finished for today, hope you have fun with this ... -\end{enumerate} - -\subsection{Macros} -A different way to achieve this result is recording and playing a macro. The python script {\small infiltration-macro.py} has already been recorded. To play this macro follow these instructions. -\begin{enumerate} - \item Repeat steps 1 and 2. - \item {\small Macros / Add new Macro} and then selecting the pre-mentioned file. - \item Play the macro via {\small Macros / infiltration-macro}.\\ - \underline{Hint 2:} Be careful, as macros also overwrite existing files (jpg, avi, pvsm, ...) without telling you. -\end{enumerate} -%Regardless of the way you have chosen, you should end up with a screenshot looking like the following one:\\ - -\subsection{Results} - -\begin{figure}[h!] - \centering - \includegraphics[width=14.0cm]{eps/infiltrationResult.eps} - \caption{NAPL Saturation at the last time step.} - \label{infiltrationResult} -\end{figure} - - diff --git a/lecture/mm/naplinfiltration/description/problemdescription-infiltration.tex b/lecture/mm/naplinfiltration/description/problemdescription-infiltration.tex index a1affb32e79c775d8d1721e75bb0db11adff31e8..75700bd783d7126f7a1f7f19800560306c41743f 100644 --- a/lecture/mm/naplinfiltration/description/problemdescription-infiltration.tex +++ b/lecture/mm/naplinfiltration/description/problemdescription-infiltration.tex @@ -36,3 +36,18 @@ magnitude of the capillary pressure via the \textit{van Genuchten} parameter $\alpha$. In addition to that one might choose a DNAPL and see the different behavior in contrast to an LNAPL (default choice). \\ +\section{Exercises} + +\begin{itemize} +\item Compile and run the 3p and 3p3c problems. Visualize the results with paraview and compare them. + Have a look in particular on the NAPL saturations in the 3p compared with the 3p3c. + Then look in the 3p3c at the NAPL saturations in comparison with the NAPL mole fractions + in the gas and in the liquid phase. Try to understand their different extensions. +\item Next, we want to investigate the influence of some parameters. In the input files + you can modify permeability, porosity, capillary pressure parameters, NAPL density + and NAPL viscosity. \\ + Check out the influence of those parameters by modifying them in \underline{reasonable} + ranges. E.g. permeabilities can be changed by one order of magnitude, while density should + not vary too much. To model a 'DNAPL' you need, of course, a density larger than that of + water. Try to get that. +\end{itemize} diff --git a/lecture/mm/naplinfiltration/spatialparams.hh b/lecture/mm/naplinfiltration/spatialparams.hh index 505d395263b4d638fece7756085457f73f774130..4767318af5a2252df5df94a3b86d00e47278cbbd 100644 --- a/lecture/mm/naplinfiltration/spatialparams.hh +++ b/lecture/mm/naplinfiltration/spatialparams.hh @@ -18,243 +18,162 @@ *****************************************************************************/ /*! * \file - * - * \brief Definition of the spatial parameters for the kuevette problem. + * \ingroup ThreePTests + * \brief Definition of the spatial parameters for the kuevette problem, which + * uses the three-phase fully implicit model. */ #ifndef DUMUX_NAPLINFILTRATION_SPATIAL_PARAMS_HH #define DUMUX_NAPLINFILTRATION_SPATIAL_PARAMS_HH -#include -#include +#include +#include #include #include #include +#include +#include -namespace Dumux -{ - -//forward declaration -template -class InfiltrationSpatialParams; - -namespace Properties -{ -// The spatial parameters TypeTag -NEW_TYPE_TAG(InfiltrationSpatialParams); - -// Set the spatial parameters -SET_TYPE_PROP(InfiltrationSpatialParams, SpatialParams, Dumux::InfiltrationSpatialParams); - -// Set the material Law -SET_PROP(InfiltrationSpatialParams, MaterialLaw) -{ - private: - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef RegularizedParkerVanGen3P EffectiveLaw; - public: - // define the material law parameterized by absolute saturations - typedef EffToAbsLaw type; -}; -} - +namespace Dumux { /*! - * \ingroup ThreePThreeCModel - * * \brief Definition of the spatial parameters for the infiltration problem */ -template -class InfiltrationSpatialParams : public ImplicitSpatialParams +template +class InfiltrationSpatialParams +: public FVSpatialParams> { - typedef ImplicitSpatialParams ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - typedef typename Grid::ctype CoordScalar; - - enum { - dim=GridView::dimension, - dimWorld=GridView::dimensionworld, + using GridView = typename FVGridGeometry::GridView; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using Element = typename GridView::template Codim<0>::Entity; + using ParentType = FVSpatialParams>; - wPhaseIdx = Indices::wPhaseIdx, - nPhaseIdx = Indices::nPhaseIdx - }; - - typedef Dune::FieldVector GlobalPosition; - typedef Dune::FieldVector Vector; - - 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, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; + using GlobalPosition = typename SubControlVolume::GlobalPosition; + using EffectiveLaw = RegularizedParkerVanGen3P; public: + // export permeability type + using PermeabilityType = Scalar; + //get the material law from the property system - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename MaterialLaw::Params MaterialLawParams; + using MaterialLaw = EffToAbsLaw; + using MaterialLawParams = typename MaterialLaw::Params; /*! * \brief The constructor * - * \param gv The grid view + * \param gridView The grid view */ - InfiltrationSpatialParams(const GridView &gv) - : ParentType(gv), eps_(1.0e-7) + InfiltrationSpatialParams(std::shared_ptr fvGridGeometry) + : ParentType(fvGridGeometry) { // intrinsic permeabilities - permeability_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, Permeability); - - // porosity - porosity_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, Porosity); + permeability_ = getParam("SpatialParams.permeability"); + // porosities + porosity_ = getParam("SpatialParams.porosity"); + vGAlpha_ = getParam("SpatialParams.vanGenuchtenAlpha"); + vGN_ = getParam("SpatialParams.vanGenuchtenN"); // residual saturations - MaterialParams_.setSwr(0.12); - MaterialParams_.setSwrx(0.12); - MaterialParams_.setSnr(0.07); - MaterialParams_.setSgr(0.03); + materialParams_.setSwr(0.12); + materialParams_.setSnr(0.07); + materialParams_.setSgr(0.03); + + // parameters for the scaling of capillary pressure (GW = 1); + materialParams_.setBetaNw(1.83); + materialParams_.setBetaGn(2.2); + materialParams_.setBetaGw(1.0); // parameters for the 3phase van Genuchten law - MaterialParams_.setVgAlpha(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, VanGenuchtenAlpha)); - MaterialParams_.setVgn(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, VanGenuchtenN)); - MaterialParams_.setKrRegardsSnr(false); + materialParams_.setVgAlpha(vGAlpha_); + materialParams_.setVgn(vGN_); + materialParams_.setKrRegardsSnr(false); // parameters for adsorption - MaterialParams_.setKdNAPL(0.0); - MaterialParams_.setRhoBulk(1500.0); - } + materialParams_.setKdNAPL(0.); + materialParams_.setRhoBulk(1500.); - ~InfiltrationSpatialParams() - {} - - /*! - * \brief Apply the intrinsic permeability tensor to a pressure - * potential gradient. - * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume - */ - const Scalar intrinsicPermeability(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return permeability_; + plotFluidMatrixInteractions_ = getParam("Output.PlotFluidMatrixInteractions"); } /*! - * \brief Define the porosity \f$[-]\f$ of the spatial parameters - * - * \param element The finite element - * \param fvElemGeom The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the porosity needs to be defined + * \brief This is called from the problem and creates a gnuplot output + * of e.g the pc-Sw curve */ - const Scalar porosity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + void plotMaterialLaw() { - return porosity_; - } + GnuplotInterface gnuplot(plotFluidMatrixInteractions_); + gnuplot.setOpenPlotWindow(plotFluidMatrixInteractions_); + PlotMaterialLaw plotMaterialLaw(plotFluidMatrixInteractions_); + gnuplot.resetAll(); + plotMaterialLaw.addpc(gnuplot, materialParams_); + gnuplot.plot("pc"); - /*! - * \brief return the parameter object for the material law which depends on the position - * - * \param element The current finite element - * \param fvElemGeom The current finite volume geometry of the element - * \param scvIdx The index of the sub-control volume - */ - const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const - { - return MaterialParams_; + gnuplot.resetAll(); + plotMaterialLaw.addkr(gnuplot, materialParams_); + gnuplot.plot("kr"); } - /*! - * \brief return the parameter object for the material law + /*! + * \brief Function for defining the (intrinsic) permeability \f$[m^2]\f$. + * + * \param element The element + * \param scv The sub control volume + * \param elemSol The element solution vector + * \return the intrinsic permeability */ - const MaterialLawParams& materialLawParams() const + template + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const { - return MaterialParams_; + return permeability_; } /*! - * \brief Returns the heat capacity \f$[J/m^3 K]\f$ of the rock matrix. - * - * This is only required for non-isothermal models. + * \brief Returns the porosity \f$[-]\f$ * - * \param element The finite element - * \param fvElemGeom The finite volume geometry - * \param scvIdx The local index of the sub-control volume where - * the heat capacity needs to be defined + * \param globalPos The global position */ - Scalar heatCapacity(const Element &element, - const FVElementGeometry &fvElemGeom, - int scvIdx) const + Scalar porosityAtPos(const GlobalPosition& globalPos) const { - return 850.0 // specific heat capacity [J / (kg K)] - * 2650.0 // density of sand [kg/m^3] - * (1.0 - porosity(element, fvElemGeom, scvIdx)); + return porosity_; } /*! - * \brief Calculate the heat flux \f$[W/m^2]\f$ through the - * rock matrix based on the temperature gradient \f$[K / m]\f$ - * - * This is only required for non-isothermal models. + * \brief Returns the parameter object for the Brooks-Corey material law * - * \param heatFlux The resulting heat flux vector - * \param fluxDat The flux variables - * \param vDat The volume variables - * \param tempGrad The temperature gradient - * \param element The current finite element - * \param fvElemGeom The finite volume geometry of the current element - * \param scvfIdx The local index of the sub-control volume face where - * the matrix heat flux should be calculated + * \param globalPos The global position */ - void matrixHeatFlux(Vector &heatFlux, - const FluxVariables &fluxDat, - const ElementVolumeVariables &vDat, - const Vector &tempGrad, - const Element &element, - const FVElementGeometry &fvElemGeom, - int scvfIdx) const + const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const { - static const Scalar ldry = 0.35; - static const Scalar lSw1 = 1.8; - static const Scalar lSn1 = 0.65; + return materialParams_; + } +private: +/* + bool isFineMaterial_(const GlobalPosition &globalPos) const + { return + 70. - eps_ <= globalPos[0] && globalPos[0] <= 85. + eps_ && + 7.0 - eps_ <= globalPos[1] && globalPos[1] <= 7.50 + eps_; + } +*/ - // arithmetic mean of the liquid saturation and the porosity - const int i = fvElemGeom.subContVolFace[scvfIdx].i; - const int j = fvElemGeom.subContVolFace[scvfIdx].j; - Scalar sw = std::max(0.0, (vDat[i].saturation(wPhaseIdx) + - vDat[j].saturation(wPhaseIdx)) / 2); - Scalar sn = std::max(0.0, (vDat[i].saturation(nPhaseIdx) + - vDat[j].saturation(nPhaseIdx)) / 2); + Scalar permeability_; - // the heat conductivity of the matrix. in general this is a - // tensorial value, but we assume isotropic heat conductivity. - Scalar heatCond = ldry + std::sqrt(sw) * (lSw1-ldry) + std::sqrt(sn) * (lSn1-ldry); + Scalar porosity_; + Scalar vGN_; + Scalar vGAlpha_; - // the matrix heat flux is the negative temperature gradient - // times the heat conductivity. - heatFlux = tempGrad; - heatFlux *= -heatCond; - } + MaterialLawParams materialParams_; -private: - const Scalar eps_; + bool plotFluidMatrixInteractions_; - Scalar permeability_, porosity_; - MaterialLawParams MaterialParams_; + static constexpr Scalar eps_ = 1e-6; }; -} +} // end namespace Dumux #endif diff --git a/lecture/mm/remediationscenarios/CMakeLists.txt b/lecture/mm/remediationscenarios/CMakeLists.txt index e3cd1949740f2b87f5b259f60a6f851edf7c433f..0ebca9dc599cf9272e4a8dfbddda3a4cb42842fc 100644 --- a/lecture/mm/remediationscenarios/CMakeLists.txt +++ b/lecture/mm/remediationscenarios/CMakeLists.txt @@ -1,4 +1,4 @@ -add_input_file_links() +add_input_file_links(remediationscenariosexercise.input) add_dumux_test(remediationscenariosexercise remediationscenariosexercise remediationscenariosexercise.cc python ${dumux_INCLUDE_DIRS}/bin/testing/runtest.py diff --git a/lecture/mm/remediationscenarios/description/paraview-kuevette.tex b/lecture/mm/remediationscenarios/description/paraview-kuevette.tex deleted file mode 100644 index 1ad79107f5930733e8bc160be4982bb4ebd8ae0d..0000000000000000000000000000000000000000 --- a/lecture/mm/remediationscenarios/description/paraview-kuevette.tex +++ /dev/nu