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>>;
187 ParentType::registerParameters();
189 registerFlowProblemParameters<Scalar>();
202 const std::string&)> addKey,
203 std::set<std::string>& seenParams,
204 std::string& errorMsg,
210 return detail::eclPositionalParameter(addKey,
221 : ParentType(simulator)
222 , BaseType(simulator.vanguard().eclState(),
223 simulator.vanguard().schedule(),
224 simulator.vanguard().gridView())
225 , transmissibilities_(simulator.vanguard().eclState(),
226 simulator.vanguard().gridView(),
227 simulator.vanguard().cartesianIndexMapper(),
228 simulator.vanguard().grid(),
229 simulator.vanguard().cellCentroids(),
230 (energyModuleType == EnergyModules::FullyImplicitThermal ||
231 energyModuleType == EnergyModules::SequentialImplicitThermal), enableDiffusion,
233 , wellModel_(simulator)
234 , aquiferModel_(simulator)
235 , pffDofData_(simulator.gridView(), this->elementMapper())
236 , tracerModel_(simulator)
237 , temperatureModel_(simulator)
244 relpermDiagnostics.
diagnosis(simulator.vanguard().eclState(),
245 simulator.vanguard().levelCartesianIndexMapper());
248 if (energyModuleType == EnergyModules::SequentialImplicitThermal) {
256 void prefetch(
const Element& elem)
const
257 { this->pffDofData_.prefetch(elem); }
270 template <
class Restarter>
277 wellModel_.deserialize(res);
280 aquiferModel_.deserialize(res);
289 template <
class Restarter>
292 wellModel_.serialize(res);
294 aquiferModel_.serialize(res);
297 int episodeIndex()
const
299 return std::max(this->simulator().episodeIndex(), 0);
309 auto& simulator = this->simulator();
310 int episodeIdx = simulator.episodeIndex();
311 auto& eclState = simulator.vanguard().eclState();
312 const auto& schedule = simulator.vanguard().schedule();
313 const auto& events = schedule[episodeIdx].events();
315 if (episodeIdx >= 0 && events.hasEvent(ScheduleEvents::GEO_MODIFIER)) {
322 const auto& miniDeck = schedule[episodeIdx].geo_keywords();
323 const auto& cc = simulator.vanguard().grid().comm();
324 eclState.apply_schedule_keywords( miniDeck );
325 eclBroadcast(cc, eclState.getTransMult() );
328 std::function<
unsigned int(
unsigned int)> equilGridToGrid = [&simulator](
unsigned int i) {
329 return simulator.vanguard().gridEquilIdxToGridIdx(i);
333 using TransUpdateQuantities =
typename Vanguard::TransmissibilityType::TransUpdateQuantities;
334 transmissibilities_.update(
true, TransUpdateQuantities::All, equilGridToGrid);
335 this->referencePorosity_[1] = this->referencePorosity_[0];
336 updateReferencePorosity_();
337 this->rockFraction_[1] = this->rockFraction_[0];
338 updateRockFraction_();
340 this->model().linearizer().updateDiscretizationParameters();
343 bool tuningEvent = this->beginEpisode_(enableExperiments, this->episodeIndex());
346 wellModel_.beginEpisode();
349 aquiferModel_.beginEpisode();
352 Scalar dt = limitNextTimeStepSize_(simulator.episodeLength());
354 if ( (episodeIdx == 0 || tuningEvent) && this->initialTimeStepSize_ > 0)
357 dt = std::min(dt, this->initialTimeStepSize_);
358 simulator.setTimeStepSize(dt);
367 const int episodeIdx = this->episodeIndex();
368 const int timeStepSize = this->simulator().timeStepSize();
370 this->beginTimeStep_(enableExperiments,
372 this->simulator().timeStepIndex(),
373 this->simulator().startTime(),
374 this->simulator().time(),
376 this->simulator().endTime());
381 this->updateExplicitQuantities_(episodeIdx, timeStepSize, first_step_ && (episodeIdx > 0));
384 if (nonTrivialBoundaryConditions()) {
385 this->model().linearizer().updateBoundaryConditionData();
388 wellModel_.beginTimeStep();
389 aquiferModel_.beginTimeStep();
390 tracerModel_.beginTimeStep();
391 temperatureModel_.beginTimeStep();
401 wellModel_.beginIteration();
402 aquiferModel_.beginIteration();
411 wellModel_.endIteration();
412 aquiferModel_.endIteration();
429 const int rank = this->simulator().gridView().comm().rank();
431 std::cout <<
"checking conservativeness of solution\n";
434 this->model().checkConservativeness(-1,
true);
436 std::cout <<
"solution is sufficiently conservative\n";
441 auto& simulator = this->simulator();
442 simulator.setTimeStepIndex(simulator.timeStepIndex()+1);
444 this->wellModel_.endTimeStep();
445 this->aquiferModel_.endTimeStep();
446 this->tracerModel_.endTimeStep();
449 this->model().linearizer().updateFlowsInfo();
451 if (this->enableDriftCompensation_ || this->enableDriftCompensationTemp_) {
452 OPM_TIMEBLOCK(driftCompansation);
454 const auto& residual = this->model().linearizer().residual();
456 for (
unsigned globalDofIdx = 0; globalDofIdx < residual.size(); globalDofIdx ++) {
457 int sfcdofIdx = simulator.vanguard().gridEquilIdxToGridIdx(globalDofIdx);
458 this->drift_[sfcdofIdx] = residual[sfcdofIdx] * simulator.timeStepSize();
461 this->drift_[sfcdofIdx] *= this->model().dofTotalVolume(sfcdofIdx);
467 if constexpr(energyModuleType == EnergyModules::SequentialImplicitThermal) {
468 this->temperatureModel_.endTimeStep(wellModel_.wellState());
477 const int episodeIdx = this->episodeIndex();
479 this->wellModel_.endEpisode();
480 this->aquiferModel_.endEpisode();
482 const auto& schedule = this->simulator().vanguard().schedule();
485 if (episodeIdx + 1 >=
static_cast<int>(schedule.size()) - 1) {
486 this->simulator().setFinished(
true);
491 this->simulator().startNextEpisode(schedule.stepLength(episodeIdx + 1));
500 OPM_TIMEBLOCK(problemWriteOutput);
506 ParentType::writeOutput(verbose);
513 template <
class Context>
516 unsigned timeIdx)
const
518 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
519 return transmissibilities_.permeability(globalSpaceIdx);
529 {
return transmissibilities_.permeability(globalElemIdx); }
534 template <
class Context>
536 [[maybe_unused]]
unsigned fromDofLocalIdx,
537 unsigned toDofLocalIdx)
const
539 assert(fromDofLocalIdx == 0);
540 return pffDofData_.get(context.element(), toDofLocalIdx).transmissibility;
548 return transmissibilities_.transmissibility(globalCenterElemIdx, globalElemIdx);
554 template <
class Context>
556 [[maybe_unused]]
unsigned fromDofLocalIdx,
557 unsigned toDofLocalIdx)
const
559 assert(fromDofLocalIdx == 0);
560 return *pffDofData_.get(context.element(), toDofLocalIdx).diffusivity;
566 Scalar
diffusivity(
const unsigned globalCellIn,
const unsigned globalCellOut)
const{
567 return transmissibilities_.diffusivity(globalCellIn, globalCellOut);
573 Scalar
dispersivity(
const unsigned globalCellIn,
const unsigned globalCellOut)
const{
574 return transmissibilities_.dispersivity(globalCellIn, globalCellOut);
581 const unsigned boundaryFaceIdx)
const
583 return transmissibilities_.thermalTransmissibilityBoundary(globalSpaceIdx, boundaryFaceIdx);
592 template <
class Context>
594 unsigned boundaryFaceIdx)
const
596 unsigned elemIdx = elemCtx.globalSpaceIndex(0, 0);
597 return transmissibilities_.transmissibilityBoundary(elemIdx, boundaryFaceIdx);
604 const unsigned boundaryFaceIdx)
const
606 return transmissibilities_.transmissibilityBoundary(globalSpaceIdx, boundaryFaceIdx);
614 const unsigned globalSpaceIdxOut)
const
616 return transmissibilities_.thermalHalfTrans(globalSpaceIdxIn,globalSpaceIdxOut);
622 template <
class Context>
625 unsigned timeIdx)
const
627 const auto& face = context.stencil(timeIdx).interiorFace(faceIdx);
628 unsigned toDofLocalIdx = face.exteriorIndex();
629 return *pffDofData_.get(context.element(), toDofLocalIdx).thermalHalfTransIn;
635 template <
class Context>
638 unsigned timeIdx)
const
640 const auto& face = context.stencil(timeIdx).interiorFace(faceIdx);
641 unsigned toDofLocalIdx = face.exteriorIndex();
642 return *pffDofData_.get(context.element(), toDofLocalIdx).thermalHalfTransOut;
648 template <
class Context>
650 unsigned boundaryFaceIdx)
const
652 unsigned elemIdx = elemCtx.globalSpaceIndex(0, 0);
653 return transmissibilities_.thermalHalfTransBoundary(elemIdx, boundaryFaceIdx);
660 {
return transmissibilities_; }
664 {
return tracerModel_; }
666 TracerModel& tracerModel()
667 {
return tracerModel_; }
669 TemperatureModel& temperatureModel()
670 {
return temperatureModel_; }
680 template <
class Context>
681 Scalar
porosity(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
683 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
684 return this->
porosity(globalSpaceIdx, timeIdx);
693 template <
class Context>
694 Scalar
dofCenterDepth(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
696 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
708 return this->simulator().vanguard().cellCenterDepth(globalSpaceIdx);
714 template <
class Context>
717 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
724 template <
class Context>
725 Scalar
rockBiotComp(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
727 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
734 template <
class Context>
735 Scalar
lame(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
737 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
738 return this->
lame(globalSpaceIdx);
744 template <
class Context>
745 Scalar
biotCoeff(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
747 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
754 template <
class Context>
757 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
766 const auto& rock_config = this->simulator().vanguard().eclState().getSimulationConfig().rock_config();
767 if (rock_config.store()) {
768 return asImp_().initialFluidState(globalSpaceIdx).pressure(refPressurePhaseIdx_());
771 if (this->rockParams_.empty())
774 unsigned tableIdx = 0;
775 if (!this->rockTableIdx_.empty()) {
776 tableIdx = this->rockTableIdx_[globalSpaceIdx];
778 return this->rockParams_[tableIdx].referencePressure;
785 template <
class Context>
787 unsigned spaceIdx,
unsigned timeIdx)
const
789 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
795 return materialLawManager_->materialLawParams(globalDofIdx);
798 const MaterialLawParams&
materialLawParams(
unsigned globalDofIdx, FaceDir::DirEnum facedir)
const
800 return materialLawManager_->materialLawParams(globalDofIdx, facedir);
806 template <
class Context>
807 const SolidEnergyLawParams&
810 unsigned timeIdx)
const
812 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
813 return thermalLawManager_->solidEnergyLawParams(globalSpaceIdx);
819 template <
class Context>
820 const ThermalConductionLawParams &
823 unsigned globalSpaceIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
824 return thermalLawManager_->thermalConductionLawParams(globalSpaceIdx);
834 {
return materialLawManager_; }
836 template <
class FluidState,
class ...Args>
838 std::array<Evaluation,numPhases> &mobility,
839 DirectionalMobilityPtr &dirMob,
840 FluidState &fluidState,
841 unsigned globalSpaceIdx)
const
843 using ContainerT = std::array<Evaluation, numPhases>;
844 OPM_TIMEBLOCK_LOCAL(updateRelperms, Subsystem::SatProps);
849 MaterialLaw::template relativePermeabilities<ContainerT, FluidState, Args...>(mobility, materialParams, fluidState);
850 Valgrind::CheckDefined(mobility);
852 if (materialLawManager_->hasDirectionalRelperms()
853 || materialLawManager_->hasDirectionalImbnum())
855 using Dir = FaceDir::DirEnum;
856 constexpr int ndim = 3;
857 dirMob = std::make_unique<DirectionalMobility<TypeTag>>();
858 Dir facedirs[ndim] = {Dir::XPlus, Dir::YPlus, Dir::ZPlus};
859 for (
int i = 0; i<ndim; i++) {
861 auto& mob_array = dirMob->getArray(i);
862 MaterialLaw::template relativePermeabilities<ContainerT, FluidState, Args...>(mob_array, materialParams, fluidState);
871 {
return materialLawManager_; }
877 template <
class Context>
878 unsigned pvtRegionIndex(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
879 {
return pvtRegionIndex(context.globalSpaceIndex(spaceIdx, timeIdx)); }
885 template <
class Context>
893 template <
class Context>
901 template <
class Context>
910 template <
class Context>
918 {
return this->simulator().vanguard().caseName(); }
923 template <
class Context>
924 Scalar
temperature(
const Context& context,
unsigned spaceIdx,
unsigned timeIdx)
const
928 unsigned globalDofIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
929 if constexpr (energyModuleType == EnergyModules::SequentialImplicitThermal)
930 return temperatureModel_.temperature(globalDofIdx);
932 return asImp_().initialFluidState(globalDofIdx).temperature(0);
936 Scalar
temperature(
unsigned globalDofIdx,
unsigned )
const
940 if constexpr (energyModuleType == EnergyModules::SequentialImplicitThermal)
941 return temperatureModel_.temperature(globalDofIdx);
943 return asImp_().initialFluidState(globalDofIdx).temperature(0);
946 const SolidEnergyLawParams&
950 return this->thermalLawManager_->solidEnergyLawParams(globalSpaceIdx);
952 const ThermalConductionLawParams &
956 return this->thermalLawManager_->thermalConductionLawParams(globalSpaceIdx);
970 if (!this->vapparsActive(this->episodeIndex()))
973 return this->maxOilSaturation_[globalDofIdx];
987 if (!this->vapparsActive(this->episodeIndex()))
990 this->maxOilSaturation_[globalDofIdx] = value;
999 this->model().invalidateAndUpdateIntensiveQuantities(0);
1005 if (!this->model().enableStorageCache() || !this->recycleFirstIterationStorage()) {
1006 this->model().invalidateAndUpdateIntensiveQuantities(1);
1014 aquiferModel_.initialSolutionApplied();
1016 const bool invalidateFromHyst = updateHysteresis_();
1017 if (invalidateFromHyst) {
1018 OPM_TIMEBLOCK(beginTimeStepInvalidateIntensiveQuantities);
1019 this->model().invalidateAndUpdateIntensiveQuantities(0);
1028 template <
class Context>
1030 const Context& context,
1032 unsigned timeIdx)
const
1034 const unsigned globalDofIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
1035 source(rate, globalDofIdx, timeIdx);
1038 void source(RateVector& rate,
1039 unsigned globalDofIdx,
1040 unsigned timeIdx)
const
1042 OPM_TIMEBLOCK_LOCAL(eclProblemSource, Subsystem::Assembly);
1046 wellModel_.computeTotalRatesForDof(rate, globalDofIdx);
1050 for (
unsigned eqIdx = 0; eqIdx < numEq; ++ eqIdx) {
1051 rate[eqIdx] /= this->model().dofTotalVolume(globalDofIdx);
1053 Valgrind::CheckDefined(rate[eqIdx]);
1054 assert(isfinite(rate[eqIdx]));
1058 addToSourceDense(rate, globalDofIdx, timeIdx);
1061 virtual void addToSourceDense(RateVector& rate,
1062 unsigned globalDofIdx,
1063 unsigned timeIdx)
const = 0;
1071 {
return wellModel_; }
1074 {
return wellModel_; }
1076 const AquiferModel& aquiferModel()
const
1077 {
return aquiferModel_; }
1079 AquiferModel& mutableAquiferModel()
1080 {
return aquiferModel_; }
1082 bool nonTrivialBoundaryConditions()
const
1083 {
return nonTrivialBoundaryConditions_; }
1093 OPM_TIMEBLOCK(nexTimeStepSize);
1095 if (this->nextTimeStepSize_ > 0.0)
1096 return this->nextTimeStepSize_;
1098 const auto& simulator = this->simulator();
1099 int episodeIdx = simulator.episodeIndex();
1103 return this->initialTimeStepSize_;
1108 const auto& newtonMethod = this->model().newtonMethod();
1109 return limitNextTimeStepSize_(newtonMethod.suggestTimeStepSize(simulator.timeStepSize()));
1117 template <
class LhsEval>
1121 if (this->rockCompPoroMult_.empty() && this->rockCompPoroMultWc_.empty())
1124 unsigned tableIdx = 0;
1125 if (!this->rockTableIdx_.empty())
1126 tableIdx = this->rockTableIdx_[elementIdx];
1128 const auto& fs = intQuants.fluidState();
1129 LhsEval effectivePressure = decay<LhsEval>(fs.pressure(refPressurePhaseIdx_()));
1130 const auto& rock_config = this->simulator().vanguard().eclState().getSimulationConfig().rock_config();
1131 if (!this->minRefPressure_.empty())
1134 min(decay<LhsEval>(fs.pressure(refPressurePhaseIdx_())),
1135 this->minRefPressure_[elementIdx]);
1137 if (!this->overburdenPressure_.empty())
1138 effectivePressure -= this->overburdenPressure_[elementIdx];
1140 if (rock_config.store()) {
1141 effectivePressure -= asImp_().initialFluidState(elementIdx).pressure(refPressurePhaseIdx_());
1144 if (!this->rockCompPoroMult_.empty()) {
1145 return this->rockCompPoroMult_[tableIdx].eval(effectivePressure,
true);
1149 assert(!this->rockCompPoroMultWc_.empty());
1150 LhsEval SwMax = max(decay<LhsEval>(fs.saturation(waterPhaseIdx)), this->maxWaterSaturation_[elementIdx]);
1151 LhsEval SwDeltaMax = SwMax - asImp_().initialFluidStates()[elementIdx].saturation(waterPhaseIdx);
1153 return this->rockCompPoroMultWc_[tableIdx].eval(effectivePressure, SwDeltaMax,
true);
1161 template <
class LhsEval>
1164 auto obtain = [](
const auto& value)
1166 if constexpr (std::is_same_v<LhsEval, Scalar>) {
1167 return getValue(value);
1175 template <
class LhsEval,
class Callback>
1176 LhsEval
rockCompTransMultiplier(
const IntensiveQuantities& intQuants,
unsigned elementIdx, Callback& obtain)
const
1178 const bool implicit = !this->explicitRockCompaction_;
1180 : this->simulator().problem().getRockCompTransMultVal(elementIdx);
1183 template <
class LhsEval,
class Callback>
1184 LhsEval wellTransMultiplier(
const IntensiveQuantities& intQuants,
unsigned elementIdx, Callback& obtain)
const
1186 OPM_TIMEBLOCK_LOCAL(wellTransMultiplier, Subsystem::Wells);
1188 const bool implicit = !this->explicitRockCompaction_;
1190 : this->simulator().problem().getRockCompTransMultVal(elementIdx);
1191 trans_mult *= this->simulator().problem().template permFactTransMultiplier<LhsEval>(intQuants, elementIdx, obtain);
1196 std::pair<BCType, RateVector> boundaryCondition(
const unsigned int globalSpaceIdx,
const int directionId)
const
1198 OPM_TIMEBLOCK_LOCAL(boundaryCondition, Subsystem::Assembly);
1199 if (!nonTrivialBoundaryConditions_) {
1200 return { BCType::NONE, RateVector(0.0) };
1202 FaceDir::DirEnum dir = FaceDir::FromIntersectionIndex(directionId);
1203 const auto& schedule = this->simulator().vanguard().schedule();
1204 if (bcindex_(dir)[globalSpaceIdx] == 0) {
1205 return { BCType::NONE, RateVector(0.0) };
1207 if (schedule[this->episodeIndex()].bcprop.size() == 0) {
1208 return { BCType::NONE, RateVector(0.0) };
1210 const auto& bc = schedule[this->episodeIndex()].bcprop[bcindex_(dir)[globalSpaceIdx]];
1211 if (bc.bctype!=BCType::RATE) {
1212 return { bc.bctype, RateVector(0.0) };
1215 RateVector rate = 0.0;
1216 switch (bc.component) {
1217 case BCComponent::OIL:
1218 rate[FluidSystem::canonicalToActiveCompIdx(oilCompIdx)] = bc.rate;
1220 case BCComponent::GAS:
1221 rate[FluidSystem::canonicalToActiveCompIdx(gasCompIdx)] = bc.rate;
1223 case BCComponent::WATER:
1224 rate[FluidSystem::canonicalToActiveCompIdx(waterCompIdx)] = bc.rate;
1226 case BCComponent::SOLVENT:
1227 this->handleSolventBC(bc, rate);
1229 case BCComponent::POLYMER:
1230 this->handlePolymerBC(bc, rate);
1232 case BCComponent::MICR:
1233 this->handleMicrBC(bc, rate);
1235 case BCComponent::OXYG:
1236 this->handleOxygBC(bc, rate);
1238 case BCComponent::UREA:
1239 this->handleUreaBC(bc, rate);
1241 case BCComponent::NONE:
1242 throw std::logic_error(
"you need to specify the component when RATE type is set in BC");
1246 return {bc.bctype, rate};
1250 template<
class Serializer>
1251 void serializeOp(Serializer& serializer)
1253 serializer(
static_cast<BaseType&
>(*
this));
1255 serializer(wellModel_);
1256 serializer(aquiferModel_);
1257 serializer(tracerModel_);
1258 serializer(*materialLawManager_);
1261 const GlobalEqVector& drift()
const
1267 Implementation& asImp_()
1268 {
return *
static_cast<Implementation *
>(
this); }
1270 const Implementation& asImp_()
const
1271 {
return *
static_cast<const Implementation *
>(
this); }
1274 template<
class UpdateFunc>
1275 void updateProperty_(
const std::string& failureMsg,
1278 OPM_TIMEBLOCK(updateProperty);
1279 const auto& model = this->simulator().model();
1280 const auto& primaryVars = model.solution(0);
1281 const auto& vanguard = this->simulator().vanguard();
1282 std::size_t numGridDof = primaryVars.size();
1283 OPM_BEGIN_PARALLEL_TRY_CATCH();
1285#pragma omp parallel for
1287 for (
unsigned dofIdx = 0; dofIdx < numGridDof; ++dofIdx) {
1288 const auto& iq = *model.cachedIntensiveQuantities(dofIdx, 0);
1291 OPM_END_PARALLEL_TRY_CATCH(failureMsg, vanguard.grid().comm());
1294 bool updateMaxOilSaturation_()
1296 OPM_TIMEBLOCK(updateMaxOilSaturation);
1297 int episodeIdx = this->episodeIndex();
1300 if (this->vapparsActive(episodeIdx)) {
1301 this->updateProperty_(
"FlowProblem::updateMaxOilSaturation_() failed:",
1302 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1304 this->updateMaxOilSaturation_(compressedDofIdx,iq);
1312 bool updateMaxOilSaturation_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1314 OPM_TIMEBLOCK_LOCAL(updateMaxOilSaturation, Subsystem::SatProps);
1315 const auto& fs = iq.fluidState();
1316 const Scalar So = decay<Scalar>(fs.saturation(refPressurePhaseIdx_()));
1317 auto& mos = this->maxOilSaturation_;
1318 if(mos[compressedDofIdx] < So){
1319 mos[compressedDofIdx] = So;
1326 bool updateMaxWaterSaturation_()
1328 OPM_TIMEBLOCK(updateMaxWaterSaturation);
1330 if (this->maxWaterSaturation_.empty())
1333 this->maxWaterSaturation_[1] = this->maxWaterSaturation_[0];
1334 this->updateProperty_(
"FlowProblem::updateMaxWaterSaturation_() failed:",
1335 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1337 this->updateMaxWaterSaturation_(compressedDofIdx,iq);
1343 bool updateMaxWaterSaturation_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1345 OPM_TIMEBLOCK_LOCAL(updateMaxWaterSaturation, Subsystem::SatProps);
1346 const auto& fs = iq.fluidState();
1347 const Scalar Sw = decay<Scalar>(fs.saturation(waterPhaseIdx));
1348 auto& mow = this->maxWaterSaturation_;
1349 if(mow[compressedDofIdx]< Sw){
1350 mow[compressedDofIdx] = Sw;
1357 bool updateMinPressure_()
1359 OPM_TIMEBLOCK(updateMinPressure);
1361 if (this->minRefPressure_.empty())
1364 this->updateProperty_(
"FlowProblem::updateMinPressure_() failed:",
1365 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1367 this->updateMinPressure_(compressedDofIdx,iq);
1372 bool updateMinPressure_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq){
1373 OPM_TIMEBLOCK_LOCAL(updateMinPressure, Subsystem::PvtProps);
1374 const auto& fs = iq.fluidState();
1375 const Scalar min_pressure = getValue(fs.pressure(refPressurePhaseIdx_()));
1376 auto& min_pressures = this->minRefPressure_;
1377 if(min_pressures[compressedDofIdx]> min_pressure){
1378 min_pressures[compressedDofIdx] = min_pressure;
1389 std::function<std::vector<double>(
const FieldPropsManager&,
const std::string&)>
1390 fieldPropDoubleOnLeafAssigner_()
1392 const auto& lookup = this->lookUpData_;
1393 return [&lookup](
const FieldPropsManager& fieldPropManager,
const std::string& propString)
1395 return lookup.assignFieldPropsDoubleOnLeaf(fieldPropManager, propString);
1403 template<
typename IntType>
1404 std::function<std::vector<IntType>(
const FieldPropsManager&,
const std::string&,
bool)>
1405 fieldPropIntTypeOnLeafAssigner_()
1407 const auto& lookup = this->lookUpData_;
1408 return [&lookup](
const FieldPropsManager& fieldPropManager,
const std::string& propString,
bool needsTranslation)
1410 return lookup.template assignFieldPropsIntOnLeaf<IntType>(fieldPropManager, propString, needsTranslation);
1414 void readMaterialParameters_()
1416 OPM_TIMEBLOCK(readMaterialParameters);
1417 const auto& simulator = this->simulator();
1418 const auto& vanguard = simulator.vanguard();
1419 const auto& eclState = vanguard.eclState();
1422 OPM_BEGIN_PARALLEL_TRY_CATCH();
1423 this->updatePvtnum_();
1424 this->updateSatnum_();
1427 this->updateMiscnum_();
1429 this->updatePlmixnum_();
1431 OPM_END_PARALLEL_TRY_CATCH(
"Invalid region numbers: ", vanguard.gridView().comm());
1434 updateReferencePorosity_();
1435 this->referencePorosity_[1] = this->referencePorosity_[0];
1440 updateRockFraction_();
1441 this->rockFraction_[1] = this->rockFraction_[0];
1446 materialLawManager_ = std::make_shared<EclMaterialLawManager>();
1447 materialLawManager_->initFromState(eclState);
1448 materialLawManager_->initParamsForElements(eclState, this->model().numGridDof(),
1449 this->
template fieldPropIntTypeOnLeafAssigner_<int>(),
1450 this-> lookupIdxOnLevelZeroAssigner_());
1454 void readThermalParameters_()
1456 if constexpr (energyModuleType == EnergyModules::FullyImplicitThermal ||
1457 energyModuleType == EnergyModules::SequentialImplicitThermal )
1459 const auto& simulator = this->simulator();
1460 const auto& vanguard = simulator.vanguard();
1461 const auto& eclState = vanguard.eclState();
1464 thermalLawManager_ = std::make_shared<EclThermalLawManager>();
1465 thermalLawManager_->initParamsForElements(eclState, this->model().numGridDof(),
1466 this-> fieldPropDoubleOnLeafAssigner_(),
1467 this->
template fieldPropIntTypeOnLeafAssigner_<unsigned int>());
1471 void updateReferencePorosity_()
1473 const auto& simulator = this->simulator();
1474 const auto& vanguard = simulator.vanguard();
1475 const auto& eclState = vanguard.eclState();
1477 std::size_t numDof = this->model().numGridDof();
1479 this->referencePorosity_[0].resize(numDof);
1481 const auto& fp = eclState.fieldProps();
1482 const std::vector<double> porvData =
this -> fieldPropDoubleOnLeafAssigner_()(fp,
"PORV");
1483 for (std::size_t dofIdx = 0; dofIdx < numDof; ++dofIdx) {
1484 int sfcdofIdx = simulator.vanguard().gridEquilIdxToGridIdx(dofIdx);
1485 Scalar poreVolume = porvData[dofIdx];
1490 Scalar dofVolume = simulator.model().dofTotalVolume(sfcdofIdx);
1491 assert(dofVolume > 0.0);
1492 this->referencePorosity_[0][sfcdofIdx] = poreVolume/dofVolume;
1496 void updateRockFraction_() {
1498 const bool solveEnergyEquation = (energyModuleType == EnergyModules::FullyImplicitThermal ||
1499 energyModuleType == EnergyModules::SequentialImplicitThermal);
1500 if (!solveEnergyEquation)
1503 const auto& simulator = this->simulator();
1504 const auto& vanguard = simulator.vanguard();
1505 const auto& eclState = vanguard.eclState();
1507 std::size_t numDof = this->model().numGridDof();
1508 this->rockFraction_[0].resize(numDof);
1517 const auto& fp = eclState.fieldProps();
1518 const std::vector<double> poroData = this->fieldPropDoubleOnLeafAssigner_()(fp,
"PORO");
1519 const std::vector<double> ntgData = this->fieldPropDoubleOnLeafAssigner_()(fp,
"NTG");
1521 for (std::size_t dofIdx = 0; dofIdx < numDof; ++dofIdx) {
1522 const auto ntg = ntgData[dofIdx];
1523 const auto poro_eff = ntg * poroData[dofIdx];
1524 const int sfcdofIdx = simulator.vanguard().gridEquilIdxToGridIdx(dofIdx);
1525 const auto rock_fraction = (1 - poro_eff) * this->referencePorosity_[0][sfcdofIdx] / poro_eff;
1526 this->rockFraction_[0][sfcdofIdx] = rock_fraction;
1530 virtual void readInitialCondition_()
1533 const auto& simulator = this->simulator();
1534 const auto& vanguard = simulator.vanguard();
1535 const auto& eclState = vanguard.eclState();
1537 if (eclState.getInitConfig().hasEquil())
1538 readEquilInitialCondition_();
1540 readExplicitInitialCondition_();
1543 std::size_t numElems = this->model().numGridDof();
1544 for (std::size_t elemIdx = 0; elemIdx < numElems; ++elemIdx) {
1545 const auto& fs = asImp_().initialFluidStates()[elemIdx];
1546 if (!this->maxWaterSaturation_.empty() && waterPhaseIdx > -1)
1547 this->maxWaterSaturation_[elemIdx] = std::max(this->maxWaterSaturation_[elemIdx], fs.saturation(waterPhaseIdx));
1548 if (!this->maxOilSaturation_.empty() && oilPhaseIdx > -1)
1549 this->maxOilSaturation_[elemIdx] = std::max(this->maxOilSaturation_[elemIdx], fs.saturation(oilPhaseIdx));
1550 if (!this->minRefPressure_.empty() && refPressurePhaseIdx_() > -1)
1551 this->minRefPressure_[elemIdx] = std::min(this->minRefPressure_[elemIdx], fs.pressure(refPressurePhaseIdx_()));
1555 virtual void readEquilInitialCondition_() = 0;
1556 virtual void readExplicitInitialCondition_() = 0;
1559 bool updateHysteresis_()
1561 if (!materialLawManager_->enableHysteresis())
1566 this->updateProperty_(
"FlowProblem::updateHysteresis_() failed:",
1567 [
this](
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1569 materialLawManager_->updateHysteresis(iq.fluidState(), compressedDofIdx);
1575 bool updateHysteresis_(
unsigned compressedDofIdx,
const IntensiveQuantities& iq)
1577 OPM_TIMEBLOCK_LOCAL(updateHysteresis_, Subsystem::SatProps);
1578 materialLawManager_->updateHysteresis(iq.fluidState(), compressedDofIdx);
1583 Scalar getRockCompTransMultVal(std::size_t dofIdx)
const
1585 if (this->rockCompTransMultVal_.empty())
1588 return this->rockCompTransMultVal_[dofIdx];
1594 ConditionalStorage<enableFullyImplicitThermal, Scalar> thermalHalfTransIn;
1595 ConditionalStorage<enableFullyImplicitThermal, Scalar> thermalHalfTransOut;
1596 ConditionalStorage<enableDiffusion, Scalar> diffusivity;
1597 ConditionalStorage<enableDispersion, Scalar> dispersivity;
1598 Scalar transmissibility;
1602 void updatePffDofData_()
1604 const auto& distFn =
1606 const Stencil& stencil,
1607 unsigned localDofIdx)
1610 const auto& elementMapper = this->model().elementMapper();
1612 unsigned globalElemIdx = elementMapper.index(stencil.entity(localDofIdx));
1613 if (localDofIdx != 0) {
1614 unsigned globalCenterElemIdx = elementMapper.index(stencil.entity(0));
1615 dofData.transmissibility = transmissibilities_.transmissibility(globalCenterElemIdx, globalElemIdx);
1617 if constexpr (enableFullyImplicitThermal) {
1618 *dofData.thermalHalfTransIn = transmissibilities_.thermalHalfTrans(globalCenterElemIdx, globalElemIdx);
1619 *dofData.thermalHalfTransOut = transmissibilities_.thermalHalfTrans(globalElemIdx, globalCenterElemIdx);
1621 if constexpr (enableDiffusion)
1622 *dofData.diffusivity = transmissibilities_.diffusivity(globalCenterElemIdx, globalElemIdx);
1623 if (enableDispersion)
1624 dofData.dispersivity = transmissibilities_.dispersivity(globalCenterElemIdx, globalElemIdx);
1628 pffDofData_.update(distFn);
1631 virtual void updateExplicitQuantities_(
int episodeIdx,
int timeStepSize,
bool first_step_after_restart) = 0;
1633 void readBoundaryConditions_()
1635 const auto& simulator = this->simulator();
1636 const auto& vanguard = simulator.vanguard();
1637 const auto& bcconfig = vanguard.eclState().getSimulationConfig().bcconfig();
1638 if (bcconfig.size() > 0) {
1639 nonTrivialBoundaryConditions_ =
true;
1641 std::size_t numCartDof = vanguard.cartesianSize();
1642 unsigned numElems = vanguard.gridView().size(0);
1643 std::vector<int> cartesianToCompressedElemIdx(numCartDof, -1);
1645 for (
unsigned elemIdx = 0; elemIdx < numElems; ++elemIdx)
1646 cartesianToCompressedElemIdx[vanguard.cartesianIndex(elemIdx)] = elemIdx;
1648 bcindex_.resize(numElems, 0);
1649 auto loopAndApply = [&cartesianToCompressedElemIdx,
1650 &vanguard](
const auto& bcface,
1653 for (
int i = bcface.i1; i <= bcface.i2; ++i) {
1654 for (
int j = bcface.j1; j <= bcface.j2; ++j) {
1655 for (
int k = bcface.k1; k <= bcface.k2; ++k) {
1656 std::array<int, 3> tmp = {i,j,k};
1657 auto elemIdx = cartesianToCompressedElemIdx[vanguard.cartesianIndex(tmp)];
1664 for (
const auto& bcface : bcconfig) {
1665 std::vector<int>& data = bcindex_(bcface.dir);
1666 const int index = bcface.index;
1667 loopAndApply(bcface,
1668 [&data,index](
int elemIdx)
1669 { data[elemIdx] = index; });
1676 Scalar limitNextTimeStepSize_(Scalar dtNext)
const
1678 if constexpr (enableExperiments) {
1679 const auto& simulator = this->simulator();
1680 const auto& schedule = simulator.vanguard().schedule();
1681 int episodeIdx = simulator.episodeIndex();
1684 Scalar maxTimeStepSize = Parameters::Get<Parameters::SolverMaxTimeStepInDays<Scalar>>() * 24 * 60 * 60;
1685 int reportStepIdx = std::max(episodeIdx, 0);
1686 if (this->enableTuning_) {
1687 const auto& tuning = schedule[reportStepIdx].tuning();
1688 maxTimeStepSize = tuning.TSMAXZ;
1691 dtNext = std::min(dtNext, maxTimeStepSize);
1693 Scalar remainingEpisodeTime =
1694 simulator.episodeStartTime() + simulator.episodeLength()
1695 - (simulator.startTime() + simulator.time());
1696 assert(remainingEpisodeTime >= 0.0);
1700 if (remainingEpisodeTime/2.0 < dtNext && dtNext < remainingEpisodeTime*(1.0 - 1e-5))
1703 dtNext = std::min(maxTimeStepSize, remainingEpisodeTime/2.0);
1705 if (simulator.episodeStarts()) {
1708 const auto& events = simulator.vanguard().schedule()[reportStepIdx].events();
1709 bool wellEventOccured =
1710 events.hasEvent(ScheduleEvents::NEW_WELL)
1711 || events.hasEvent(ScheduleEvents::PRODUCTION_UPDATE)
1712 || events.hasEvent(ScheduleEvents::INJECTION_UPDATE)
1713 || events.hasEvent(ScheduleEvents::WELL_STATUS_CHANGE);
1714 if (episodeIdx >= 0 && wellEventOccured && this->maxTimeStepAfterWellEvent_ > 0)
1715 dtNext = std::min(dtNext, this->maxTimeStepAfterWellEvent_);
1722 int refPressurePhaseIdx_()
const {
1723 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1726 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1730 return waterPhaseIdx;
1734 void updateRockCompTransMultVal_()
1736 const auto& model = this->simulator().model();
1737 std::size_t numGridDof = this->model().numGridDof();
1738 this->rockCompTransMultVal_.resize(numGridDof, 1.0);
1739 for (std::size_t elementIdx = 0; elementIdx < numGridDof; ++elementIdx) {
1740 const auto& iq = *model.cachedIntensiveQuantities(elementIdx, 0);
1742 this->rockCompTransMultVal_[elementIdx] = trans_mult;
1751 template <
class LhsEval>
1754 auto obtain = [](
const auto& value)
1756 if constexpr (std::is_same_v<LhsEval, Scalar>) {
1757 return getValue(value);
1766 template <
class LhsEval,
class Callback>
1769 OPM_TIMEBLOCK_LOCAL(computeRockCompTransMultiplier, Subsystem::PvtProps);
1770 if (this->rockCompTransMult_.empty() && this->rockCompTransMultWc_.empty())
1773 unsigned tableIdx = 0;
1774 if (!this->rockTableIdx_.empty())
1775 tableIdx = this->rockTableIdx_[elementIdx];
1777 const auto& fs = intQuants.fluidState();
1778 LhsEval effectivePressure = obtain(fs.pressure(refPressurePhaseIdx_()));
1779 const auto& rock_config = this->simulator().vanguard().eclState().getSimulationConfig().rock_config();
1780 if (!this->minRefPressure_.empty())
1783 min(obtain(fs.pressure(refPressurePhaseIdx_())),
1784 this->minRefPressure_[elementIdx]);
1786 if (!this->overburdenPressure_.empty())
1787 effectivePressure -= this->overburdenPressure_[elementIdx];
1789 if (rock_config.store()) {
1790 effectivePressure -= asImp_().initialFluidState(elementIdx).pressure(refPressurePhaseIdx_());
1793 if (!this->rockCompTransMult_.empty())
1794 return this->rockCompTransMult_[tableIdx].eval(effectivePressure,
true);
1797 assert(!this->rockCompTransMultWc_.empty());
1798 LhsEval SwMax = max(obtain(fs.saturation(waterPhaseIdx)), this->maxWaterSaturation_[elementIdx]);
1799 LhsEval SwDeltaMax = SwMax - asImp_().initialFluidStates()[elementIdx].saturation(waterPhaseIdx);
1801 return this->rockCompTransMultWc_[tableIdx].eval(effectivePressure, SwDeltaMax,
true);
1804 typename Vanguard::TransmissibilityType transmissibilities_;
1806 std::shared_ptr<EclMaterialLawManager> materialLawManager_;
1807 std::shared_ptr<EclThermalLawManager> thermalLawManager_;
1809 GlobalEqVector drift_;
1811 WellModel wellModel_;
1812 AquiferModel aquiferModel_;
1814 PffGridVector<GridView, Stencil, PffDofData_, DofMapper> pffDofData_;
1815 TracerModel tracerModel_;
1816 TemperatureModel temperatureModel_;
1821 std::array<std::vector<T>,6> data;
1823 void resize(std::size_t size, T defVal)
1825 for (
auto& d : data)
1826 d.resize(size, defVal);
1829 const std::vector<T>& operator()(FaceDir::DirEnum dir)
const
1831 if (dir == FaceDir::DirEnum::Unknown)
1832 throw std::runtime_error(
"Tried to access BC data for the 'Unknown' direction");
1834 int div =
static_cast<int>(dir);
1835 while ((div /= 2) >= 1)
1837 assert(idx >= 0 && idx <= 5);
1841 std::vector<T>& operator()(FaceDir::DirEnum dir)
1843 return const_cast<std::vector<T>&
>(std::as_const(*
this)(dir));
1847 virtual void handleSolventBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1849 virtual void handlePolymerBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1851 virtual void handleMicrBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1853 virtual void handleOxygBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1855 virtual void handleUreaBC(
const BCProp::BCFace&, RateVector&)
const = 0;
1858 bool nonTrivialBoundaryConditions_ =
false;
1859 bool first_step_ =
true;
1865 return this->simulator().episodeWillBeOver();