// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- // vi: set et ts=4 sw=4 sts=4: /***************************************************************************** * Copyright (C) 2012-2013 by Andreas Lauser * * * * 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 * * \copydoc Ewoms::FractureProblem */ #ifndef EWOMS_FRACTURE_PROBLEM_HH #define EWOMS_FRACTURE_PROBLEM_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ewoms { template class FractureProblem; } namespace Opm { namespace Properties { // Create a type tag for the problem NEW_TYPE_TAG(FractureProblem, INHERITS_FROM(VcfvDiscreteFracture)); // Set the GridCreator property SET_TYPE_PROP(FractureProblem, GridCreator, Ewoms::ArtGridCreator); // Set the grid type SET_TYPE_PROP( FractureProblem, Grid, Dune::ALUGrid); // Set the problem property SET_TYPE_PROP(FractureProblem, Problem, Ewoms::FractureProblem); // Set the wetting phase SET_PROP(FractureProblem, WettingPhase) { private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; public: typedef Opm::LiquidPhase > type; }; // Set the non-wetting phase SET_PROP(FractureProblem, NonwettingPhase) { private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; public: typedef Opm::LiquidPhase > type; }; // Set the material Law SET_PROP(FractureProblem, MaterialLaw) { private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef Opm::TwoPhaseMaterialTraits Traits; // define the material law which is parameterized by effective // saturations typedef Opm::RegularizedBrooksCorey EffectiveLaw; // typedef RegularizedVanGenuchten EffectiveLaw; // typedef LinearMaterial EffectiveLaw; public: typedef Opm::EffToAbsLaw type; }; // Enable the energy equation SET_BOOL_PROP(FractureProblem, EnableEnergy, true); // Set the heat conduction law SET_PROP(FractureProblem, HeatConductionLaw) { private: typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; public: // define the material law parameterized by absolute saturations typedef Opm::Somerton type; }; // Disable gravity SET_BOOL_PROP(FractureProblem, EnableGravity, false); // For this problem, we use constraints to specify the left boundary SET_BOOL_PROP(FractureProblem, EnableConstraints, true); // Set the default value for the file name of the grid SET_STRING_PROP(FractureProblem, GridFile, "grids/fracture.art"); // Set the default value for the end time SET_SCALAR_PROP(FractureProblem, EndTime, 1e6); // Set the default value for the initial time step size SET_SCALAR_PROP(FractureProblem, InitialTimeStepSize, 100); } // namespace Properties } // namespace Opm namespace Ewoms { /*! * \ingroup VcfvTestProblems * * \brief Two-phase problem which involves fractures * * The domain is initially completely saturated by the oil phase, * except for the left side, which is fully water saturated. Since the * capillary pressure in the fractures is lower than in the rock * matrix and the material is hydrophilic, water infiltrates through * the fractures and gradually pushes the oil out on the right side, * where the pressure is kept constant. */ template class FractureProblem : public GET_PROP_TYPE(TypeTag, BaseProblem) { typedef typename GET_PROP_TYPE(TypeTag, BaseProblem) ParentType; typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; typedef typename GET_PROP_TYPE(TypeTag, Constraints) Constraints; typedef typename GET_PROP_TYPE(TypeTag, EqVector) EqVector; typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; typedef typename GET_PROP_TYPE(TypeTag, BoundaryRateVector) BoundaryRateVector; typedef typename GET_PROP_TYPE(TypeTag, RateVector) RateVector; typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; typedef typename GET_PROP_TYPE(TypeTag, HeatConductionLawParams) HeatConductionLawParams; enum { // phase indices wPhaseIdx = MaterialLaw::wPhaseIdx, nPhaseIdx = MaterialLaw::nPhaseIdx, // number of phases numPhases = FluidSystem::numPhases, // Grid and world dimension dim = GridView::dimension, dimWorld = GridView::dimensionworld }; typedef Opm::ImmiscibleFluidState FluidState; typedef Dune::FieldVector GlobalPosition; typedef Dune::FieldMatrix DimMatrix; template struct FaceLayout { bool contains(Dune::GeometryType gt) { return gt.dim() == dim - 1; } }; typedef Dune::MultipleCodimMultipleGeomTypeMapper FaceMapper; typedef Ewoms::FractureMapper FractureMapper; public: /*! * \copydoc Doxygen::defaultProblemConstructor */ FractureProblem(TimeManager &timeManager) #if DUNE_VERSION_NEWER(DUNE_COMMON, 2, 3) : ParentType(timeManager, GET_PROP_TYPE(TypeTag, GridCreator)::grid().leafGridView()) #else : ParentType(timeManager, GET_PROP_TYPE(TypeTag, GridCreator)::grid().leafView()) #endif { eps_ = 3e-6; temperature_ = 273.15 + 20; // -> 20°C matrixMaterialParams_.setResidualSaturation(wPhaseIdx, 0.0); matrixMaterialParams_.setResidualSaturation(nPhaseIdx, 0.0); fractureMaterialParams_.setResidualSaturation(wPhaseIdx, 0.0); fractureMaterialParams_.setResidualSaturation(nPhaseIdx, 0.0); #if 0 // linear matrixMaterialParams_.setEntryPC(0.0); matrixMaterialParams_.setMaxPC(2000.0); fractureMaterialParams_.setEntryPC(0.0); fractureMaterialParams_.setMaxPC(1000.0); #endif #if 1 // Brooks-Corey matrixMaterialParams_.setEntryPressure(2000); matrixMaterialParams_.setLambda(2.0); matrixMaterialParams_.setThresholdSw(1e-1); fractureMaterialParams_.setEntryPressure(1000); fractureMaterialParams_.setLambda(2.0); fractureMaterialParams_.setThresholdSw(5e-2); #endif #if 0 // van Genuchten matrixMaterialParams_.setVgAlpha(0.0037); matrixMaterialParams_.setVgN(4.7); fractureMaterialParams_.setVgAlpha(0.0025); fractureMaterialParams_.setVgN(4.7); #endif matrixMaterialParams_.finalize(); fractureMaterialParams_.finalize(); matrixK_ = this->toDimMatrix_(1e-15); // m^2 fractureK_ = this->toDimMatrix_(1e5 * 1e-15); // m^2 matrixPorosity_ = 0.10; fracturePorosity_ = 0.25; fractureWidth_ = 1e-3; // [m] // parameters for the somerton law of heat conduction computeHeatCondParams_(heatCondParams_, matrixPorosity_); } /*! * \name Auxiliary methods */ //! \{ /*! * \copydoc VcfvProblem::name */ std::string name() const { std::ostringstream oss; oss << "fracture_" << this->model().name(); return oss.str(); } /*! * \brief Called directly after the time integration. */ void postTimeStep() { // Calculate storage terms EqVector storage; this->model().globalStorage(storage); // Process with rank 0 informs about the total masses of all // components inside the domain if (this->gridView().comm().rank() == 0) { std::cout << "Mass in domain: " << storage << std::endl; } } /*! * \copydoc VcfvMultiPhaseProblem::temperature */ template Scalar temperature(const Context &context, int spaceIdx, int timeIdx) const { return temperature_; } // \} /*! * \name Soil parameters */ //! \{ /*! * \copydoc VcfvMultiPhaseProblem::intrinsicPermeability */ template const DimMatrix &intrinsicPermeability(const Context &context, int spaceIdx, int timeIdx) const { return matrixK_; } /*! * \brief Intrinsic permeability of fractures. * * \copydoc Doxygen::contextParams */ template const DimMatrix &fractureIntrinsicPermeability(const Context &context, int spaceIdx, int timeIdx) const { return fractureK_; } /*! * \copydoc VcfvMultiPhaseProblem::porosity */ template Scalar porosity(const Context &context, int spaceIdx, int timeIdx) const { return matrixPorosity_; } /*! * \brief The porosity inside the fractures. * * \copydoc Doxygen::contextParams */ template Scalar fracturePorosity(const Context &context, int spaceIdx, int timeIdx) const { return fracturePorosity_; } /*! * \copydoc VcfvMultiPhaseProblem::materialLawParams */ template const MaterialLawParams &materialLawParams(const Context &context, int spaceIdx, int timeIdx) const { return matrixMaterialParams_; } /*! * \brief The parameters for the material law inside the fractures. * * \copydoc Doxygen::contextParams */ template const MaterialLawParams &fractureMaterialLawParams(const Context &context, int spaceIdx, int timeIdx) const { return fractureMaterialParams_; } /*! * \brief Returns the object representating the fracture topology. */ const FractureMapper &fractureMapper() const { return GET_PROP_TYPE(TypeTag, GridCreator)::fractureMapper(); } /*! * \brief Returns the width of the fracture. * * \todo This method should get one face index instead of two * vertex indices. This probably requires a new context * class, though. * * \param context The execution context. * \param spaceIdx1 The local index of the edge's first edge. * \param spaceIdx2 The local index of the edge's second edge. * \param timeIdx The index used by the time discretization. */ template Scalar fractureWidth(const Context &context, int spaceIdx1, int spaceIdx2, int timeIdx) const { return fractureWidth_; } /*! * \copydoc VcfvMultiPhaseProblem::heatConductionParams */ template const HeatConductionLawParams & heatConductionParams(const Context &context, int spaceIdx, int timeIdx) const { return heatCondParams_; } /*! * \copydoc VcfvMultiPhaseProblem::heatCapacitySolid * * In this case, we assume the rock-matrix to be granite. */ template Scalar heatCapacitySolid(const Context &context, int spaceIdx, int timeIdx) const { return 790 // specific heat capacity of granite [J / (kg K)] * 2700; // density of granite [kg/m^3] } // \} /*! * \name Boundary conditions */ // \{ /*! * \copydoc VcfvProblem::boundary */ template void boundary(BoundaryRateVector &values, const Context &context, int spaceIdx, int timeIdx) const { const GlobalPosition &pos = context.pos(spaceIdx, timeIdx); if (onRightBoundary_(pos)) { // on the right boundary, we impose a free-flow // (i.e. Dirichlet) condition FluidState fluidState; fluidState.setTemperature(temperature_); fluidState.setSaturation(wPhaseIdx, 0.0); fluidState.setSaturation(nPhaseIdx, 1.0 - fluidState.saturation(wPhaseIdx)); fluidState.setPressure(wPhaseIdx, 1e5); fluidState.setPressure(nPhaseIdx, fluidState.pressure(wPhaseIdx)); // set a free flow (i.e. Dirichlet) boundary values.setFreeFlow(context, spaceIdx, timeIdx, fluidState); } else // for the upper, lower and left boundaries, use a no-flow // condition (i.e. a Neumann 0 condition) values.setNoFlow(); } // \} /*! * \name Volume terms */ // \{ /*! * \copydoc VcfvProblem::constraints */ template void constraints(Constraints &constraints, const Context &context, int spaceIdx, int timeIdx) const { const GlobalPosition &pos = context.pos(spaceIdx, timeIdx); if (!onLeftBoundary_(pos)) // only impose constraints adjacent to the left boundary return; int globalIdx = context.globalSpaceIndex(spaceIdx, timeIdx); if (!fractureMapper().isFractureVertex(globalIdx)) { // do not impose constraints if the finite volume does // not contain fractures. return; } // if the current finite volume is on the left boundary // and features a fracture, specify the fracture fluid // state. FluidState fractureFluidState; fractureFluidState.setTemperature(temperature_ + 10); fractureFluidState.setSaturation(wPhaseIdx, 1.0); fractureFluidState.setSaturation(nPhaseIdx, 1.0 - fractureFluidState.saturation( wPhaseIdx)); Scalar pCFracture[numPhases]; MaterialLaw::capillaryPressures(pCFracture, fractureMaterialParams_, fractureFluidState); fractureFluidState.setPressure(wPhaseIdx, /*pressure=*/1e5); fractureFluidState.setPressure(nPhaseIdx, fractureFluidState.pressure(wPhaseIdx) + (pCFracture[nPhaseIdx] - pCFracture[wPhaseIdx])); constraints.setAllConstraint(); constraints.assignNaiveFromFracture(fractureFluidState, matrixMaterialParams_); } /*! * \copydoc VcfvProblem::initial */ template void initial(PrimaryVariables &values, const Context &context, int spaceIdx, int timeIdx) const { FluidState fluidState; fluidState.setTemperature(temperature_); fluidState.setPressure(FluidSystem::wPhaseIdx, /*pressure=*/1e5); fluidState.setPressure(nPhaseIdx, fluidState.pressure(wPhaseIdx)); fluidState.setSaturation(wPhaseIdx, 0.0); fluidState.setSaturation(nPhaseIdx, 1.0 - fluidState.saturation(wPhaseIdx)); values.assignNaive(fluidState); } /*! * \copydoc VcfvProblem::source * * For this problem, the source term of all components is 0 * everywhere. */ template void source(RateVector &rate, const Context &context, int spaceIdx, int timeIdx) const { rate = Scalar(0.0); } // \} private: bool onLeftBoundary_(const GlobalPosition &pos) const { return pos[0] < this->bboxMin()[0] + eps_; } bool onRightBoundary_(const GlobalPosition &pos) const { return pos[0] > this->bboxMax()[0] - eps_; } bool onLowerBoundary_(const GlobalPosition &pos) const { return pos[1] < this->bboxMin()[1] + eps_; } bool onUpperBoundary_(const GlobalPosition &pos) const { return pos[1] > this->bboxMax()[1] - eps_; } void computeHeatCondParams_(HeatConductionLawParams ¶ms, Scalar poro) { Scalar lambdaGranite = 2.8; // [W / (K m)] // create a Fluid state which has all phases present Opm::ImmiscibleFluidState fs; fs.setTemperature(293.15); for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { fs.setPressure(phaseIdx, 1.0135e5); } typename FluidSystem::ParameterCache paramCache; paramCache.updateAll(fs); for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { Scalar rho = FluidSystem::density(fs, paramCache, phaseIdx); fs.setDensity(phaseIdx, rho); } for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { Scalar lambdaSaturated; if (FluidSystem::isLiquid(phaseIdx)) { Scalar lambdaFluid = FluidSystem::thermalConductivity(fs, paramCache, phaseIdx); lambdaSaturated = std::pow(lambdaGranite, (1 - poro)) + std::pow(lambdaFluid, poro); } else lambdaSaturated = std::pow(lambdaGranite, (1 - poro)); params.setFullySaturatedLambda(phaseIdx, lambdaSaturated); } Scalar lambdaVac = std::pow(lambdaGranite, (1 - poro)); params.setVacuumLambda(lambdaVac); } DimMatrix matrixK_; DimMatrix fractureK_; Scalar matrixPorosity_; Scalar fracturePorosity_; Scalar fractureWidth_; MaterialLawParams fractureMaterialParams_; MaterialLawParams matrixMaterialParams_; HeatConductionLawParams heatCondParams_; Scalar temperature_; Scalar eps_; }; } // namespace Ewoms #endif // EWOMS_FRACTURE_PROBLEM_HH