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());
469 const Scalar
dt = simulator_.timeStepSize();
470 const auto&
ws = simulator_.problem().wellModel().wellState().well(well.name());
471 for (std::size_t i = 0; i <
ws.perf_data.size(); ++i) {
472 const auto I =
ws.perf_data.cell_index[i];
473 const Scalar rate = well.volumetricSurfaceRateForConnection(I,
tr.phaseIdx_);
475 if (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
476 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.vaporized_oil];
478 else if (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
479 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.dissolved_gas];
496 if (
eclWell.isMultiSegment()) {
497 (*mswTracerRate)[
tIdx].rate[
eclWell.getConnections().get(i).segment()] +=
delta;
523 dVol_[Solution][
tr.phaseIdx_][I] -=
rate_s *
dt;
532 void assembleTracerEquationSource(
TrRe&
tr,
536 if (
tr.numTracer() == 0) {
541 if (
tr.phaseIdx_ == FluidSystem::waterPhaseIdx ||
542 (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && !FluidSystem::enableDissolvedGas()) ||
543 (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && !FluidSystem::enableVaporizedOil()))
548 const Scalar&
dsVol = dVol_[Solution][
tr.phaseIdx_][I];
549 const Scalar&
dfVol = dVol_[Free][
tr.phaseIdx_][I];
568 (*
tr.mat)[I][I][Free][Free] -=
delta;
569 (*
tr.mat)[I][I][Solution][Free] +=
delta;
573 (*
tr.mat)[I][I][Free][Solution] +=
delta;
574 (*
tr.mat)[I][I][Solution][Solution] -=
delta;
578 void assembleTracerEquations_()
587 OPM_BEGIN_PARALLEL_TRY_CATCH()
590 for (
auto&
tr : tbatch) {
591 if (
tr.numTracer() != 0) {
599 this->wellTracerRate_.clear();
600 this->wellFreeTracerRate_.clear();
601 this->wellSolTracerRate_.clear();
604 const auto num_msw = this->mSwTracerRate_.size();
605 this->mSwTracerRate_.clear();
608 const auto&
wellPtrs = simulator_.problem().wellModel().localNonshutWells();
609 this->wellTracerRate_.reserve(
wellPtrs.size());
610 this->wellFreeTracerRate_.reserve(
wellPtrs.size());
611 this->wellSolTracerRate_.reserve(
wellPtrs.size());
612 this->mSwTracerRate_.reserve(
num_msw);
614 for (
auto&
tr : tbatch) {
615 this->assembleTracerEquationWell(
tr, *
wellPtr);
621 #pragma omp parallel for
623 for (
const auto&
chunk : element_chunks_) {
624 ElementContext elemCtx(simulator_);
625 const Scalar
dt = elemCtx.simulator().timeStepSize();
628 elemCtx.updateStencil(
elem);
629 const std::size_t I = elemCtx.globalSpaceIndex( 0, 0);
631 if (
elem.partitionType() != Dune::InteriorEntity) {
635 for (
const auto&
tr : tbatch) {
636 if (
tr.numTracer() != 0) {
637 (*
tr.mat)[I][I][0][0] = 1.;
638 (*
tr.mat)[I][I][1][1] = 1.;
643 elemCtx.updateAllIntensiveQuantities();
644 elemCtx.updateAllExtensiveQuantities();
646 const Scalar extrusionFactor =
647 elemCtx.intensiveQuantities( 0, 0).extrusionFactor();
648 Valgrind::CheckDefined(extrusionFactor);
650 assert(extrusionFactor > 0.0);
652 elemCtx.stencil(0).subControlVolume( 0).volume() * extrusionFactor;
653 const std::size_t
I1 = elemCtx.globalSpaceIndex( 0, 1);
657 for (
auto&
tr : tbatch) {
658 if (
tr.numTracer() == 0) {
664 const std::size_t numInteriorFaces = elemCtx.numInteriorFaces(0);
666 const auto&
face = elemCtx.stencil(0).interiorFace(
scvfIdx);
667 const unsigned j =
face.exteriorIndex();
668 const unsigned J = elemCtx.globalSpaceIndex( j, 0);
669 for (
auto&
tr : tbatch) {
670 if (
tr.numTracer() == 0) {
673 this->assembleTracerEquationFlux(
tr, elemCtx,
scvfIdx, I, J,
dt);
678 for (
auto&
tr : tbatch) {
679 if (
tr.numTracer() == 0) {
682 this->assembleTracerEquationSource(
tr,
dt, I);
688 "assembleTracerEquations() failed: ",
689 true, simulator_.gridView().comm())
693 if (
tr.numTracer() == 0) {
697 simulator_.gridView());
698 simulator_.gridView().communicate(handle, Dune::InteriorBorder_All_Interface,
699 Dune::ForwardCommunication);
703 template<TracerTypeIdx Index,
class TrRe>
706 const unsigned globalDofIdx)
710 dVol_[
Index][
tr.phaseIdx_][globalDofIdx] = 0.0;
712 tr.storageOfTimeIndex1_[
tIdx][globalDofIdx][
Index] =
717 void updateStorageCache()
719 for (
auto&
tr : tbatch) {
720 if (
tr.numTracer() != 0) {
721 tr.concentrationInitial_ =
tr.concentration_;
727 #pragma omp parallel for
729 for (
const auto&
chunk : element_chunks_) {
730 ElementContext elemCtx(simulator_);
733 elemCtx.updatePrimaryStencil(
elem);
734 elemCtx.updatePrimaryIntensiveQuantities(0);
735 const Scalar extrusionFactor = elemCtx.intensiveQuantities( 0, 0).extrusionFactor();
736 const Scalar
scvVolume = elemCtx.stencil(0).subControlVolume( 0).volume() * extrusionFactor;
737 const unsigned globalDofIdx = elemCtx.globalSpaceIndex(0, 0);
739 for (
auto&
tr : tbatch) {
740 if (
tr.numTracer() == 0) {
752 template<TracerTypeIdx Index,
class TrRe>
753 void copyForOutput(
TrRe&
tr,
754 const std::vector<TracerVector>&
dx,
757 const unsigned globalDofIdx,
758 std::vector<TracerVectorSingle>&
sc)
763 tr.concentration_[
tIdx][globalDofIdx][
Index] = 0.0;
768 template<TracerTypeIdx Index,
class TrRe>
769 void assignRates(
const TrRe&
tr,
784 if (
eclWell.isMultiSegment()) {
785 (*mswTracerRate)[
tIdx].rate[
eclWell.getConnections().get(i).segment()] +=
delta;
791 void advanceTracerFields()
793 assembleTracerEquations_();
795 for (
auto&
tr : tbatch) {
796 if (
tr.numTracer() == 0) {
802 std::vector<TracerVector>
dx(
tr.concentration_);
807 const bool converged = this->linearSolveBatchwise_(*
tr.mat,
dx,
tr.residual_);
809 OpmLog::warning(
"### Tracer model: Linear solver did not converge. ###");
815 for (std::size_t globalDofIdx = 0; globalDofIdx <
tr.concentration_[
tIdx].size(); ++globalDofIdx) {
818 const auto& intQuants = simulator_.model().intensiveQuantities(globalDofIdx, 0);
819 const auto& fs = intQuants.fluidState();
823 if (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
826 else if (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
836 const auto&
wellPtrs = simulator_.problem().wellModel().localNonshutWells();
847 const std::size_t well_index = simulator_.problem().wellModel().wellState().index(
eclWell.name()).value();
848 const auto&
ws = simulator_.problem().wellModel().wellState().well(well_index);
854 for (std::size_t i = 0; i <
ws.perf_data.size(); ++i) {
855 const auto I =
ws.perf_data.cell_index[i];
856 const Scalar rate =
wellPtr->volumetricSurfaceRateForConnection(I,
tr.phaseIdx_);
859 if (
tr.phaseIdx_ == FluidSystem::oilPhaseIdx && FluidSystem::enableVaporizedOil()) {
860 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.vaporized_oil];
862 else if (
tr.phaseIdx_ == FluidSystem::gasPhaseIdx && FluidSystem::enableDissolvedGas()) {
863 rate_s =
ws.perf_data.phase_mixing_rates[i][
ws.dissolved_gas];
889 simulator_.problem().wellModel().wellState().well(well_index).surface_rates[
tr.phaseIdx_];
894 constexpr Scalar
bucketPrDay = 10.0 / (1000. * 3600. * 24.);
904 Simulator& simulator_;
914 template <
typename TV>
917 std::vector<int> idx_;
919 std::vector<TV> concentrationInitial_;
920 std::vector<TV> concentration_;
921 std::vector<TV> storageOfTimeIndex1_;
922 std::vector<TV> residual_;
923 std::unique_ptr<TracerMatrix> mat;
927 return this->concentrationInitial_ == rhs.concentrationInitial_ &&
928 this->concentration_ == rhs.concentration_;
935 result.concentrationInitial_ = {5.0, 6.0};
936 result.concentration_ = {7.0, 8.0};
937 result.storageOfTimeIndex1_ = {9.0, 10.0, 11.0};
938 result.residual_ = {12.0, 13.0};
943 template<
class Serializer>
952 int numTracer()
const
953 {
return idx_.size(); }
955 void addTracer(
const int idx,
const TV& concentration)
957 const int numGridDof = concentration.size();
958 idx_.emplace_back(
idx);
959 concentrationInitial_.emplace_back(concentration);
960 concentration_.emplace_back(concentration);
961 residual_.emplace_back(numGridDof);
962 storageOfTimeIndex1_.emplace_back(numGridDof);
966 std::array<TracerBatch<TracerVector>,numPhases> tbatch;
970 std::array<std::array<std::vector<Scalar>,numPhases>,2> vol1_;
971 std::array<std::array<std::vector<Scalar>,numPhases>,2> dVol_;