70 GetPropType<TypeTag, Properties::GridView>,
71 GetPropType<TypeTag, Properties::DofMapper>,
72 GetPropType<TypeTag, Properties::Stencil>,
73 GetPropType<TypeTag, Properties::FluidSystem>,
74 GetPropType<TypeTag, Properties::Scalar>>
92 using TracerEvaluation = DenseAd::Evaluation<Scalar,1>;
94 using TracerMatrix =
typename BaseType::TracerMatrix;
95 using TracerVector =
typename BaseType::TracerVector;
96 using TracerVectorSingle =
typename BaseType::TracerVectorSingle;
99 enum { numPhases = FluidSystem::numPhases };
100 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
101 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
102 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
106 :
BaseType(simulator.vanguard().gridView(),
107 simulator.vanguard().eclState(),
108 simulator.vanguard().cartesianIndexMapper(),
109 simulator.model().dofMapper(),
110 simulator.vanguard().cellCentroids())
111 , simulator_(simulator)
112 , tbatch({waterPhaseIdx, oilPhaseIdx, gasPhaseIdx})
140 this->
doInit(rst, simulator_.model().numGridDof(),
141 gasPhaseIdx, oilPhaseIdx, waterPhaseIdx);
144 void prepareTracerBatches()
147 if (this->tracerPhaseIdx_[
tracerIdx] == FluidSystem::waterPhaseIdx) {
148 if (! FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)){
149 throw std::runtime_error(
"Water tracer specified for non-water fluid system: " +
155 else if (this->tracerPhaseIdx_[
tracerIdx] == FluidSystem::oilPhaseIdx) {
156 if (! FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)){
157 throw std::runtime_error(
"Oil tracer specified for non-oil fluid system: " +
163 else if (this->tracerPhaseIdx_[
tracerIdx] == FluidSystem::gasPhaseIdx) {
164 if (! FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)){
165 throw std::runtime_error(
"Gas tracer specified for non-gas fluid system: " +
173 vol1_[0][this->tracerPhaseIdx_[
tracerIdx]].
174 resize(this->freeTracerConcentration_[
tracerIdx].size());
175 vol1_[1][this->tracerPhaseIdx_[
tracerIdx]].
176 resize(this->freeTracerConcentration_[
tracerIdx].size());
177 dVol_[0][this->tracerPhaseIdx_[
tracerIdx]].
178 resize(this->solTracerConcentration_[
tracerIdx].size());
179 dVol_[1][this->tracerPhaseIdx_[
tracerIdx]].
180 resize(this->solTracerConcentration_[
tracerIdx].size());
184 TracerMatrix*
base = this->tracerMatrix_.get();
185 for (
auto&
tr : this->tbatch) {
186 if (
tr.numTracer() != 0) {
187 if (this->tracerMatrix_) {
188 tr.mat = std::move(this->tracerMatrix_);
191 tr.mat = std::make_unique<TracerMatrix>(*
base);
204 updateStorageCache();
217 advanceTracerFields();
224 template <
class Restarter>
234 template <
class Restarter>
238 template<
class Serializer>
247 using BaseType::Free;
248 using BaseType::Solution;
251 template<TracerTypeIdx Index>
253 const unsigned globalDofIdx,
256 const auto& intQuants = simulator_.model().intensiveQuantities(globalDofIdx,
timeIdx);
257 const auto& fs = intQuants.fluidState();
260 if constexpr (Index == Free) {
267 if (
tracerPhaseIdx == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
268 return std::max(
decay<Scalar>(fs.saturation(FluidSystem::gasPhaseIdx)) *
276 else if (
tracerPhaseIdx == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
277 return std::max(
decay<Scalar>(fs.saturation(FluidSystem::oilPhaseIdx)) *
288 template<TracerTypeIdx Index>
289 std::pair<TracerEvaluation, bool>
291 const ElementContext& elemCtx,
295 const auto& stencil = elemCtx.stencil(
timeIdx);
304 if constexpr (
Index == Free) {
306 const auto& intQuants = elemCtx.intensiveQuantities(
upIdx,
timeIdx);
307 const auto& fs = intQuants.fluidState();
311 if (
tracerPhaseIdx == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
314 const auto& intQuants = elemCtx.intensiveQuantities(
upIdx,
timeIdx);
315 const auto& fs = intQuants.fluidState();
321 else if (
tracerPhaseIdx == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
324 const auto& intQuants = elemCtx.intensiveQuantities(
upIdx,
timeIdx);
325 const auto& fs = intQuants.fluidState();
336 const Scalar
A =
scvf.area();
339 : std::pair{
A *
v,
false};
342 template<TracerTypeIdx Index,
class TrRe>
343 Scalar storage1_(
const TrRe&
tr,
358 void assembleTracerEquationVolume(
TrRe&
tr,
359 const ElementContext& elemCtx,
366 if (
tr.numTracer() == 0) {
372 dVol_[Solution][
tr.phaseIdx_][I] +=
sVol.value() *
scvVolume - vol1_[1][
tr.phaseIdx_][I];
373 dVol_[Free][
tr.phaseIdx_][I] +=
fVol.value() *
scvVolume - vol1_[0][
tr.phaseIdx_][I];
394 void assembleTracerEquationFlux(
TrRe&
tr,
395 const ElementContext& elemCtx,
401 if (
tr.numTracer() == 0) {
407 dVol_[Solution][
tr.phaseIdx_][I] +=
sFlux.value() *
dt;
408 dVol_[Free][
tr.phaseIdx_][I] +=
fFlux.value() *
dt;
419 (*
tr.mat)[J][I][Free][Free] = -
fFlux.derivative(0);
420 (*
tr.mat)[I][I][Free][Free] +=
fFlux.derivative(0);
423 (*
tr.mat)[J][I][Solution][Solution] = -
sFlux.derivative(0);
424 (*
tr.mat)[I][I][Solution][Solution] +=
sFlux.derivative(0);
428 template<
class TrRe,
class Well>
429 void assembleTracerEquationWell(
TrRe&
tr,
432 if (
tr.numTracer() == 0) {
436 const auto&
eclWell = well.wellEcl();
446 ? &this->mSwTracerRate_[
eclWell.seqIndex()]
455 if (
eclWell.isMultiSegment()) {
457 wtr.rate.reserve(
eclWell.getConnections().size());
458 for (std::size_t i = 0; i <
eclWell.getConnections().size(); ++i) {
459 wtr.rate.emplace(
eclWell.getConnections().get(i).segment(), 0.0);
464 std::vector<Scalar>
wtracer(
tr.numTracer());
467 simulator_.problem().wellModel().summaryState());
470 const Scalar
dt = simulator_.timeStepSize();
471 const auto&
ws = simulator_.problem().wellModel().wellState().well(well.name());
472 for (std::size_t i = 0; i <
ws.perf_data.size(); ++i) {
473 const auto I =
ws.perf_data.cell_index[i];
474 const Scalar rate = well.volumetricSurfaceRateForConnection(I,
tr.phaseIdx_);
476 if (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
477 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.vaporized_oil];
479 else if (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
480 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.dissolved_gas];
497 if (
eclWell.isMultiSegment()) {
498 (*mswTracerRate)[
tIdx].rate[
eclWell.getConnections().get(i).segment()] +=
delta;
524 dVol_[Solution][
tr.phaseIdx_][I] -=
rate_s *
dt;
533 void assembleTracerEquationSource(
TrRe&
tr,
537 if (
tr.numTracer() == 0) {
542 if (
tr.phaseIdx_ == FluidSystem::waterPhaseIdx ||
543 (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && !FluidSystem::enableDissolvedGas()) ||
544 (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && !FluidSystem::enableVaporizedOil()))
549 const Scalar&
dsVol = dVol_[Solution][
tr.phaseIdx_][I];
550 const Scalar&
dfVol = dVol_[Free][
tr.phaseIdx_][I];
569 (*
tr.mat)[I][I][Free][Free] -=
delta;
570 (*
tr.mat)[I][I][Solution][Free] +=
delta;
574 (*
tr.mat)[I][I][Free][Solution] +=
delta;
575 (*
tr.mat)[I][I][Solution][Solution] -=
delta;
579 void assembleTracerEquations_()
588 OPM_BEGIN_PARALLEL_TRY_CATCH()
591 for (
auto&
tr : tbatch) {
592 if (
tr.numTracer() != 0) {
600 this->wellTracerRate_.clear();
601 this->wellFreeTracerRate_.clear();
602 this->wellSolTracerRate_.clear();
605 const auto num_msw = this->mSwTracerRate_.size();
606 this->mSwTracerRate_.clear();
609 const auto&
wellPtrs = simulator_.problem().wellModel().localNonshutWells();
610 this->wellTracerRate_.reserve(
wellPtrs.size());
611 this->wellFreeTracerRate_.reserve(
wellPtrs.size());
612 this->wellSolTracerRate_.reserve(
wellPtrs.size());
613 this->mSwTracerRate_.reserve(
num_msw);
615 for (
auto&
tr : tbatch) {
616 this->assembleTracerEquationWell(
tr, *
wellPtr);
622 #pragma omp parallel for
624 for (
const auto&
chunk : element_chunks_) {
625 ElementContext elemCtx(simulator_);
626 const Scalar
dt = elemCtx.simulator().timeStepSize();
629 elemCtx.updateStencil(
elem);
630 const std::size_t I = elemCtx.globalSpaceIndex( 0, 0);
632 if (
elem.partitionType() != Dune::InteriorEntity) {
636 for (
const auto&
tr : tbatch) {
637 if (
tr.numTracer() != 0) {
638 (*
tr.mat)[I][I][0][0] = 1.;
639 (*
tr.mat)[I][I][1][1] = 1.;
644 elemCtx.updateAllIntensiveQuantities();
645 elemCtx.updateAllExtensiveQuantities();
647 const Scalar extrusionFactor =
648 elemCtx.intensiveQuantities( 0, 0).extrusionFactor();
649 Valgrind::CheckDefined(extrusionFactor);
651 assert(extrusionFactor > 0.0);
653 elemCtx.stencil(0).subControlVolume( 0).volume() * extrusionFactor;
654 const std::size_t
I1 = elemCtx.globalSpaceIndex( 0, 1);
658 for (
auto&
tr : tbatch) {
659 if (
tr.numTracer() == 0) {
665 const std::size_t numInteriorFaces = elemCtx.numInteriorFaces(0);
667 const auto&
face = elemCtx.stencil(0).interiorFace(
scvfIdx);
668 const unsigned j =
face.exteriorIndex();
669 const unsigned J = elemCtx.globalSpaceIndex( j, 0);
670 for (
auto&
tr : tbatch) {
671 if (
tr.numTracer() == 0) {
674 this->assembleTracerEquationFlux(
tr, elemCtx,
scvfIdx, I, J,
dt);
679 for (
auto&
tr : tbatch) {
680 if (
tr.numTracer() == 0) {
683 this->assembleTracerEquationSource(
tr,
dt, I);
689 "assembleTracerEquations() failed: ",
690 true, simulator_.gridView().comm())
694 if (
tr.numTracer() == 0) {
698 simulator_.gridView());
699 simulator_.gridView().communicate(handle, Dune::InteriorBorder_All_Interface,
700 Dune::ForwardCommunication);
704 template<TracerTypeIdx Index,
class TrRe>
707 const unsigned globalDofIdx)
711 dVol_[
Index][
tr.phaseIdx_][globalDofIdx] = 0.0;
713 tr.storageOfTimeIndex1_[
tIdx][globalDofIdx][
Index] =
718 void updateStorageCache()
720 for (
auto&
tr : tbatch) {
721 if (
tr.numTracer() != 0) {
722 tr.concentrationInitial_ =
tr.concentration_;
728 #pragma omp parallel for
730 for (
const auto&
chunk : element_chunks_) {
731 ElementContext elemCtx(simulator_);
734 elemCtx.updatePrimaryStencil(
elem);
735 elemCtx.updatePrimaryIntensiveQuantities(0);
736 const Scalar extrusionFactor = elemCtx.intensiveQuantities( 0, 0).extrusionFactor();
737 const Scalar
scvVolume = elemCtx.stencil(0).subControlVolume( 0).volume() * extrusionFactor;
738 const unsigned globalDofIdx = elemCtx.globalSpaceIndex(0, 0);
740 for (
auto&
tr : tbatch) {
741 if (
tr.numTracer() == 0) {
753 template<TracerTypeIdx Index,
class TrRe>
754 void copyForOutput(
TrRe&
tr,
755 const std::vector<TracerVector>&
dx,
758 const unsigned globalDofIdx,
759 std::vector<TracerVectorSingle>&
sc)
764 tr.concentration_[
tIdx][globalDofIdx][
Index] = 0.0;
769 template<TracerTypeIdx Index,
class TrRe>
770 void assignRates(
const TrRe&
tr,
785 if (
eclWell.isMultiSegment()) {
786 (*mswTracerRate)[
tIdx].rate[
eclWell.getConnections().get(i).segment()] +=
delta;
792 void advanceTracerFields()
794 assembleTracerEquations_();
796 for (
auto&
tr : tbatch) {
797 if (
tr.numTracer() == 0) {
803 std::vector<TracerVector>
dx(
tr.concentration_);
808 const bool converged = this->linearSolveBatchwise_(*
tr.mat,
dx,
tr.residual_);
810 OpmLog::warning(
"### Tracer model: Linear solver did not converge. ###");
816 for (std::size_t globalDofIdx = 0; globalDofIdx <
tr.concentration_[
tIdx].size(); ++globalDofIdx) {
819 const auto& intQuants = simulator_.model().intensiveQuantities(globalDofIdx, 0);
820 const auto& fs = intQuants.fluidState();
824 if (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
827 else if (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
837 const auto&
wellPtrs = simulator_.problem().wellModel().localNonshutWells();
848 const std::size_t well_index = simulator_.problem().wellModel().wellState().index(
eclWell.name()).value();
849 const auto&
ws = simulator_.problem().wellModel().wellState().well(well_index);
855 for (std::size_t i = 0; i <
ws.perf_data.size(); ++i) {
856 const auto I =
ws.perf_data.cell_index[i];
857 const Scalar rate =
wellPtr->volumetricSurfaceRateForConnection(I,
tr.phaseIdx_);
860 if (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
861 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.vaporized_oil];
863 else if (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
864 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.dissolved_gas];
890 simulator_.problem().wellModel().wellState().well(well_index).surface_rates[
tr.phaseIdx_];
895 constexpr Scalar
bucketPrDay = 10.0 / (1000. * 3600. * 24.);
905 Simulator& simulator_;
915 template <
typename TV>
918 std::vector<int> idx_;
920 std::vector<TV> concentrationInitial_;
921 std::vector<TV> concentration_;
922 std::vector<TV> storageOfTimeIndex1_;
923 std::vector<TV> residual_;
924 std::unique_ptr<TracerMatrix> mat;
928 return this->concentrationInitial_ == rhs.concentrationInitial_ &&
929 this->concentration_ == rhs.concentration_;
936 result.concentrationInitial_ = {5.0, 6.0};
937 result.concentration_ = {7.0, 8.0};
938 result.storageOfTimeIndex1_ = {9.0, 10.0, 11.0};
939 result.residual_ = {12.0, 13.0};
944 template<
class Serializer>
953 int numTracer()
const
954 {
return idx_.size(); }
956 void addTracer(
const int idx,
const TV& concentration)
958 const int numGridDof = concentration.size();
959 idx_.emplace_back(
idx);
960 concentrationInitial_.emplace_back(concentration);
961 concentration_.emplace_back(concentration);
962 residual_.emplace_back(numGridDof);
963 storageOfTimeIndex1_.emplace_back(numGridDof);
967 std::array<TracerBatch<TracerVector>,numPhases> tbatch;
971 std::array<std::array<std::vector<Scalar>,numPhases>,2> vol1_;
972 std::array<std::array<std::vector<Scalar>,numPhases>,2> dVol_;