// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- // vi: set et ts=4 sw=4 sts=4: /* This file is part of the Open Porous Media project (OPM). OPM 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. OPM 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 OPM. If not, see . Consult the COPYING file in the top-level source directory of this module for the precise wording of the license and the list of copyright holders. */ /*! * \file * * \copydoc Opm::co2ptflashproblem */ #ifndef OPM_CO2PTFLASH_PROBLEM_HH #define OPM_CO2PTFLASH_PROBLEM_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Opm { template class CO2PTProblem; } // namespace Opm */ namespace Opm::Properties { namespace TTag { struct CO2PTBaseProblem {}; } // end namespace TTag template struct NumComp { using type = UndefinedProperty; }; template struct NumComp { static constexpr int value = 3; }; // Set the grid type: --->2D template struct Grid { using type = Dune::YaspGrid; }; // Set the problem property template struct Problem { using type = Opm::CO2PTProblem; }; // Set flash solver template struct FlashSolver { private: using Scalar = GetPropType; using FluidSystem = GetPropType; using Evaluation = GetPropType; public: using type = Opm::PTFlash; }; // Set fluid configuration template struct FluidSystem { private: using Scalar = GetPropType; static constexpr int num_comp = getPropValue(); public: using type = Opm::GenericOilGasFluidSystem; }; // Set the material Law template struct MaterialLaw { private: using FluidSystem = GetPropType; enum { oilPhaseIdx = FluidSystem::oilPhaseIdx }; enum { gasPhaseIdx = FluidSystem::gasPhaseIdx }; using Scalar = GetPropType; using Traits = Opm::TwoPhaseMaterialTraits; // define the material law which is parameterized by effective saturation using EffMaterialLaw = Opm::NullMaterial; //using EffMaterialLaw = Opm::BrooksCorey; public: using type = EffMaterialLaw; }; // mesh grid template struct Vanguard { using type = Opm::StructuredGridVanguard; }; template struct EnableEnergy { static constexpr bool value = false; }; } // namespace Opm::Properties namespace Opm::Parameters { // this is kinds of telling the report step length template struct EpisodeLength { static constexpr Scalar value = 0.1 * 60. * 60.; }; template struct Initialpressure { static constexpr Scalar value = 75e5; }; struct SimulationName { static constexpr auto value = "co2_ptflash"; }; // set the defaults for the problem specific properties template struct Temperature { static constexpr Scalar value = 423.25; }; } // namespace Opm::Parameters namespace Opm { /*! * \ingroup TestProblems * * \brief 3 component simple testproblem with ["CO2", "C1", "C10"] * */ template class CO2PTProblem : public GetPropType { using ParentType = GetPropType; using Scalar = GetPropType; using Evaluation = GetPropType; using GridView = GetPropType; using FluidSystem = GetPropType; enum { dim = GridView::dimension }; enum { dimWorld = GridView::dimensionworld }; using Indices = GetPropType; using PrimaryVariables = GetPropType; using RateVector = GetPropType; using BoundaryRateVector = GetPropType; using MaterialLaw = GetPropType; using Simulator = GetPropType; using Model = GetPropType; using MaterialLawParams = GetPropType; using Toolbox = Opm::MathToolbox; using CoordScalar = typename GridView::ctype; enum { numPhases = FluidSystem::numPhases }; enum { oilPhaseIdx = FluidSystem::oilPhaseIdx }; enum { gasPhaseIdx = FluidSystem::gasPhaseIdx }; enum { conti0EqIdx = Indices::conti0EqIdx }; enum { numComponents = getPropValue() }; enum { enableEnergy = getPropValue() }; enum { enableDiffusion = getPropValue() }; using GlobalPosition = Dune::FieldVector; using DimMatrix = Dune::FieldMatrix; using DimVector = Dune::FieldVector; using ComponentVector = Dune::FieldVector; using FlashSolver = GetPropType; public: using FluidState = Opm::CompositionalFluidState; /*! * \copydoc Doxygen::defaultProblemConstructor */ explicit CO2PTProblem(Simulator& simulator) : ParentType(simulator) { const Scalar epi_len = Parameters::Get>(); simulator.setEpisodeLength(epi_len); FluidSystem::init(); using CompParm = typename FluidSystem::ComponentParam; using CO2 = Opm::SimpleCO2; using C1 = Opm::C1; using C10 = Opm::C10; FluidSystem::addComponent(CompParm {CO2::name(), CO2::molarMass(), CO2::criticalTemperature(), CO2::criticalPressure(), CO2::criticalVolume(), CO2::acentricFactor()}); FluidSystem::addComponent(CompParm {C1::name(), C1::molarMass(), C1::criticalTemperature(), C1::criticalPressure(), C1::criticalVolume(), C1::acentricFactor()}); FluidSystem::addComponent(CompParm{C10::name(), C10::molarMass(), C10::criticalTemperature(), C10::criticalPressure(), C10::criticalVolume(), C10::acentricFactor()}); // FluidSystem::add } void initPetrophysics() { temperature_ = Parameters::Get>(); K_ = this->toDimMatrix_(9.869232667160131e-14); porosity_ = 0.1; } template const DimVector& gravity([[maybe_unused]]const Context& context, [[maybe_unused]] unsigned spaceIdx, [[maybe_unused]] unsigned timeIdx) const { return gravity(); } const DimVector& gravity() const { return gravity_; } /*! * \copydoc FvBaseProblem::finishInit */ void finishInit() { ParentType::finishInit(); // initialize fixed parameters; temperature, permeability, porosity initPetrophysics(); } /*! * \copydoc co2ptflashproblem::registerParameters */ static void registerParameters() { ParentType::registerParameters(); Parameters::Register> ("The temperature [K] in the reservoir"); Parameters::Register> ("The initial pressure [Pa s] in the reservoir"); Parameters::Register ("The name of the simulation used for the output files"); Parameters::Register> ("Time interval [s] for episode length"); Parameters::SetDefault(30); Parameters::SetDefault>(300.0); if constexpr (dim > 1) { Parameters::SetDefault(1); Parameters::SetDefault>(1.0); } if constexpr (dim == 3) { Parameters::SetDefault(1); Parameters::SetDefault>(1.0); } Parameters::SetDefault>(60. * 60.); Parameters::SetDefault>(0.1 * 60. * 60.); Parameters::SetDefault(30); Parameters::SetDefault(6); Parameters::SetDefault>(1e-3); Parameters::SetDefault(true); Parameters::SetDefault(true); Parameters::SetDefault(true); Parameters::SetDefault(true); Parameters::SetDefault(true); Parameters::SetDefault(true); Parameters::SetDefault(true); Parameters::SetDefault>(0.0); Parameters::SetDefault>(1e-3); } /*! * \copydoc FvBaseProblem::name */ std::string name() const { std::ostringstream oss; oss << Parameters::Get(); return oss.str(); } // This method must be overridden for the simulator to continue with // a new episode. We just start a new episode with the same length as // the old one. void endEpisode() { Scalar epi_len = Parameters::Get>(); this->simulator().startNextEpisode(epi_len); } // only write output when episodes change, aka. report steps, and // include the initial timestep too bool shouldWriteOutput() { return this->simulator().episodeWillBeOver() || (this->simulator().timeStepIndex() == -1); } // we don't care about doing restarts from every fifth timestep, it // will just slow us down bool shouldWriteRestartFile() { return false; } /*! * \copydoc FvBaseProblem::endTimeStep */ void endTimeStep() { Scalar tol = this->model().newtonMethod().tolerance() * 1e5; this->model().checkConservativeness(tol); // Calculate storage terms PrimaryVariables storageO, storageW; this->model().globalPhaseStorage(storageO, oilPhaseIdx); // Calculate storage terms PrimaryVariables storageL, storageG; this->model().globalPhaseStorage(storageL, /*phaseIdx=*/0); this->model().globalPhaseStorage(storageG, /*phaseIdx=*/1); // Write mass balance information for rank 0 // if (this->gridView().comm().rank() == 0) { // std::cout << "Storage: liquid=[" << storageL << "]" // << " gas=[" << storageG << "]\n" << std::flush; // } } /*! * \copydoc FvBaseProblem::initial */ template void initial(PrimaryVariables& values, const Context& context, unsigned spaceIdx, unsigned timeIdx) const { Opm::CompositionalFluidState fs; initialFluidState(fs, context, spaceIdx, timeIdx); values.assignNaive(fs); } // Constant temperature template Scalar temperature([[maybe_unused]] const Context& context, [[maybe_unused]] unsigned spaceIdx, [[maybe_unused]] unsigned timeIdx) const { return temperature_; } // Constant permeability template const DimMatrix& intrinsicPermeability([[maybe_unused]] const Context& context, [[maybe_unused]] unsigned spaceIdx, [[maybe_unused]] unsigned timeIdx) const { return K_; } // Constant porosity template Scalar porosity([[maybe_unused]] const Context& context, [[maybe_unused]] unsigned spaceIdx, [[maybe_unused]] unsigned timeIdx) const { int spatialIdx = context.globalSpaceIndex(spaceIdx, timeIdx); int inj = 0; int prod = Parameters::Get() - 1; if (spatialIdx == inj || spatialIdx == prod) { return 1.0; } else { return porosity_; } } /*! * \copydoc FvBaseMultiPhaseProblem::materialLawParams */ template const MaterialLawParams& materialLawParams([[maybe_unused]] const Context& context, [[maybe_unused]] unsigned spaceIdx, [[maybe_unused]] unsigned timeIdx) const { return this->mat_; } // No flow (introduce fake wells instead) template void boundary(BoundaryRateVector& values, const Context& /*context*/, unsigned /*spaceIdx*/, unsigned /*timeIdx*/) const { values.setNoFlow(); } // No source terms template void source(RateVector& source_rate, [[maybe_unused]] const Context& context, [[maybe_unused]] unsigned spaceIdx, [[maybe_unused]] unsigned timeIdx) const { source_rate = Scalar(0.0); } private: /*! * \copydoc FvBaseProblem::initial */ template void initialFluidState(FluidState& fs, const Context& context, unsigned spaceIdx, unsigned timeIdx) const { // z0 = [0.5, 0.3, 0.2] // zi = [0.99, 0.01-1e-3, 1e-3] // p0 = 75e5 // T0 = 423.25 int inj = 0; int prod = Parameters::Get() - 1; int spatialIdx = context.globalSpaceIndex(spaceIdx, timeIdx); ComponentVector comp; comp[0] = Evaluation::createVariable(0.5, 1); comp[1] = Evaluation::createVariable(0.3, 2); comp[2] = 1. - comp[0] - comp[1]; if (spatialIdx == inj) { comp[0] = Evaluation::createVariable(0.99, 1); comp[1] = Evaluation::createVariable(0.01 - 1e-3, 2); comp[2] = 1. - comp[0] - comp[1]; } ComponentVector sat; sat[0] = 1.0; sat[1] = 1.0 - sat[0]; Scalar p0 = Parameters::Get>(); //\Note, for an AD variable, if we multiply it with 2, the derivative will also be scalced with 2, //\Note, so we should not do it. if (spatialIdx == inj) { p0 *= 2.0; } if (spatialIdx == prod) { p0 *= 0.5; } Evaluation p_init = Evaluation::createVariable(p0, 0); fs.setPressure(FluidSystem::oilPhaseIdx, p_init); fs.setPressure(FluidSystem::gasPhaseIdx, p_init); for (unsigned compIdx = 0; compIdx < numComponents; ++compIdx) { fs.setMoleFraction(FluidSystem::oilPhaseIdx, compIdx, comp[compIdx]); fs.setMoleFraction(FluidSystem::gasPhaseIdx, compIdx, comp[compIdx]); } // It is used here only for calculate the z fs.setSaturation(FluidSystem::oilPhaseIdx, sat[0]); fs.setSaturation(FluidSystem::gasPhaseIdx, sat[1]); fs.setTemperature(temperature_); // ParameterCache paramCache; { typename FluidSystem::template ParameterCache paramCache; paramCache.updatePhase(fs, FluidSystem::oilPhaseIdx); paramCache.updatePhase(fs, FluidSystem::gasPhaseIdx); fs.setDensity(FluidSystem::oilPhaseIdx, FluidSystem::density(fs, paramCache, FluidSystem::oilPhaseIdx)); fs.setDensity(FluidSystem::gasPhaseIdx, FluidSystem::density(fs, paramCache, FluidSystem::gasPhaseIdx)); fs.setViscosity(FluidSystem::oilPhaseIdx, FluidSystem::viscosity(fs, paramCache, FluidSystem::oilPhaseIdx)); fs.setViscosity(FluidSystem::gasPhaseIdx, FluidSystem::viscosity(fs, paramCache, FluidSystem::gasPhaseIdx)); } // determine the component total fractions // TODO: duplicated code here, while should be refactored out when we swithing // to starting from total mole fractions Dune::FieldVector z(0.0); Scalar sumMoles = 0.0; for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { const auto saturation = getValue(fs.saturation(phaseIdx)); for (unsigned compIdx = 0; compIdx < numComponents; ++compIdx) { Scalar tmp = getValue(fs.molarity(phaseIdx, compIdx)) * saturation; tmp = max(tmp, 1.e-8); z[compIdx] += tmp; sumMoles += tmp; } } z /= sumMoles; for (unsigned compIdx = 0; compIdx < numComponents - 1; ++compIdx) { fs.setMoleFraction(compIdx, z[compIdx]); } // Set initial K and L for (unsigned compIdx = 0; compIdx < numComponents; ++compIdx) { const Evaluation Ktmp = fs.wilsonK_(compIdx); fs.setKvalue(compIdx, Ktmp); } const Evaluation& Ltmp = -1.0; fs.setLvalue(Ltmp); } DimMatrix K_; Scalar porosity_; Scalar temperature_; MaterialLawParams mat_; DimVector gravity_; }; } // namespace Opm #endif