93 ,
public FlowGenericProblem<GetPropType<TypeTag, Properties::GridView>,
94 GetPropType<TypeTag, Properties::FluidSystem>>
97 using BaseType = FlowGenericProblem<GetPropType<TypeTag, Properties::GridView>,
112 enum { dim = GridView::dimension };
113 enum { dimWorld = GridView::dimensionworld };
117 enum { numPhases = FluidSystem::numPhases };
118 enum { numComponents = FluidSystem::numComponents };
130 enum { enableMICP = Indices::enableMICP };
137 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
138 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
139 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
143 enum { gasCompIdx = FluidSystem::gasCompIdx };
144 enum { oilCompIdx = FluidSystem::oilCompIdx };
145 enum { waterCompIdx = FluidSystem::waterCompIdx };
150 using Element =
typename GridView::template Codim<0>::Entity;
154 using MaterialLawParams =
typename EclMaterialLawManager::MaterialLawParams;
155 using SolidEnergyLawParams =
typename EclThermalLawManager::SolidEnergyLawParams;
156 using ThermalConductionLawParams =
typename EclThermalLawManager::ThermalConductionLawParams;
164 using Toolbox = MathToolbox<Evaluation>;
165 using DimMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
169 using DirectionalMobilityPtr = Utility::CopyablePtr<DirectionalMobility<TypeTag>>;
184 ParentType::registerParameters();
186 registerFlowProblemParameters<Scalar>();
199 const std::string&)> addKey,
200 std::set<std::string>& seenParams,
201 std::string& errorMsg,
207 return detail::eclPositionalParameter(addKey,
218 : ParentType(simulator)
219 , BaseType(simulator.vanguard().eclState(),
220 simulator.vanguard().schedule(),
221 simulator.vanguard().gridView())
222 , transmissibilities_(simulator.vanguard().eclState(),
223 simulator.vanguard().gridView(),
224 simulator.vanguard().cartesianIndexMapper(),
225 simulator.vanguard().grid(),
226 simulator.vanguard().cellCentroids(),
227 (energyModuleType == EnergyModules::FullyImplicitThermal ||
228 energyModuleType == EnergyModules::SequentialImplicitThermal), enableDiffusion,
230 , wellModel_(simulator)
231 , aquiferModel_(simulator)
232 , pffDofData_(simulator.gridView(), this->elementMapper())
233 , tracerModel_(simulator)
234 , temperatureModel_(simulator)
241 relpermDiagnostics.
diagnosis(simulator.vanguard().eclState(),
242 simulator.vanguard().levelCartesianIndexMapper());
245 if (energyModuleType == EnergyModules::SequentialImplicitThermal) {
253 void prefetch(
const Element& elem)
const
254 { this->pffDofData_.prefetch(elem); }
267 template <
class Restarter>
274 wellModel_.deserialize(res);
277 aquiferModel_.deserialize(res);
286 template <
class Restarter>
289 wellModel_.serialize(res);
291 aquiferModel_.serialize(res);
294 int episodeIndex()
const
296 return std::max(this->simulator().episodeIndex(), 0);
306 auto& simulator = this->simulator();
307 int episodeIdx = simulator.episodeIndex();
308 auto& eclState = simulator.vanguard().eclState();
309 const auto& schedule = simulator.vanguard().schedule();
310 const auto& events = schedule[episodeIdx].events();
312 if (episodeIdx >= 0 && events.hasEvent(ScheduleEvents::GEO_MODIFIER)) {
319 const auto& miniDeck = schedule[episodeIdx].geo_keywords();
320 const auto& cc = simulator.vanguard().grid().comm();
321 eclState.apply_schedule_keywords( miniDeck );
322 eclBroadcast(cc, eclState.getTransMult() );
325 std::function<
unsigned int(
unsigned int)> equilGridToGrid = [&simulator](
unsigned int i) {
326 return simulator.vanguard().gridEquilIdxToGridIdx(i);
330 using TransUpdateQuantities =
typename Vanguard::TransmissibilityType::TransUpdateQuantities;
331 transmissibilities_.update(
true, TransUpdateQuantities::All, equilGridToGrid);
332 this->referencePorosity_[1] = this->referencePorosity_[0];
333 updateReferencePorosity_();
334 this->rockFraction_[1] = this->rockFraction_[0];
335 updateRockFraction_();
337 this->model().linearizer().updateDiscretizationParameters();
340 bool tuningEvent = this->beginEpisode_(enableExperiments, this->episodeIndex());
343 wellModel_.beginEpisode();
346 aquiferModel_.beginEpisode();
349 Scalar dt = limitNextTimeStepSize_(simulator.episodeLength());
351 if ( (episodeIdx == 0 || tuningEvent) && this->initialTimeStepSize_ > 0)
354 dt = std::min(dt, this->initialTimeStepSize_);
355 simulator.setTimeStepSize(dt);
364 const int episodeIdx = this->episodeIndex();
365 const int timeStepSize = this->simulator().timeStepSize();
367 this->beginTimeStep_(enableExperiments,
369 this->simulator().timeStepIndex(),
370 this->simulator().startTime(),
371 this->simulator().time(),
373 this->simulator().endTime());
378 this->updateExplicitQuantities_(episodeIdx, timeStepSize, first_step_ && (episodeIdx > 0));
381 if (nonTrivialBoundaryConditions()) {
382 this->model().linearizer().updateBoundaryConditionData();
385 wellModel_.beginTimeStep();
386 aquiferModel_.beginTimeStep();
387 tracerModel_.beginTimeStep();
388 temperatureModel_.beginTimeStep();
398 wellModel_.beginIteration();
399 aquiferModel_.beginIteration();
408 wellModel_.endIteration();
409 aquiferModel_.endIteration();
426 const int rank = this->simulator().gridView().comm().rank();
428 std::cout <<
"checking conservativeness of solution\n";
431 this->model().checkConservativeness(-1,
true);
433 std::cout <<
"solution is sufficiently conservative\n";
438 auto& simulator = this->simulator();
439 simulator.setTimeStepIndex(simulator.timeStepIndex()+1);
441 this->wellModel_.endTimeStep();
442 this->aquiferModel_.endTimeStep();
443 this->tracerModel_.endTimeStep();
446 this->model().linearizer().updateFlowsInfo();
448 if (this->enableDriftCompensation_ || this->enableDriftCompensationTemp_) {
449 OPM_TIMEBLOCK(driftCompansation);
451 const auto& residual = this->model().linearizer().residual();
453 for (
unsigned globalDofIdx = 0; globalDofIdx < residual.size(); globalDofIdx ++) {
454 int sfcdofIdx = simulator.vanguard().gridEquilIdxToGridIdx(globalDofIdx);
455 this->drift_[sfcdofIdx] = residual[sfcdofIdx] * simulator.timeStepSize();
458 this->drift_[sfcdofIdx] *= this->model().dofTotalVolume(sfcdofIdx);
464 if constexpr(energyModuleType == EnergyModules::SequentialImplicitThermal) {
465 this->temperatureModel_.endTimeStep(wellModel_.wellState());
474 const int episodeIdx = this->episodeIndex();
476 this->wellModel_.endEpisode();
477 this->aquiferModel_.endEpisode();
479 const auto& schedule = this->simulator().vanguard().schedule();
482 if (episodeIdx + 1 >=
static_cast<int>(schedule.size()) - 1) {
483 this->simulator().setFinished(
true);
488 this->simulator().startNextEpisode(schedule.stepLength(episodeIdx + 1));
497 OPM_TIMEBLOCK(problemWriteOutput);
503 ParentType::writeOutput(verbose);
510 template <
class Context>
513 unsigned timeIdx)
const
515 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
516 return transmissibilities_.permeability(globalSpaceIdx);
526 {
return transmissibilities_.permeability(globalElemIdx); }
531 template <
class Context>
533 [[maybe_unused]]
unsigned fromDofLocalIdx,
534 unsigned toDofLocalIdx)
const
536 assert(fromDofLocalIdx == 0);
537 return pffDofData_.get(context.element(), toDofLocalIdx).transmissibility;
545 return transmissibilities_.transmissibility(globalCenterElemIdx, globalElemIdx);
551 template <
class Context>
553 [[maybe_unused]]
unsigned fromDofLocalIdx,
554 unsigned toDofLocalIdx)
const
556 assert(fromDofLocalIdx == 0);
557 return *pffDofData_.get(context.element(), toDofLocalIdx).diffusivity;
563 Scalar
diffusivity(
const unsigned globalCellIn,
const unsigned globalCellOut)
const{
564 return transmissibilities_.diffusivity(globalCellIn, globalCellOut);
570 Scalar
dispersivity(
const unsigned globalCellIn,
const unsigned globalCellOut)
const{
571 return transmissibilities_.dispersivity(globalCellIn, globalCellOut);
578 const unsigned boundaryFaceIdx)
const
580 return transmissibilities_.thermalTransmissibilityBoundary(globalSpaceIdx, boundaryFaceIdx);
589 template <
class Context>
591 unsigned boundaryFaceIdx)
const
593 unsigned elemIdx = elemCtx.globalSpaceIndex(0, 0);
594 return transmissibilities_.transmissibilityBoundary(elemIdx, boundaryFaceIdx);
601 const unsigned boundaryFaceIdx)
const
603 return transmissibilities_.transmissibilityBoundary(globalSpaceIdx, boundaryFaceIdx);
611 const unsigned globalSpaceIdxOut)
const
613 return transmissibilities_.thermalHalfTrans(globalSpaceIdxIn,globalSpaceIdxOut);
619 template <
class Context>
622 unsigned timeIdx)
const
624 const auto& face = context.stencil(timeIdx).interiorFace(faceIdx);
625 unsigned toDofLocalIdx = face.exteriorIndex();
626 return *pffDofData_.get(context.element(), toDofLocalIdx).thermalHalfTransIn;
632 template <
class Context>
635 unsigned timeIdx)
const
637 const auto& face = context.stencil(timeIdx).interiorFace(faceIdx);
638 unsigned toDofLocalIdx = face.exteriorIndex();
639 return *pffDofData_.get(context.element(), toDofLocalIdx).thermalHalfTransOut;
645 template <
class Context>
647 unsigned boundaryFaceIdx)
const
649 unsigned elemIdx = elemCtx.globalSpaceIndex(0, 0);
650 return transmissibilities_.thermalHalfTransBoundary(elemIdx, boundaryFaceIdx);
657 {
return transmissibilities_; }
661 {
return tracerModel_; }
663 TracerModel& tracerModel()
664 {
return tracerModel_; }
666 TemperatureModel& temperatureModel()
667 {
return temperatureModel_; }
677 template <
class Context>
678 Scalar
porosity(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
680 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
681 return this->
porosity(globalSpaceIdx, timeIdx);
690 template <
class Context>
691 Scalar
dofCenterDepth(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
693 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
705 return this->simulator().vanguard().cellCenterDepth(globalSpaceIdx);
711 template <
class Context>
714 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
721 template <
class Context>
724 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
733 const auto& rock_config = this->simulator().vanguard().eclState().getSimulationConfig().rock_config();
734 if (rock_config.store()) {
735 return asImp_().initialFluidState(globalSpaceIdx).pressure(refPressurePhaseIdx_());
738 if (this->rockParams_.empty())
741 unsigned tableIdx = 0;
742 if (!this->rockTableIdx_.empty()) {
743 tableIdx = this->rockTableIdx_[globalSpaceIdx];
745 return this->rockParams_[tableIdx].referencePressure;
752 template <
class Context>
754 unsigned spaceIdx,
unsigned timeIdx)
const
756 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
762 return materialLawManager_->materialLawParams(globalDofIdx);
765 const MaterialLawParams&
materialLawParams(
unsigned globalDofIdx, FaceDir::DirEnum facedir)
const
767 return materialLawManager_->materialLawParams(globalDofIdx, facedir);
773 template <
class Context>
774 const SolidEnergyLawParams&
777 unsigned timeIdx)
const
779 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
780 return thermalLawManager_->solidEnergyLawParams(globalSpaceIdx);
786 template <
class Context>
787 const ThermalConductionLawParams &
790 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
791 return thermalLawManager_->thermalConductionLawParams(globalSpaceIdx);
801 {
return materialLawManager_; }
803 template <
class FluidState,
class ...Args>
805 std::array<Evaluation,numPhases> &mobility,
806 DirectionalMobilityPtr &dirMob,
807 FluidState &fluidState,
808 unsigned globalSpaceIdx)
const
810 using ContainerT = std::array<Evaluation, numPhases>;
811 OPM_TIMEBLOCK_LOCAL(updateRelperms, Subsystem::SatProps);
816 MaterialLaw::template relativePermeabilities<ContainerT, FluidState, Args...>(mobility, materialParams, fluidState);
817 Valgrind::CheckDefined(mobility);
819 if (materialLawManager_->hasDirectionalRelperms()
820 || materialLawManager_->hasDirectionalImbnum())
822 using Dir = FaceDir::DirEnum;
823 constexpr int ndim = 3;
824 dirMob = std::make_unique<DirectionalMobility<TypeTag>>();
825 Dir facedirs[ndim] = {Dir::XPlus, Dir::YPlus, Dir::ZPlus};
826 for (
int i = 0; i<ndim; i++) {
828 auto& mob_array = dirMob->getArray(i);
829 MaterialLaw::template relativePermeabilities<ContainerT, FluidState, Args...>(mob_array, materialParams, fluidState);
838 {
return materialLawManager_; }
844 template <
class Context>
845 unsigned pvtRegionIndex(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
846 {
return pvtRegionIndex(context.globalSpaceIndex(spaceIdx, timeIdx)); }
852 template <
class Context>
860 template <
class Context>
868 template <
class Context>
877 template <
class Context>
885 {
return this->simulator().vanguard().caseName(); }
890 template <
class Context>
891 Scalar
temperature(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
895 unsigned globalDofIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
896 if constexpr (energyModuleType == EnergyModules::SequentialImplicitThermal)
897 return temperatureModel_.temperature(globalDofIdx);
899 return asImp_().initialFluidState(globalDofIdx).temperature(0);
903 Scalar
temperature(
unsigned globalDofIdx,
unsigned )
const
907 if constexpr (energyModuleType == EnergyModules::SequentialImplicitThermal)
908 return temperatureModel_.temperature(globalDofIdx);
910 return asImp_().initialFluidState(globalDofIdx).temperature(0);
913 const SolidEnergyLawParams&
917 return this->thermalLawManager_->solidEnergyLawParams(globalSpaceIdx);
919 const ThermalConductionLawParams &
923 return this->thermalLawManager_->thermalConductionLawParams(globalSpaceIdx);
937 if (!this->vapparsActive(this->episodeIndex()))
940 return this->maxOilSaturation_[globalDofIdx];
954 if (!this->vapparsActive(this->episodeIndex()))
957 this->maxOilSaturation_[globalDofIdx] = value;
966 this->model().invalidateAndUpdateIntensiveQuantities(0);
972 if (!this->model().enableStorageCache() || !this->recycleFirstIterationStorage()) {
973 this->model().invalidateAndUpdateIntensiveQuantities(1);
981 aquiferModel_.initialSolutionApplied();
983 const bool invalidateFromHyst = updateHysteresis_();
984 if (invalidateFromHyst) {
985 OPM_TIMEBLOCK(beginTimeStepInvalidateIntensiveQuantities);
986 this->model().invalidateAndUpdateIntensiveQuantities(0);
995 template <
class Context>
997 const Context& context,
999 unsigned timeIdx)
const
1001 const unsigned globalDofIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
1002 source(rate, globalDofIdx, timeIdx);
1005 void source(RateVector& rate,
1006 unsigned globalDofIdx,
1007 unsigned timeIdx)
const
1009 OPM_TIMEBLOCK_LOCAL(eclProblemSource, Subsystem::Assembly);
1013 wellModel_.computeTotalRatesForDof(rate, globalDofIdx);
1017 for (
unsigned eqIdx = 0; eqIdx < numEq; ++ eqIdx) {
1018 rate[eqIdx] /= this->model().dofTotalVolume(globalDofIdx);
1020 Valgrind::CheckDefined(rate[eqIdx]);
1021 assert(isfinite(rate[eqIdx]));
1025 addToSourceDense(rate, globalDofIdx, timeIdx);
1028 virtual void addToSourceDense(RateVector& rate,
1029 unsigned globalDofIdx,
1030 unsigned timeIdx)
const = 0;
1038 {
return wellModel_; }
1041 {
return wellModel_; }
1043 const AquiferModel& aquiferModel()
const
1044 {
return aquiferModel_; }
1046 AquiferModel& mutableAquiferModel()
1047 {
return aquiferModel_; }
1049 bool nonTrivialBoundaryConditions()
const
1050 {
return nonTrivialBoundaryConditions_; }
1060 OPM_TIMEBLOCK(nexTimeStepSize);
1062 if (this->nextTimeStepSize_ > 0.0)
1063 return this->nextTimeStepSize_;
1065 const auto& simulator = this->simulator();
1066 int episodeIdx = simulator.episodeIndex();
1070 return this->initialTimeStepSize_;
1075 const auto& newtonMethod = this->model().newtonMethod();
1076 return limitNextTimeStepSize_(newtonMethod.suggestTimeStepSize(simulator.timeStepSize()));
1084 template <
class LhsEval>
1088 if (this->rockCompPoroMult_.empty() && this->rockCompPoroMultWc_.empty())
1091 unsigned tableIdx = 0;
1092 if (!this->rockTableIdx_.empty())
1093 tableIdx = this->rockTableIdx_[elementIdx];
1095 const auto& fs = intQuants.fluidState();
1096 LhsEval effectivePressure = decay<LhsEval>(fs.pressure(refPressurePhaseIdx_()));
1097 const auto& rock_config = this->simulator().vanguard().eclState().getSimulationConfig().rock_config();
1098 if (!this->minRefPressure_.empty())
1101 min(decay<LhsEval>(fs.pressure(refPressurePhaseIdx_())),
1102 this->minRefPressure_[elementIdx]);
1104 if (!this->overburdenPressure_.empty())
1105 effectivePressure -= this->overburdenPressure_[elementIdx];
1107 if (rock_config.store()) {
1108 effectivePressure -= asImp_().initialFluidState(elementIdx).pressure(refPressurePhaseIdx_());
1111 if (!this->rockCompPoroMult_.empty()) {
1112 return this->rockCompPoroMult_[tableIdx].eval(effectivePressure,
true);
1116 assert(!this->rockCompPoroMultWc_.empty());
1117 LhsEval SwMax = max(decay<LhsEval>(fs.saturation(waterPhaseIdx)), this->maxWaterSaturation_[elementIdx]);
1118 LhsEval SwDeltaMax = SwMax - asImp_().initialFluidStates()[elementIdx].saturation(waterPhaseIdx);
1120 return this->rockCompPoroMultWc_[tableIdx].eval(effectivePressure, SwDeltaMax,
true);
1128 template <
class LhsEval>
1131 auto obtain = [](
const auto& value)
1133 if constexpr (std::is_same_v<LhsEval, Scalar>) {
1134 return getValue(value);
1142 template <
class LhsEval,
class Callback>
1143 LhsEval
rockCompTransMultiplier(
const IntensiveQuantities& intQuants,
unsigned elementIdx, Callback& obtain)
const
1145 const bool implicit = !this->explicitRockCompaction_;
1147 : this->simulator().problem().getRockCompTransMultVal(elementIdx);
1150 template <
class LhsEval,
class Callback>
1151 LhsEval wellTransMultiplier(
const IntensiveQuantities& intQuants,
unsigned elementIdx, Callback& obtain)
const
1153 OPM_TIMEBLOCK_LOCAL(wellTransMultiplier, Subsystem::Wells);
1155 const bool implicit = !this->explicitRockCompaction_;
1157 : this->simulator().problem().getRockCompTransMultVal(elementIdx);
1158 trans_mult *= this->simulator().problem().template permFactTransMultiplier<LhsEval>(intQuants, elementIdx, obtain);
1163 std::pair<BCType, RateVector> boundaryCondition(
const unsigned int globalSpaceIdx,
const int directionId)
const
1165 OPM_TIMEBLOCK_LOCAL(boundaryCondition, Subsystem::Assembly);
1166 if (!nonTrivialBoundaryConditions_) {
1167 return { BCType::NONE, RateVector(0.0) };
1169 FaceDir::DirEnum dir = FaceDir::FromIntersectionIndex(directionId);
1170 const auto& schedule = this->simulator().vanguard().schedule();
1171 if (bcindex_(dir)[globalSpaceIdx] == 0) {
1172 return { BCType::NONE, RateVector(0.0) };
1174 if (schedule[this->episodeIndex()].bcprop.size() == 0) {
1175 return { BCType::NONE, RateVector(0.0) };
1177 const auto& bc = schedule[this->episodeIndex()].bcprop[bcindex_(dir)[globalSpaceIdx]];
1178 if (bc.bctype!=BCType::RATE) {
1179 return { bc.bctype, RateVector(0.0) };
1182 RateVector rate = 0.0;
1183 switch (bc.component) {
1184 case BCComponent::OIL:
1185 rate[FluidSystem::canonicalToActiveCompIdx(oilCompIdx)] = bc.rate;
1187 case BCComponent::GAS:
1188 rate[FluidSystem::canonicalToActiveCompIdx(gasCompIdx)] = bc.rate;
1190 case BCComponent::WATER:
1191 rate[FluidSystem::canonicalToActiveCompIdx(waterCompIdx)] = bc.rate;
1193 case BCComponent::SOLVENT:
1194 this->handleSolventBC(bc, rate);
1196 case BCComponent::POLYMER:
1197 this->handlePolymerBC(bc, rate);
1199 case BCComponent::MICR:
1200 this->handleMicrBC(bc, rate);
1202 case BCComponent::OXYG:
1203 this->handleOxygBC(bc, rate);
1205 case BCComponent::UREA:
1206 this->handleUreaBC(bc, rate);
1208 case BCComponent::NONE:
1209 throw std::logic_error(
"you need to specify the component when RATE type is set in BC");
1213 return {bc.bctype, rate};
1217 template<
class Serializer>
1218 void serializeOp(Serializer& serializer)
1220 serializer(
static_cast<BaseType&
>(*
this));
1222 serializer(wellModel_);
1223 serializer(aquiferModel_);
1224 serializer(tracerModel_);
1225 serializer(*materialLawManager_);
1228 const GlobalEqVector& drift()
const
1234 Implementation& asImp_()
1235 {
return *
static_cast<Implementation *
>(
this); }
1237 const Implementation& asImp_()
const
1238 {
return *
static_cast<const Implementation *
>(
this); }
1241 template<
class UpdateFunc>
1242 void updateProperty_(
const std::string& failureMsg,
1245 OPM_TIMEBLOCK(updateProperty);
1246 const auto& model = this->simulator().model();
1247 const auto& primaryVars = model.solution(0);
1248 const auto& vanguard = this->simulator().vanguard();
1249 std::size_t numGridDof = primaryVars.size();
1250 OPM_BEGIN_PARALLEL_TRY_CATCH();
1252#pragma omp parallel for
1254 for (
unsigned dofIdx = 0; dofIdx < numGridDof; ++dofIdx) {
1255 const auto& iq = *model.cachedIntensiveQuantities(dofIdx, 0);
1258 OPM_END_PARALLEL_TRY_CATCH(failureMsg, vanguard.grid().comm());
1261 bool updateMaxOilSaturation_()
1263 OPM_TIMEBLOCK(updateMaxOilSaturation);
1264 int episodeIdx = this->episodeIndex();
1267 if (this->vapparsActive(episodeIdx)) {
1268 this->updateProperty_(
"FlowProblem::updateMaxOilSaturation_() failed:",
1269 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1271 this->updateMaxOilSaturation_(compressedDofIdx,iq);
1279 bool updateMaxOilSaturation_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1281 OPM_TIMEBLOCK_LOCAL(updateMaxOilSaturation, Subsystem::SatProps);
1282 const auto& fs = iq.fluidState();
1283 const Scalar So = decay<Scalar>(fs.saturation(refPressurePhaseIdx_()));
1284 auto& mos = this->maxOilSaturation_;
1285 if(mos[compressedDofIdx] < So){
1286 mos[compressedDofIdx] = So;
1293 bool updateMaxWaterSaturation_()
1295 OPM_TIMEBLOCK(updateMaxWaterSaturation);
1297 if (this->maxWaterSaturation_.empty())
1300 this->maxWaterSaturation_[1] = this->maxWaterSaturation_[0];
1301 this->updateProperty_(
"FlowProblem::updateMaxWaterSaturation_() failed:",
1302 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1304 this->updateMaxWaterSaturation_(compressedDofIdx,iq);
1310 bool updateMaxWaterSaturation_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1312 OPM_TIMEBLOCK_LOCAL(updateMaxWaterSaturation, Subsystem::SatProps);
1313 const auto& fs = iq.fluidState();
1314 const Scalar Sw = decay<Scalar>(fs.saturation(waterPhaseIdx));
1315 auto& mow = this->maxWaterSaturation_;
1316 if(mow[compressedDofIdx]< Sw){
1317 mow[compressedDofIdx] = Sw;
1324 bool updateMinPressure_()
1326 OPM_TIMEBLOCK(updateMinPressure);
1328 if (this->minRefPressure_.empty())
1331 this->updateProperty_(
"FlowProblem::updateMinPressure_() failed:",
1332 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1334 this->updateMinPressure_(compressedDofIdx,iq);
1339 bool updateMinPressure_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq){
1340 OPM_TIMEBLOCK_LOCAL(updateMinPressure, Subsystem::PvtProps);
1341 const auto& fs = iq.fluidState();
1342 const Scalar min_pressure = getValue(fs.pressure(refPressurePhaseIdx_()));
1343 auto& min_pressures = this->minRefPressure_;
1344 if(min_pressures[compressedDofIdx]> min_pressure){
1345 min_pressures[compressedDofIdx] = min_pressure;
1356 std::function<std::vector<double>(
const FieldPropsManager&,
const std::string&)>
1357 fieldPropDoubleOnLeafAssigner_()
1359 const auto& lookup = this->lookUpData_;
1360 return [&lookup](
const FieldPropsManager& fieldPropManager,
const std::string& propString)
1362 return lookup.assignFieldPropsDoubleOnLeaf(fieldPropManager, propString);
1370 template<
typename IntType>
1371 std::function<std::vector<IntType>(
const FieldPropsManager&,
const std::string&,
bool)>
1372 fieldPropIntTypeOnLeafAssigner_()
1374 const auto& lookup = this->lookUpData_;
1375 return [&lookup](
const FieldPropsManager& fieldPropManager,
const std::string& propString,
bool needsTranslation)
1377 return lookup.template assignFieldPropsIntOnLeaf<IntType>(fieldPropManager, propString, needsTranslation);
1381 void readMaterialParameters_()
1383 OPM_TIMEBLOCK(readMaterialParameters);
1384 const auto& simulator = this->simulator();
1385 const auto& vanguard = simulator.vanguard();
1386 const auto& eclState = vanguard.eclState();
1389 OPM_BEGIN_PARALLEL_TRY_CATCH();
1390 this->updatePvtnum_();
1391 this->updateSatnum_();
1394 this->updateMiscnum_();
1396 this->updatePlmixnum_();
1398 OPM_END_PARALLEL_TRY_CATCH(
"Invalid region numbers: ", vanguard.gridView().comm());
1401 updateReferencePorosity_();
1402 this->referencePorosity_[1] = this->referencePorosity_[0];
1407 updateRockFraction_();
1408 this->rockFraction_[1] = this->rockFraction_[0];
1413 materialLawManager_ = std::make_shared<EclMaterialLawManager>();
1414 materialLawManager_->initFromState(eclState);
1415 materialLawManager_->initParamsForElements(eclState, this->model().numGridDof(),
1416 this->
template fieldPropIntTypeOnLeafAssigner_<int>(),
1417 this-> lookupIdxOnLevelZeroAssigner_());
1421 void readThermalParameters_()
1423 if constexpr (energyModuleType == EnergyModules::FullyImplicitThermal ||
1424 energyModuleType == EnergyModules::SequentialImplicitThermal )
1426 const auto& simulator = this->simulator();
1427 const auto& vanguard = simulator.vanguard();
1428 const auto& eclState = vanguard.eclState();
1431 thermalLawManager_ = std::make_shared<EclThermalLawManager>();
1432 thermalLawManager_->initParamsForElements(eclState, this->model().numGridDof(),
1433 this-> fieldPropDoubleOnLeafAssigner_(),
1434 this->
template fieldPropIntTypeOnLeafAssigner_<unsigned int>());
1438 void updateReferencePorosity_()
1440 const auto& simulator = this->simulator();
1441 const auto& vanguard = simulator.vanguard();
1442 const auto& eclState = vanguard.eclState();
1444 std::size_t numDof = this->model().numGridDof();
1446 this->referencePorosity_[0].resize(numDof);
1448 const auto& fp = eclState.fieldProps();
1449 const std::vector<double> porvData =
this -> fieldPropDoubleOnLeafAssigner_()(fp,
"PORV");
1450 for (std::size_t dofIdx = 0; dofIdx < numDof; ++dofIdx) {
1451 int sfcdofIdx = simulator.vanguard().gridEquilIdxToGridIdx(dofIdx);
1452 Scalar poreVolume = porvData[dofIdx];
1457 Scalar dofVolume = simulator.model().dofTotalVolume(sfcdofIdx);
1458 assert(dofVolume > 0.0);
1459 this->referencePorosity_[0][sfcdofIdx] = poreVolume/dofVolume;
1463 void updateRockFraction_() {
1465 const bool solveEnergyEquation = (energyModuleType == EnergyModules::FullyImplicitThermal ||
1466 energyModuleType == EnergyModules::SequentialImplicitThermal);
1467 if (!solveEnergyEquation)
1470 const auto& simulator = this->simulator();
1471 const auto& vanguard = simulator.vanguard();
1472 const auto& eclState = vanguard.eclState();
1474 std::size_t numDof = this->model().numGridDof();
1475 this->rockFraction_[0].resize(numDof);
1484 const auto& fp = eclState.fieldProps();
1485 const std::vector<double> poroData = this->fieldPropDoubleOnLeafAssigner_()(fp,
"PORO");
1486 const std::vector<double> ntgData = this->fieldPropDoubleOnLeafAssigner_()(fp,
"NTG");
1488 for (std::size_t dofIdx = 0; dofIdx < numDof; ++dofIdx) {
1489 const auto ntg = ntgData[dofIdx];
1490 const auto poro_eff = ntg * poroData[dofIdx];
1491 const int sfcdofIdx = simulator.vanguard().gridEquilIdxToGridIdx(dofIdx);
1492 const auto rock_fraction = (1 - poro_eff) * this->referencePorosity_[0][sfcdofIdx] / poro_eff;
1493 this->rockFraction_[0][sfcdofIdx] = rock_fraction;
1497 virtual void readInitialCondition_()
1500 const auto& simulator = this->simulator();
1501 const auto& vanguard = simulator.vanguard();
1502 const auto& eclState = vanguard.eclState();
1504 if (eclState.getInitConfig().hasEquil())
1505 readEquilInitialCondition_();
1507 readExplicitInitialCondition_();
1510 std::size_t numElems = this->model().numGridDof();
1511 for (std::size_t elemIdx = 0; elemIdx < numElems; ++elemIdx) {
1512 const auto& fs = asImp_().initialFluidStates()[elemIdx];
1513 if (!this->maxWaterSaturation_.empty() && waterPhaseIdx > -1)
1514 this->maxWaterSaturation_[elemIdx] = std::max(this->maxWaterSaturation_[elemIdx], fs.saturation(waterPhaseIdx));
1515 if (!this->maxOilSaturation_.empty() && oilPhaseIdx > -1)
1516 this->maxOilSaturation_[elemIdx] = std::max(this->maxOilSaturation_[elemIdx], fs.saturation(oilPhaseIdx));
1517 if (!this->minRefPressure_.empty() && refPressurePhaseIdx_() > -1)
1518 this->minRefPressure_[elemIdx] = std::min(this->minRefPressure_[elemIdx], fs.pressure(refPressurePhaseIdx_()));
1522 virtual void readEquilInitialCondition_() = 0;
1523 virtual void readExplicitInitialCondition_() = 0;
1526 bool updateHysteresis_()
1528 if (!materialLawManager_->enableHysteresis())
1533 this->updateProperty_(
"FlowProblem::updateHysteresis_() failed:",
1534 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1536 materialLawManager_->updateHysteresis(iq.fluidState(), compressedDofIdx);
1542 bool updateHysteresis_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1544 OPM_TIMEBLOCK_LOCAL(updateHysteresis_, Subsystem::SatProps);
1545 materialLawManager_->updateHysteresis(iq.fluidState(), compressedDofIdx);
1550 Scalar getRockCompTransMultVal(std::size_t dofIdx)
const
1552 if (this->rockCompTransMultVal_.empty())
1555 return this->rockCompTransMultVal_[dofIdx];
1561 ConditionalStorage<enableFullyImplicitThermal, Scalar> thermalHalfTransIn;
1562 ConditionalStorage<enableFullyImplicitThermal, Scalar> thermalHalfTransOut;
1563 ConditionalStorage<enableDiffusion, Scalar> diffusivity;
1564 ConditionalStorage<enableDispersion, Scalar> dispersivity;
1565 Scalar transmissibility;
1569 void updatePffDofData_()
1571 const auto& distFn =
1573 const Stencil& stencil,
1574 unsigned localDofIdx)
1577 const auto& elementMapper = this->model().elementMapper();
1579 unsigned globalElemIdx = elementMapper.index(stencil.entity(localDofIdx));
1580 if (localDofIdx != 0) {
1581 unsigned globalCenterElemIdx = elementMapper.index(stencil.entity(0));
1582 dofData.transmissibility = transmissibilities_.transmissibility(globalCenterElemIdx, globalElemIdx);
1584 if constexpr (enableFullyImplicitThermal) {
1585 *dofData.thermalHalfTransIn = transmissibilities_.thermalHalfTrans(globalCenterElemIdx, globalElemIdx);
1586 *dofData.thermalHalfTransOut = transmissibilities_.thermalHalfTrans(globalElemIdx, globalCenterElemIdx);
1588 if constexpr (enableDiffusion)
1589 *dofData.diffusivity = transmissibilities_.diffusivity(globalCenterElemIdx, globalElemIdx);
1590 if (enableDispersion)
1591 dofData.dispersivity = transmissibilities_.dispersivity(globalCenterElemIdx, globalElemIdx);
1595 pffDofData_.update(distFn);
1598 virtual void updateExplicitQuantities_(
int episodeIdx,
int timeStepSize,
bool first_step_after_restart) = 0;
1600 void readBoundaryConditions_()
1602 const auto& simulator = this->simulator();
1603 const auto& vanguard = simulator.vanguard();
1604 const auto& bcconfig = vanguard.eclState().getSimulationConfig().bcconfig();
1605 if (bcconfig.size() > 0) {
1606 nonTrivialBoundaryConditions_ =
true;
1608 std::size_t numCartDof = vanguard.cartesianSize();
1609 unsigned numElems = vanguard.gridView().size(0);
1610 std::vector<int> cartesianToCompressedElemIdx(numCartDof, -1);
1612 for (
unsigned elemIdx = 0; elemIdx < numElems; ++elemIdx)
1613 cartesianToCompressedElemIdx[vanguard.cartesianIndex(elemIdx)] = elemIdx;
1615 bcindex_.resize(numElems, 0);
1616 auto loopAndApply = [&cartesianToCompressedElemIdx,
1617 &vanguard](
const auto& bcface,
1620 for (
int i = bcface.i1; i <= bcface.i2; ++i) {
1621 for (
int j = bcface.j1; j <= bcface.j2; ++j) {
1622 for (
int k = bcface.k1; k <= bcface.k2; ++k) {
1623 std::array<int, 3> tmp = {i,j,k};
1624 auto elemIdx = cartesianToCompressedElemIdx[vanguard.cartesianIndex(tmp)];
1631 for (
const auto& bcface : bcconfig) {
1632 std::vector<int>& data = bcindex_(bcface.dir);
1633 const int index = bcface.index;
1634 loopAndApply(bcface,
1635 [&data,index](
int elemIdx)
1636 { data[elemIdx] = index; });
1643 Scalar limitNextTimeStepSize_(Scalar dtNext)
const
1645 if constexpr (enableExperiments) {
1646 const auto& simulator = this->simulator();
1647 const auto& schedule = simulator.vanguard().schedule();
1648 int episodeIdx = simulator.episodeIndex();
1651 Scalar maxTimeStepSize = Parameters::Get<Parameters::SolverMaxTimeStepInDays<Scalar>>() * 24 * 60 * 60;
1652 int reportStepIdx = std::max(episodeIdx, 0);
1653 if (this->enableTuning_) {
1654 const auto& tuning = schedule[reportStepIdx].tuning();
1655 maxTimeStepSize = tuning.TSMAXZ;
1658 dtNext = std::min(dtNext, maxTimeStepSize);
1660 Scalar remainingEpisodeTime =
1661 simulator.episodeStartTime() + simulator.episodeLength()
1662 - (simulator.startTime() + simulator.time());
1663 assert(remainingEpisodeTime >= 0.0);
1667 if (remainingEpisodeTime/2.0 < dtNext && dtNext < remainingEpisodeTime*(1.0 - 1e-5))
1670 dtNext = std::min(maxTimeStepSize, remainingEpisodeTime/2.0);
1672 if (simulator.episodeStarts()) {
1675 const auto& events = simulator.vanguard().schedule()[reportStepIdx].events();
1676 bool wellEventOccured =
1677 events.hasEvent(ScheduleEvents::NEW_WELL)
1678 || events.hasEvent(ScheduleEvents::PRODUCTION_UPDATE)
1679 || events.hasEvent(ScheduleEvents::INJECTION_UPDATE)
1680 || events.hasEvent(ScheduleEvents::WELL_STATUS_CHANGE);
1681 if (episodeIdx >= 0 && wellEventOccured && this->maxTimeStepAfterWellEvent_ > 0)
1682 dtNext = std::min(dtNext, this->maxTimeStepAfterWellEvent_);
1689 int refPressurePhaseIdx_()
const {
1690 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1693 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1697 return waterPhaseIdx;
1701 void updateRockCompTransMultVal_()
1703 const auto& model = this->simulator().model();
1704 std::size_t numGridDof = this->model().numGridDof();
1705 this->rockCompTransMultVal_.resize(numGridDof, 1.0);
1706 for (std::size_t elementIdx = 0; elementIdx < numGridDof; ++elementIdx) {
1707 const auto& iq = *model.cachedIntensiveQuantities(elementIdx, 0);
1709 this->rockCompTransMultVal_[elementIdx] = trans_mult;
1718 template <
class LhsEval>
1721 auto obtain = [](
const auto& value)
1723 if constexpr (std::is_same_v<LhsEval, Scalar>) {
1724 return getValue(value);
1733 template <
class LhsEval,
class Callback>
1736 OPM_TIMEBLOCK_LOCAL(computeRockCompTransMultiplier, Subsystem::PvtProps);
1737 if (this->rockCompTransMult_.empty() && this->rockCompTransMultWc_.empty())
1740 unsigned tableIdx = 0;
1741 if (!this->rockTableIdx_.empty())
1742 tableIdx = this->rockTableIdx_[elementIdx];
1744 const auto& fs = intQuants.fluidState();
1745 LhsEval effectivePressure = obtain(fs.pressure(refPressurePhaseIdx_()));
1746 const auto& rock_config = this->simulator().vanguard().eclState().getSimulationConfig().rock_config();
1747 if (!this->minRefPressure_.empty())
1750 min(obtain(fs.pressure(refPressurePhaseIdx_())),
1751 this->minRefPressure_[elementIdx]);
1753 if (!this->overburdenPressure_.empty())
1754 effectivePressure -= this->overburdenPressure_[elementIdx];
1756 if (rock_config.store()) {
1757 effectivePressure -= asImp_().initialFluidState(elementIdx).pressure(refPressurePhaseIdx_());
1760 if (!this->rockCompTransMult_.empty())
1761 return this->rockCompTransMult_[tableIdx].eval(effectivePressure,
true);
1764 assert(!this->rockCompTransMultWc_.empty());
1765 LhsEval SwMax = max(obtain(fs.saturation(waterPhaseIdx)), this->maxWaterSaturation_[elementIdx]);
1766 LhsEval SwDeltaMax = SwMax - asImp_().initialFluidStates()[elementIdx].saturation(waterPhaseIdx);
1768 return this->rockCompTransMultWc_[tableIdx].eval(effectivePressure, SwDeltaMax,
true);
1771 typename Vanguard::TransmissibilityType transmissibilities_;
1773 std::shared_ptr<EclMaterialLawManager> materialLawManager_;
1774 std::shared_ptr<EclThermalLawManager> thermalLawManager_;
1776 GlobalEqVector drift_;
1778 WellModel wellModel_;
1779 AquiferModel aquiferModel_;
1781 PffGridVector<GridView, Stencil, PffDofData_, DofMapper> pffDofData_;
1782 TracerModel tracerModel_;
1783 TemperatureModel temperatureModel_;
1788 std::array<std::vector<T>,6> data;
1790 void resize(std::size_t size, T defVal)
1792 for (
auto& d : data)
1793 d.resize(size, defVal);
1796 const std::vector<T>& operator()(FaceDir::DirEnum dir)
const
1798 if (dir == FaceDir::DirEnum::Unknown)
1799 throw std::runtime_error(
"Tried to access BC data for the 'Unknown' direction");
1801 int div =
static_cast<int>(dir);
1802 while ((div /= 2) >= 1)
1804 assert(idx >= 0 && idx <= 5);
1808 std::vector<T>& operator()(FaceDir::DirEnum dir)
1810 return const_cast<std::vector<T>&
>(std::as_const(*
this)(dir));
1814 virtual void handleSolventBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1816 virtual void handlePolymerBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1818 virtual void handleMicrBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1820 virtual void handleOxygBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1822 virtual void handleUreaBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1825 bool nonTrivialBoundaryConditions_ =
false;
1826 bool first_step_ =
true;
1832 return this->simulator().episodeWillBeOver();