/* Copyright 2017 TNO - Heat Transfer & Fluid Dynamics, Modelling & Optimization of the Subsurface Copyright 2017 Statoil ASA. 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 3 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 . */ #include #include #include #include #include #include namespace Opm { template BlackoilAquiferModel::BlackoilAquiferModel(Simulator& simulator) : simulator_(simulator) { // Grid needs to support Facetag using Grid = std::remove_const_t>; static_assert(SupportsFaceTag::value, "Grid has to support assumptions about face tag."); init(); } template void BlackoilAquiferModel::initialSolutionApplied() { for (auto& aquifer : aquifers) aquifer->initialSolutionApplied(); } template void BlackoilAquiferModel::initFromRestart(const data::Aquifers& aquiferSoln) { for (auto& aquifer : this->aquifers) aquifer->initFromRestart(aquiferSoln); } template void BlackoilAquiferModel::beginEpisode() { // Probably function name beginReportStep() is more appropriate. // // Basically, we want to update the aquifer related information from // SCHEDULE setup in this section it is the beginning of a report step this->createDynamicAquifers(this->simulator_.episodeIndex()); } template void BlackoilAquiferModel::beginIteration() {} template void BlackoilAquiferModel::beginTimeStep() { for (auto& aquifer : aquifers) aquifer->beginTimeStep(); } template template void BlackoilAquiferModel::addToSource(RateVector& rates, const Context& context, unsigned spaceIdx, unsigned timeIdx) const { for (auto& aquifer : aquifers) aquifer->addToSource(rates, context, spaceIdx, timeIdx); } template void BlackoilAquiferModel::addToSource(RateVector& rates, unsigned globalSpaceIdx, unsigned timeIdx) const { for (auto& aquifer : aquifers) aquifer->addToSource(rates, globalSpaceIdx, timeIdx); } template void BlackoilAquiferModel::endIteration() {} template void BlackoilAquiferModel::endTimeStep() { for (auto& aquifer : aquifers) { aquifer->endTimeStep(); using NumAq = AquiferNumerical; NumAq* num = dynamic_cast(aquifer.get()); if (num) this->simulator_.vanguard().grid().comm().barrier(); } } template void BlackoilAquiferModel::endEpisode() {} template template void BlackoilAquiferModel::serialize(Restarter& /* res */) { // TODO (?) throw std::logic_error("BlackoilAquiferModel::serialize() is not yet implemented"); } template template void BlackoilAquiferModel::deserialize(Restarter& /* res */) { // TODO (?) throw std::logic_error("BlackoilAquiferModel::deserialize() is not yet implemented"); } // Initialize the aquifers in the deck template void BlackoilAquiferModel::init() { if (this->simulator_.vanguard().eclState().aquifer().active()) { this->initializeStaticAquifers(); } } template data::Aquifers BlackoilAquiferModel::aquiferData() const { data::Aquifers data; for (const auto& aqu : this->aquifers) data.insert_or_assign(aqu->aquiferID(), aqu->aquiferData()); return data; } template template void BlackoilAquiferModel:: serializeOp(Serializer& serializer) { for (auto& aiPtr : aquifers) { auto* ct = dynamic_cast*>(aiPtr.get()); auto* fetp = dynamic_cast*>(aiPtr.get()); auto* num = dynamic_cast*>(aiPtr.get()); auto* flux = dynamic_cast*>(aiPtr.get()); if (ct) { serializer(*ct); } else if (fetp) { serializer(*fetp); } else if (num) { serializer(*num); } else if (flux) { serializer(*flux); } else { OPM_THROW(std::logic_error, "Error serializing BlackoilAquiferModel: unknown aquifer type"); } } } template void BlackoilAquiferModel::initializeStaticAquifers() { const auto& aquifer = this->simulator_.vanguard().eclState().aquifer(); for (const auto& aquCT : aquifer.ct()) { auto aquCTPtr = this->template createAnalyticAquiferPointer >(aquCT, aquCT.aquiferID, "Carter-Tracy"); if (aquCTPtr != nullptr) { this->aquifers.push_back(std::move(aquCTPtr)); } } for (const auto& aquFetp : aquifer.fetp()) { auto aquFetpPtr = this->template createAnalyticAquiferPointer >(aquFetp, aquFetp.aquiferID, "Fetkovich"); if (aquFetpPtr != nullptr) { this->aquifers.push_back(std::move(aquFetpPtr)); } } for (const auto& [id, aquFlux] : aquifer.aquflux()) { // Make sure not dummy constant flux aquifers if (! aquFlux.active) { continue; } auto aquFluxPtr = this->template createAnalyticAquiferPointer >(aquFlux, id, "Constant Flux"); if (aquFluxPtr != nullptr) { this->aquifers.push_back(std::move(aquFluxPtr)); } } if (aquifer.hasNumericalAquifer()) { for (const auto& aquNum : aquifer.numericalAquifers().aquifers()) { auto aquNumPtr = std::make_unique> (aquNum.second, this->simulator_); this->aquifers.push_back(std::move(aquNumPtr)); } } } template template std::unique_ptr BlackoilAquiferModel:: createAnalyticAquiferPointer(const AquiferData& aqData, const int aquiferID, std::string_view aqType) const { const auto& connections = this->simulator_.vanguard().eclState().aquifer().connections(); if (! connections.hasAquiferConnections(aquiferID)) { const auto msg = fmt::format("No valid connections for {} aquifer {}. " "Aquifer {} will be ignored.", aqType, aquiferID, aquiferID); OpmLog::warning(msg); return {}; } return std::make_unique (connections.getConnections(aquiferID), this->simulator_, aqData); } template void BlackoilAquiferModel::createDynamicAquifers(const int episode_index) { const auto& sched = this->simulator_.vanguard().schedule()[episode_index]; for (const auto& [id, aquFlux] : sched.aqufluxs) { auto aquPos = std::find_if(std::begin(this->aquifers), std::end(this->aquifers), [id = id](const auto& aquPtr) { return aquPtr->aquiferID() == id; }); if (aquPos == std::end(this->aquifers)) { // An aquifer with this 'id' does not yet exist in // the collection managed by this object. Create it. auto aquFluxPtr = this->template createAnalyticAquiferPointer >(aquFlux, id, "Constant Flux"); if (aquFluxPtr != nullptr) { this->aquifers.push_back(std::move(aquFluxPtr)); } } else { auto aquFluxPtr = dynamic_cast*>(aquPos->get()); if (aquFluxPtr == nullptr) { // If the aquifers can return types easily, we might be able // to give a better message with type information. const auto msg = fmt::format("Aquifer {} is updated with constant flux " "aquifer keyword AQUFLUX at report step {}, " "while it might be specified to be a " "different type of aquifer before this. " "We do not support the conversion between " "different types of aquifer.\n", id, episode_index); OPM_THROW(std::runtime_error, msg); } aquFluxPtr->updateAquifer(aquFlux); } } } } // namespace Opm