Line | Branch | Exec | Source |
---|---|---|---|
1 | // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- | ||
2 | // vi: set et ts=4 sw=4 sts=4: | ||
3 | // | ||
4 | // SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder | ||
5 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
6 | // | ||
7 | #ifndef DUMUX_MICP_COLUMN_SIMPLE_CHEM_PROBLEM_HH | ||
8 | #define DUMUX_MICP_COLUMN_SIMPLE_CHEM_PROBLEM_HH | ||
9 | |||
10 | // ## Initial and boundary conditions (`problem.hh`) | ||
11 | // | ||
12 | // This file contains the __problem class__ which defines the initial and boundary | ||
13 | // conditions for the biominearalization example. | ||
14 | // | ||
15 | // [[content]] | ||
16 | // | ||
17 | // ### Include files | ||
18 | // [[codeblock]] | ||
19 | // This header contains the PorousMediumFlowProblem class | ||
20 | #include <dumux/porousmediumflow/problem.hh> | ||
21 | #include <dumux/common/boundarytypes.hh> // for `BoundaryTypes` | ||
22 | #include <dumux/common/properties.hh> // GetPropType | ||
23 | #include <dumux/common/numeqvector.hh> // for `NumEqVector` | ||
24 | #include <dumux/material/components/ammonia.hh> | ||
25 | #include <dumux/discretization/evalgradients.hh> | ||
26 | #include <dumux/io/container.hh> | ||
27 | #include <algorithm> // for std::transform | ||
28 | // [[/codeblock]] | ||
29 | |||
30 | // ### The problem class | ||
31 | // The problem class defines the boundary and initial conditions. | ||
32 | // As this is a biomineralization problem in porous media, we inherit from the porous-media-flow problem | ||
33 | |||
34 | // [[codeblock]] | ||
35 | namespace Dumux { | ||
36 | |||
37 | template <class TypeTag> | ||
38 | class MICPColumnProblemSimpleChemistry : public PorousMediumFlowProblem<TypeTag> | ||
39 | { | ||
40 | using ParentType = PorousMediumFlowProblem<TypeTag>; | ||
41 | using Scalar = GetPropType<TypeTag, Properties::Scalar>; | ||
42 | using NH3 = Components::Ammonia<Scalar>; // for molar mass of Ammonia | ||
43 | using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>; | ||
44 | using SolidSystem = GetPropType<TypeTag, Properties::SolidSystem>; | ||
45 | using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>; | ||
46 | using FluxVariables = GetPropType<TypeTag, Properties::FluxVariables>; | ||
47 | using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; | ||
48 | |||
49 | // We define some indices for convenience to be used later when defining the initial and boundary conditions | ||
50 | enum { | ||
51 | numComponents = FluidSystem::numComponents, | ||
52 | |||
53 | pressureIdx = Indices::pressureIdx, | ||
54 | switchIdx = Indices::switchIdx, //Saturation | ||
55 | xwNaIdx = FluidSystem::NaIdx, | ||
56 | xwClIdx = FluidSystem::ClIdx, | ||
57 | xwCaIdx = FluidSystem::CaIdx, | ||
58 | xwUreaIdx = FluidSystem::UreaIdx, | ||
59 | xwO2Idx = FluidSystem::O2Idx, | ||
60 | xwBiosubIdx = FluidSystem::GlucoseIdx, | ||
61 | xwSuspendedBiomassIdx = FluidSystem::SuspendedBiomassIdx, | ||
62 | phiBiofilmIdx = numComponents, | ||
63 | phiCalciteIdx = numComponents + 1, | ||
64 | |||
65 | //Indices of the components | ||
66 | wCompIdx = FluidSystem::wCompIdx, | ||
67 | nCompIdx = FluidSystem::nCompIdx, | ||
68 | NaIdx = FluidSystem::NaIdx, | ||
69 | ClIdx = FluidSystem::ClIdx, | ||
70 | CaIdx = FluidSystem::CaIdx, | ||
71 | UreaIdx = FluidSystem::UreaIdx, | ||
72 | O2Idx = FluidSystem::O2Idx, | ||
73 | BiosubIdx = FluidSystem::GlucoseIdx, | ||
74 | SuspendedBiomassIdx = FluidSystem::SuspendedBiomassIdx, | ||
75 | |||
76 | wPhaseIdx = FluidSystem::wPhaseIdx, | ||
77 | conti0EqIdx = Indices::conti0EqIdx, | ||
78 | |||
79 | // Phase State | ||
80 | wPhaseOnly = Indices::firstPhaseOnly, | ||
81 | bothPhases = Indices::bothPhases | ||
82 | }; | ||
83 | |||
84 | using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; | ||
85 | using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; | ||
86 | using BoundaryTypes = Dumux::BoundaryTypes<PrimaryVariables::size()>; | ||
87 | using ElementVolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::LocalView; | ||
88 | using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; | ||
89 | using GridView = typename GridGeometry::GridView; | ||
90 | using Element = typename GridView::template Codim<0>::Entity; | ||
91 | using FVElementGeometry = typename GridGeometry::LocalView; | ||
92 | using SubControlVolume = typename GridGeometry::SubControlVolume; | ||
93 | using GlobalPosition = typename Element::Geometry::GlobalCoordinate; | ||
94 | static constexpr int dim = GridView::dimension; | ||
95 | // [[/codeblock]] | ||
96 | // | ||
97 | // We define an enum class to distinguish between different injection processes. | ||
98 | // We will use these later in the definition of the Neumann boundary conditions. | ||
99 | enum class InjectionProcess : int | ||
100 | { | ||
101 | noInjection = -99, | ||
102 | noFlow = -9, | ||
103 | rinse = -1, | ||
104 | resuscitation = 3, | ||
105 | inoculation = 2, | ||
106 | mineralization = 1 | ||
107 | }; | ||
108 | // | ||
109 | // ### Reading of parameters specified in the params.input file | ||
110 | // [[details]] parameters | ||
111 | // [[codeblock]] | ||
112 | public: | ||
113 | // This is the constructor of our problem class. | ||
114 | 1 | MICPColumnProblemSimpleChemistry(std::shared_ptr<const GridGeometry> gridGeometry) | |
115 |
9/28✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 24 taken 1 times.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
3 | : ParentType(gridGeometry) |
116 | { | ||
117 | // We read the parameters from the params.input file. | ||
118 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
|
1 | name_ = getParam<std::string>("Problem.Name"); |
119 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | temperature_ = getParam<Scalar>("Problem.Temperature"); |
120 | |||
121 | // biomass parameters | ||
122 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ca1_ = getParam<Scalar>("BioCoefficients.Ca1"); |
123 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ca2_ = getParam<Scalar>("BioCoefficients.Ca2"); |
124 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | cd1_ = getParam<Scalar>("BioCoefficients.Cd1"); |
125 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | dc0_ = getParam<Scalar>("BioCoefficients.Dc0"); |
126 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | kmue_ = getParam<Scalar>("BioCoefficients.Kmue"); |
127 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | f_ = getParam<Scalar>("BioCoefficients.F"); |
128 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ke_ = getParam<Scalar>("BioCoefficients.Ke"); |
129 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ks_ = getParam<Scalar>("BioCoefficients.Ks"); |
130 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | yield_ = getParam<Scalar>("BioCoefficients.Yield"); |
131 | |||
132 | // ureolysis kinetic parameters | ||
133 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | kub_ = getParam<Scalar>("UreolysisCoefficients.Kub"); |
134 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | kurease_ = getParam<Scalar>("UreolysisCoefficients.Kurease"); |
135 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ku_ = getParam<Scalar>("UreolysisCoefficients.Ku"); |
136 | |||
137 | // initial values | ||
138 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | densityW_ = getParam<Scalar>("Initial.DensityW"); |
139 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initPressure_ = getParam<Scalar>("Initial.Pressure"); |
140 | |||
141 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwTC_ = getParam<Scalar>("Initial.XwTC"); |
142 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwNa_ = getParam<Scalar>("Initial.XwNa"); |
143 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwCl_ = getParam<Scalar>("Initial.XwCl"); |
144 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwCa_ = getParam<Scalar>("Initial.XwCa"); |
145 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwUrea_ = getParam<Scalar>("Initial.XwUrea"); |
146 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwTNH_ = getParam<Scalar>("Initial.XwTNH"); |
147 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwO2_ = getParam<Scalar>("Initial.XwO2"); |
148 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwBiosub_ = getParam<Scalar>("Initial.XwSubstrate"); |
149 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initxwBiosusp_ = getParam<Scalar>("Initial.XwSuspendedBiomass"); |
150 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initCalcite_ = getParam<Scalar>("Initial.Calcite"); |
151 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | initBiofilm_ = getParam<Scalar>("Initial.Biofilm"); |
152 | |||
153 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | xwNaCorr_ = getParam<Scalar>("Initial.XwNaCorr"); |
154 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | xwClCorr_ = getParam<Scalar>("Initial.XwClCorr"); |
155 | |||
156 | // injection values | ||
157 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injQ_ = getParam<Scalar>("Injection.FlowRate"); |
158 | |||
159 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injTC_ = getParam<Scalar>("Injection.MassFracTC"); |
160 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injNa_ = getParam<Scalar>("Injection.ConcentrationNa"); |
161 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injCa_ = getParam<Scalar>("Injection.ConcentrationCa"); |
162 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injUrea_ = getParam<Scalar>("Injection.ConcentrationUrea"); |
163 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injTNH_ = getParam<Scalar>("Injection.ConcentrationTNH"); |
164 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injO2_ = getParam<Scalar>("Injection.ConcentrationO2"); |
165 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injSub_ = getParam<Scalar>("Injection.ConcentrationSubstrate"); |
166 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injBiosusp_= getParam<Scalar>("Injection.ConcentrationSuspendedBiomass"); |
167 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | injNaCorr_ = getParam<Scalar>("Injection.ConcentrationNaCorr"); |
168 | // [[/codeblock]] | ||
169 | // [[/details]] | ||
170 | // | ||
171 | // ### Reading of the temporal sequence of the different types of injection | ||
172 | // Here, the number of injections and the corresponding parameters are defined based on what is specified in the input files. | ||
173 | // [[codeblock]] | ||
174 | // We get the number of injections and the injection data file name from params.input | ||
175 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | numInjections_ = getParam<int>("Injection.NumInjections"); |
176 | |||
177 | // We resize the permeability vector containing the permeabilities for the additional output | ||
178 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
3 | permeability_.resize(gridGeometry->numDofs()); |
179 | |||
180 | // We read from the injection data file which injection type we have in each episode. | ||
181 | // We will use this in the Neumann boundary condition to set time dependent, changing boundary conditions. | ||
182 | // We do this similarly to the episode ends in the main file. | ||
183 |
3/8✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
3 | const auto injType = readFileToContainer<std::vector<int>>("injection_type.dat"); |
184 | // translate integer to InjectionProcess type | ||
185 | 4 | std::transform(injType.begin(), injType.end(), std::back_inserter(injectionType_), | |
186 | [](int n){ return static_cast<InjectionProcess>(n); }); | ||
187 | |||
188 | |||
189 | // [[/codeblock]] | ||
190 | |||
191 | // We check the injection data against the number of injections specified in the parameter file | ||
192 | // and print an error message if the test fails | ||
193 | // [[codeblock]] | ||
194 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
2 | if (injectionType_.size() != numInjections_) |
195 | ✗ | DUNE_THROW(Dune::IOError, "numInjections from the parameterfile and the number of injection types " | |
196 | << "specified in the injection data file do not match!\n" | ||
197 | << "numInjections from parameter file: " << numInjections_ << "\n" | ||
198 | << "numInjTypes from injection data file: "<< injectionType_.size()); | ||
199 | |||
200 | // Initialize the fluidsystem | ||
201 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
2 | FluidSystem::init(/*startTemp=*/temperature_ -5.0, /*endTemp=*/temperature_ +5.0, /*tempSteps=*/5, |
202 | /*startPressure=*/1e4, /*endPressure=*/1e6, /*pressureSteps=*/500); | ||
203 | 1 | } | |
204 | // [[/codeblock]] | ||
205 | |||
206 | // In the following, functions to set the time, time step size and the index of the episode | ||
207 | // are declared which are used the time loop in main.cc | ||
208 | // [[codeblock]] | ||
209 | ✗ | void setTime(const Scalar t) | |
210 |
1/2✓ Branch 1 taken 275 times.
✗ Branch 2 not taken.
|
275 | { time_ = t; } |
211 | |||
212 | // We need the time step size to regularize the reactive source terms. | ||
213 | ✗ | void setTimeStepSize(const Scalar dt) | |
214 |
1/2✓ Branch 1 taken 275 times.
✗ Branch 2 not taken.
|
275 | { timeStepSize_ = dt; } |
215 | |||
216 | // We need the episode index to choose the right Neumann boundary condition for each episode based on the injection data. | ||
217 | ✗ | void setEpisodeIdx(const Scalar epIdx) | |
218 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
8 | { episodeIdx_ = epIdx; } |
219 | // [[/codeblock]] | ||
220 | |||
221 | // Here, functions to return the injectionType and the name of the problem are defined | ||
222 | // [[codeblock]] | ||
223 | int injectionType(int episodeIdx) const | ||
224 | { return injectionType_[episodeIdx]; } | ||
225 | |||
226 | // Get the problem name. It is used as a prefix for files generated by the simulation. | ||
227 | const std::string& name() const | ||
228 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | { return name_; } |
229 | // [[/codeblock]] | ||
230 | |||
231 | // #### Boundary conditions | ||
232 | // With the following function we define the type of boundary conditions depending on the location. Two types of boundary conditions | ||
233 | // can be specified: Dirichlet or Neumann boundary condition. On a Dirichlet boundary, the values of the | ||
234 | // primary variables need to be fixed. On a Neumann boundary condition, values for derivatives need to be fixed. | ||
235 | |||
236 | // [[codeblock]] | ||
237 | 6950 | BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const | |
238 | { | ||
239 | 6950 | BoundaryTypes bcTypes; | |
240 | // We set all to Neumann, except for the top boundary, which is set to Dirichlet. | ||
241 | 13900 | const Scalar zmax = this->gridGeometry().bBoxMax()[dim - 1]; | |
242 | 6950 | bcTypes.setAllNeumann(); | |
243 |
2/2✓ Branch 0 taken 3475 times.
✓ Branch 1 taken 3475 times.
|
6950 | if (globalPos[dim - 1] > zmax - eps_) |
244 | bcTypes.setAllDirichlet(); | ||
245 | |||
246 | 6950 | return bcTypes; | |
247 | } | ||
248 | // [[/codeblock]] | ||
249 | |||
250 | // We define the Dirichlet boundary conditions | ||
251 | // [[codeblock]] | ||
252 | 1899 | PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const | |
253 | { | ||
254 | 1899 | PrimaryVariables priVars(0.0); | |
255 | 1899 | priVars.setState(wPhaseOnly); | |
256 | // We recycle the initial conditions, but additionally enforce that substrate and oxygen necessary for biomass growth are zero. | ||
257 | 1899 | priVars = initial_(globalPos); | |
258 | 3798 | priVars[xwBiosubIdx] = 0.0; | |
259 | 3798 | priVars[xwO2Idx] = 0.0; | |
260 | 1899 | return priVars; | |
261 | } | ||
262 | // [[/codeblock]] | ||
263 | |||
264 | // The injections can be described with a Neumann boundary condition. | ||
265 | // Since we have multiple injections durng the whole simulation period, the Neumann boundary conditions in this case are time or rather episode depended. | ||
266 | // [[codeblock]] | ||
267 | 36248 | NumEqVector neumannAtPos(const GlobalPosition& globalPos) const | |
268 | { | ||
269 | // We only have injection at the bottom, negative values for injection | ||
270 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36248 times.
|
36248 | if (globalPos[dim - 1] > eps_) |
271 | ✗ | return NumEqVector(0.0); | |
272 | |||
273 | // We calculate the injected water velocity based on the volume flux injected into a 1 inch diameter column. | ||
274 | 36248 | const Scalar diameter = 0.0254; | |
275 | 36248 | const Scalar waterFlux = injQ_/(3.14*diameter*diameter/4.); //[m/s] | |
276 |
2/2✓ Branch 0 taken 26128 times.
✓ Branch 1 taken 10120 times.
|
36248 | const auto injProcess = injectionType_[episodeIdx_]; |
277 | |||
278 | // We also have no-injection, no-flow periods, which are coded for by -99 or 9. | ||
279 | // Thus, for no flow, we set the Neumann BC to zero for all components. | ||
280 |
2/2✓ Branch 0 taken 26128 times.
✓ Branch 1 taken 10120 times.
|
36248 | if (injProcess == InjectionProcess::noInjection || injProcess == InjectionProcess::noFlow) |
281 | 26128 | return NumEqVector(0.0); | |
282 | |||
283 | // We set the injection BC to the basic rinse injection and later only change the BC for those components that are different. | ||
284 | 10120 | NumEqVector values(0.0); | |
285 | |||
286 | 10120 | values[conti0EqIdx + wCompIdx] = -waterFlux * 996/FluidSystem::molarMass(wCompIdx); | |
287 | 10120 | values[conti0EqIdx + nCompIdx] = -waterFlux * injTC_*996 /FluidSystem::molarMass(nCompIdx); | |
288 | 10120 | values[conti0EqIdx + xwCaIdx] = 0; | |
289 | 10120 | values[conti0EqIdx + xwSuspendedBiomassIdx] = 0; | |
290 | 10120 | values[conti0EqIdx + xwBiosubIdx] = -waterFlux * injSub_ /FluidSystem::molarMass(xwBiosubIdx); | |
291 | 10120 | values[conti0EqIdx + xwO2Idx] = -waterFlux * injO2_ /FluidSystem::molarMass(O2Idx); | |
292 | 10120 | values[conti0EqIdx + xwUreaIdx] = 0; | |
293 | 10120 | values[conti0EqIdx + phiCalciteIdx] = 0; | |
294 | 10120 | values[conti0EqIdx + phiBiofilmIdx] = 0; | |
295 | 10120 | values[conti0EqIdx + xwNaIdx] = -waterFlux * (injNa_ + injNaCorr_) /FluidSystem::molarMass(NaIdx); | |
296 | 30360 | values[conti0EqIdx + xwClIdx] = -waterFlux *injTNH_ /NH3::molarMass() //NH4Cl ---> mol Cl = mol NH4 | |
297 |
2/2✓ Branch 1 taken 368 times.
✓ Branch 2 taken 9752 times.
|
10120 | -waterFlux *injNa_ /FluidSystem::molarMass(NaIdx); //NaCl ---> mol Cl = mol Na |
298 | // rinse, used as standard injection fluid | ||
299 |
2/2✓ Branch 0 taken 368 times.
✓ Branch 1 taken 9752 times.
|
10120 | if (injProcess == InjectionProcess::rinse) |
300 | { | ||
301 | 368 | return values; // do not change anything. | |
302 | } | ||
303 | |||
304 | // injProcess == 1 codes for an injection of mineralization medium containing urea and calcium chloride. | ||
305 | // Thus, we add BC terms for those components. | ||
306 | // Additionally, we need to adjust the amount of water injected due to the high concentration of other components injected. | ||
307 | // Finally, we need to adapt the injected NaCorr concentration to account for the lower pH. | ||
308 |
2/2✓ Branch 0 taken 345 times.
✓ Branch 1 taken 9407 times.
|
9752 | else if (injProcess == InjectionProcess::mineralization) |
309 | { | ||
310 | 345 | values[conti0EqIdx + wCompIdx] = - waterFlux * 0.8716 * densityW_ /FluidSystem::molarMass(wCompIdx); //0.8716 factor accounts for less water per volume due to relatively high solute concentrations! | |
311 | 345 | values[conti0EqIdx + nCompIdx] = - waterFlux * injTC_ * densityW_ /FluidSystem::molarMass(nCompIdx); | |
312 | 345 | values[conti0EqIdx + xwCaIdx] = - waterFlux * injCa_/FluidSystem::molarMass(CaIdx); | |
313 | 345 | values[conti0EqIdx + xwUreaIdx] = - waterFlux * injUrea_ /FluidSystem::molarMass(UreaIdx); | |
314 | 345 | values[conti0EqIdx + xwNaIdx] = - waterFlux * injNa_ /FluidSystem::molarMass(NaIdx) | |
315 | 345 | - waterFlux * injNaCorr_ /FluidSystem::molarMass(NaIdx)* 0.032; | |
316 | 1035 | values[conti0EqIdx + xwClIdx] = - waterFlux * injTNH_ /NH3::molarMass() //NH4Cl ---> mol Cl = mol NH4 | |
317 | 345 | - waterFlux * 2 * injCa_/FluidSystem::molarMass(CaIdx) //+CaCl2 ---> mol Cl = mol Ca*2 | |
318 | 345 | -waterFlux *injNa_ /FluidSystem::molarMass(NaIdx); //NaCl ---> mol Cl = mol Na | |
319 | 345 | return values; | |
320 | } | ||
321 | |||
322 | // injProcess == 3 codes for a resuscitation injection to regrow biomass. | ||
323 | // It is similar to a rinse injection, but with added urea, which is what we add to the basic rinse | ||
324 |
2/2✓ Branch 0 taken 8740 times.
✓ Branch 1 taken 667 times.
|
9407 | else if (injProcess == InjectionProcess::resuscitation ) |
325 | { | ||
326 | 8740 | values[conti0EqIdx + xwUreaIdx] = - waterFlux * injUrea_ /FluidSystem::molarMass(UreaIdx); | |
327 | 8740 | return values; | |
328 | } | ||
329 | |||
330 | // injProcess == 2 codes for a inoculation or biomass injection. | ||
331 | // It is similar to a rinse injection, but with added suspended biomass, which is what we add to the basic rinse | ||
332 |
1/2✓ Branch 0 taken 667 times.
✗ Branch 1 not taken.
|
667 | else if (injProcess == InjectionProcess::inoculation) |
333 | { | ||
334 | 667 | values[conti0EqIdx + xwSuspendedBiomassIdx] = -waterFlux * injBiosusp_ /FluidSystem::molarMass(xwSuspendedBiomassIdx); | |
335 | 667 | return values; | |
336 | } | ||
337 | else | ||
338 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid injection process " << static_cast<int>(injProcess)); | |
339 | } | ||
340 | // [[/codeblock]] | ||
341 | |||
342 | // #### Initial conditions | ||
343 | // We specify the initial conditions for the primary variable depending | ||
344 | // on the location. Here, we set zero model fractions everywhere in the domain except for a strip | ||
345 | // at the bottom of the domain where we set an initial mole fraction of $`1e-9`$. | ||
346 | // [[codeblock]] | ||
347 | PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const | ||
348 | 57 | { return initial_(globalPos); } | |
349 | |||
350 | // [[/codeblock]] | ||
351 | |||
352 | // #### Reactive source and sink terms | ||
353 | // We calculate the reactive source and sink terms. | ||
354 | // For the details, see the "simplified chemistry case" in the dissertation of Hommel available at https://elib.uni-stuttgart.de/handle/11682/8787 | ||
355 | // [[codeblock]] | ||
356 | 4059776 | NumEqVector source(const Element &element, | |
357 | const FVElementGeometry& fvGeometry, | ||
358 | const ElementVolumeVariables& elemVolVars, | ||
359 | const SubControlVolume &scv) const | ||
360 | { | ||
361 | 4059776 | const auto elemSol = elementSolution(element, elemVolVars, fvGeometry); | |
362 | 12179328 | const auto gradPw = evalGradients(element, | |
363 | element.geometry(), | ||
364 | this->gridGeometry(), | ||
365 | elemSol, | ||
366 | 4059776 | scv.center())[pressureIdx]; | |
367 | 4059776 | const Scalar scvPotGradNorm = gradPw.two_norm(); | |
368 | |||
369 | //define and compute some parameters for siplicity: | ||
370 | 8119552 | const Scalar porosity = elemVolVars[scv].porosity(); | |
371 | 4059776 | Scalar initialPorosity = 1.0; | |
372 | 4059776 | constexpr int numActiveComps = SolidSystem::numComponents-SolidSystem::numInertComponents; | |
373 |
2/2✓ Branch 0 taken 4059776 times.
✓ Branch 1 taken 4059776 times.
|
8119552 | for (int i = numActiveComps; i<SolidSystem::numComponents; ++i) |
374 | 12179328 | initialPorosity -= elemVolVars[scv].solidVolumeFraction(i); | |
375 | |||
376 | 8119552 | const Scalar Sw = elemVolVars[scv].saturation(wPhaseIdx); | |
377 | 4059776 | const Scalar xlSalinity = elemVolVars[scv].moleFraction(wPhaseIdx,NaIdx) | |
378 | 12179328 | + elemVolVars[scv].moleFraction(wPhaseIdx,CaIdx) | |
379 | 8119552 | + elemVolVars[scv].moleFraction(wPhaseIdx,ClIdx); | |
380 | 8119552 | const Scalar densityBiofilm = elemVolVars[scv].solidComponentDensity(SolidSystem::BiofilmIdx); | |
381 | 8119552 | const Scalar densityCalcite = elemVolVars[scv].solidComponentDensity(SolidSystem::CalciteIdx); | |
382 | 4059776 | const Scalar cBio = std::max(0.0, elemVolVars[scv].moleFraction(wPhaseIdx, SuspendedBiomassIdx) | |
383 | 12179328 | * elemVolVars[scv].molarDensity(wPhaseIdx) | |
384 |
2/2✓ Branch 1 taken 4022639 times.
✓ Branch 2 taken 37137 times.
|
4059776 | * FluidSystem::molarMass(SuspendedBiomassIdx)); //[kg_suspended_Biomass/m³_waterphase] |
385 |
6/6✓ Branch 0 taken 293728 times.
✓ Branch 1 taken 3766048 times.
✓ Branch 2 taken 293728 times.
✓ Branch 3 taken 3766048 times.
✓ Branch 4 taken 293728 times.
✓ Branch 5 taken 3766048 times.
|
12179328 | const Scalar volFracCalcite = std::max(0.0, elemVolVars[scv].solidVolumeFraction(SolidSystem::CalciteIdx)); |
386 |
6/6✓ Branch 0 taken 4022662 times.
✓ Branch 1 taken 37114 times.
✓ Branch 2 taken 4022662 times.
✓ Branch 3 taken 37114 times.
✓ Branch 4 taken 4022662 times.
✓ Branch 5 taken 37114 times.
|
12179328 | const Scalar volFracBiofilm = std::max(0.0, elemVolVars[scv].solidVolumeFraction(SolidSystem::BiofilmIdx)); |
387 | 4059776 | const Scalar massBiofilm = densityBiofilm * volFracBiofilm; | |
388 | 4059776 | const Scalar cSubstrate = std::max(0.0, elemVolVars[scv].moleFraction(wPhaseIdx, BiosubIdx) | |
389 | 12179328 | * elemVolVars[scv].molarDensity(wPhaseIdx) | |
390 |
2/2✓ Branch 1 taken 4025170 times.
✓ Branch 2 taken 34606 times.
|
4059776 | * FluidSystem::molarMass(BiosubIdx)); //[kg_substrate/m³_waterphase] |
391 | 4059776 | const Scalar cO2 = std::max(0.0, elemVolVars[scv].moleFraction(wPhaseIdx, O2Idx) | |
392 | 12179328 | * elemVolVars[scv].molarDensity(wPhaseIdx) | |
393 |
2/2✓ Branch 1 taken 3936839 times.
✓ Branch 2 taken 122937 times.
|
4059776 | * FluidSystem::molarMass(O2Idx)); //[kg_oxygen/m³_waterphase] |
394 | 8119552 | const Scalar mUrea = std::max(0.0, moleFracToMolality(elemVolVars[scv].moleFraction(wPhaseIdx,UreaIdx), | |
395 | xlSalinity, | ||
396 | 8119552 | elemVolVars[scv].moleFraction(wPhaseIdx,nCompIdx))); //[mol_urea/kg_H2O] | |
397 | // compute rate of ureolysis: | ||
398 | 4059776 | const Scalar vmax = kurease_; | |
399 | 4059776 | const Scalar Zub = kub_ * massBiofilm; // [kgurease/m³] | |
400 | 4059776 | const Scalar rurea = vmax * Zub * mUrea / (ku_ + mUrea); //[mol/m³s] | |
401 | |||
402 | // compute precipitation rate of calcite, no dissolution! Simplification: rprec = rurea | ||
403 | // additionally regularize the precipitation rate that we do not precipitate more calcium than available. | ||
404 |
4/4✓ Branch 0 taken 1126645 times.
✓ Branch 1 taken 2933131 times.
✓ Branch 2 taken 1126645 times.
✓ Branch 3 taken 2933131 times.
|
8119552 | const Scalar maxPrecipitationRate = elemVolVars[scv].moleFraction(wPhaseIdx,CaIdx) * Sw * porosity |
405 |
4/4✓ Branch 0 taken 1126645 times.
✓ Branch 1 taken 2933131 times.
✓ Branch 2 taken 1126645 times.
✓ Branch 3 taken 2933131 times.
|
8119552 | * elemVolVars[scv].molarDensity(wPhaseIdx) / timeStepSize_; |
406 |
2/2✓ Branch 0 taken 1126645 times.
✓ Branch 1 taken 2933131 times.
|
4059776 | const Scalar rprec = std::min(rurea, maxPrecipitationRate); |
407 | |||
408 | //compute biomass growth coefficient and rate | ||
409 | 4059776 | const Scalar mue = kmue_ * cSubstrate / (ks_ + cSubstrate) * cO2 / (ke_ + cO2);// [1/s] | |
410 | 4059776 | const Scalar rgf = mue * massBiofilm; //[kg/m³s] | |
411 | 4059776 | const Scalar rgb = mue * porosity * Sw * cBio; //[kg/m³s] | |
412 | |||
413 | // compute biomass decay coefficient and rate: | ||
414 | 4059776 | const Scalar dcf = dc0_ + (rprec * SolidSystem::molarMass(SolidSystem::CalciteIdx) | |
415 | 4059776 | /(densityCalcite * (initialPorosity - volFracCalcite))); | |
416 | 4059776 | const Scalar dcb = dc0_; //[1/s] | |
417 | 4059776 | const Scalar rdcf = dcf * massBiofilm; //[kg/m³s] | |
418 | 4059776 | const Scalar rdcb = dcb * porosity * Sw * cBio; //[kg/m³s] | |
419 | |||
420 | // compute attachment coefficient and rate: | ||
421 | 4059776 | const Scalar ka = ca1_ * volFracBiofilm + ca2_; //[1/s] | |
422 | 4059776 | const Scalar ra = ka * porosity * Sw * cBio; //[kg/m³s] | |
423 | |||
424 | // compute detachment coefficient and rate: | ||
425 | 4059776 | const Scalar cd2 = volFracBiofilm / (initialPorosity - volFracCalcite); //[-] | |
426 | 4059776 | const Scalar kd = cd1_ * std::pow((porosity * Sw * scvPotGradNorm),0.58) + cd2 * mue; //[1/s] | |
427 | 4059776 | const Scalar rd = kd * massBiofilm; //[kg/m³s] | |
428 | |||
429 | // rprec[mol/m³s] | ||
430 | // rurea[mol/m³s] | ||
431 | // rgb + rdcb + ra + rd [kg/m³s] | ||
432 | // source[kg/m³s] | ||
433 | 4059776 | NumEqVector source(0.0); | |
434 | 4059776 | source[wCompIdx] += 0; | |
435 | 4059776 | source[nCompIdx] += rurea - rprec; | |
436 | 4059776 | source[NaIdx] += 0; | |
437 | 4059776 | source[ClIdx] += 0; | |
438 | 4059776 | source[CaIdx] += - rprec; | |
439 | 4059776 | source[UreaIdx] += - rurea; | |
440 | 4059776 | source[O2Idx] += -(rgf + rgb) *f_/yield_ / FluidSystem::molarMass(O2Idx); | |
441 | 4059776 | source[BiosubIdx] += -(rgf + rgb) / yield_ / FluidSystem::molarMass(BiosubIdx); | |
442 | 4059776 | source[SuspendedBiomassIdx] += (rgb - rdcb - ra + rd) / FluidSystem::molarMass(SuspendedBiomassIdx); | |
443 | 4059776 | source[phiBiofilmIdx] += (rgf - rdcf + ra - rd) / SolidSystem::molarMass(SolidSystem::BiofilmIdx); | |
444 | 8119552 | source[phiCalciteIdx] += + rprec; | |
445 | |||
446 | 4059776 | return source; | |
447 | } | ||
448 | // [[/codeblock]] | ||
449 | |||
450 | // The permeability is added to the vtk output | ||
451 | // [[codeblock]] | ||
452 | // Function to return the permeability for additional vtk output | ||
453 | const std::vector<Scalar>& getPermeability() | ||
454 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | { return permeability_; } |
455 | |||
456 | // Function to update the permeability for additional vtk output | ||
457 | template<class SolutionVector> | ||
458 | 294 | void updateVtkOutput(const SolutionVector& curSol) | |
459 | { | ||
460 |
1/2✓ Branch 3 taken 16758 times.
✗ Branch 4 not taken.
|
33810 | for (const auto& element : elements(this->gridGeometry().gridView())) |
461 | { | ||
462 | 32928 | const auto elemSol = elementSolution(element, curSol, this->gridGeometry()); | |
463 |
2/4✓ Branch 1 taken 16464 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16464 times.
✗ Branch 5 not taken.
|
49392 | auto fvGeometry = localView(this->gridGeometry()); |
464 |
1/2✓ Branch 1 taken 16464 times.
✗ Branch 2 not taken.
|
16464 | fvGeometry.bindElement(element); |
465 |
4/4✓ Branch 0 taken 32928 times.
✓ Branch 1 taken 16464 times.
✓ Branch 2 taken 32928 times.
✓ Branch 3 taken 16464 times.
|
98784 | for (const auto& scv : scvs(fvGeometry)) |
466 | { | ||
467 | 32928 | VolumeVariables volVars; | |
468 |
1/2✓ Branch 1 taken 32928 times.
✗ Branch 2 not taken.
|
32928 | volVars.update(elemSol, *this, element, scv); |
469 | 32928 | const auto dofIdxGlobal = scv.dofIndex(); | |
470 | 65856 | permeability_[dofIdxGlobal] = volVars.permeability(); | |
471 | } | ||
472 | } | ||
473 | 294 | } | |
474 | // [[/codeblock]] | ||
475 | |||
476 | // ### Declaring all necessary variables and private functions | ||
477 | // The internal methods are defined here | ||
478 | // [[codeblock]] | ||
479 | private: | ||
480 | // Internal method for the initial condition reused for the dirichlet conditions. | ||
481 | 1956 | PrimaryVariables initial_(const GlobalPosition &globalPos) const | |
482 | { | ||
483 | 1956 | PrimaryVariables priVars(0.0); | |
484 | 3912 | priVars.setState(wPhaseOnly); | |
485 | 3912 | priVars[pressureIdx] = initPressure_ ; //70e5; // - (maxHeight - globalPos[1])*densityW_*9.81; //p_atm + rho*g*h | |
486 | 3912 | priVars[switchIdx] = initxwTC_; | |
487 | 3912 | priVars[xwNaIdx] = initxwNa_ + xwNaCorr_; | |
488 | 3912 | priVars[xwClIdx] = initxwCl_ + initxwTNH_ + 2*initxwCa_ + xwClCorr_; | |
489 | 3912 | priVars[xwCaIdx] = initxwCa_; | |
490 | 3912 | priVars[xwUreaIdx] = initxwUrea_; | |
491 | 3912 | priVars[xwO2Idx] = initxwO2_; | |
492 | 3912 | priVars[xwBiosubIdx] = initxwBiosub_; | |
493 | 3912 | priVars[xwSuspendedBiomassIdx] = initxwBiosusp_; | |
494 | 3912 | priVars[phiBiofilmIdx] = initBiofilm_; // [m^3/m^3] | |
495 | 3912 | priVars[phiCalciteIdx] = initCalcite_; // [m^3/m^3] | |
496 | 1956 | return priVars; | |
497 | } | ||
498 | |||
499 | // Internal method to calculate the molality of a component based on its mole fraction. | ||
500 | static Scalar moleFracToMolality(Scalar moleFracX, Scalar moleFracSalinity, Scalar moleFracCTot) | ||
501 | { | ||
502 |
2/2✓ Branch 0 taken 1250992 times.
✓ Branch 1 taken 2808784 times.
|
4059776 | const Scalar molalityX = moleFracX / (1 - moleFracSalinity - moleFracCTot) |
503 | 4059776 | / FluidSystem::molarMass(FluidSystem::H2OIdx); | |
504 | return molalityX; | ||
505 | } | ||
506 | // [[/codeblock]] | ||
507 | |||
508 | // The remainder of the class contains an epsilon value used for floating point comparisons | ||
509 | // and parameters needed to describe the chemical processes. | ||
510 | // Additionally the problem name, the peremability vector as well as some time-parameters are declared | ||
511 | // [[details]] private members | ||
512 | // eps is used as a small value for the definition of the boundary conditions | ||
513 | static constexpr Scalar eps_ = 1e-6; | ||
514 | |||
515 | // initial condition parameters | ||
516 | Scalar initPressure_; | ||
517 | Scalar densityW_;//1087; // rhow=1087; | ||
518 | Scalar initxwTC_;//2.3864e-7; // [mol/mol] | ||
519 | Scalar initxwNa_;//0; | ||
520 | Scalar initxwCl_;//0; | ||
521 | Scalar initxwCa_;//0; | ||
522 | Scalar initxwUrea_;//0; | ||
523 | Scalar initxwTNH_;//3.341641e-3; | ||
524 | Scalar initxwO2_;//4.4686e-6; | ||
525 | Scalar initxwBiosub_;//2.97638e-4; | ||
526 | Scalar initxwBiosusp_;//0; | ||
527 | Scalar xwNaCorr_;//2.9466e-6; | ||
528 | Scalar xwClCorr_;//0; | ||
529 | Scalar initBiofilm_; | ||
530 | Scalar initCalcite_; | ||
531 | Scalar temperature_; | ||
532 | |||
533 | // biomass parameters for source/sink calculations | ||
534 | Scalar ca1_; | ||
535 | Scalar ca2_; | ||
536 | Scalar cd1_; | ||
537 | Scalar dc0_; | ||
538 | Scalar kmue_ ; | ||
539 | Scalar f_; | ||
540 | Scalar ke_; | ||
541 | Scalar ks_; | ||
542 | Scalar yield_; | ||
543 | // urease parameters for source/sink calculations | ||
544 | Scalar kub_; | ||
545 | Scalar kurease_; | ||
546 | Scalar ku_; | ||
547 | |||
548 | // injection parameters | ||
549 | Scalar injQ_; | ||
550 | Scalar injTC_; // [kg/kg] | ||
551 | Scalar injNa_; // [kg/m³] | ||
552 | Scalar injCa_; // [kg/m³] //computed from CaCl2 | ||
553 | Scalar injUrea_; // [kg/m³] | ||
554 | Scalar injTNH_; // [kg/m³] //computed from NH4Cl | ||
555 | Scalar injO2_; // [kg/m³] | ||
556 | Scalar injSub_; // [kg/m³] | ||
557 | Scalar injBiosusp_; // [kg/m³] | ||
558 | Scalar injNaCorr_; // [kg/m³] | ||
559 | int numInjections_; | ||
560 | std::vector<InjectionProcess> injectionType_; | ||
561 | |||
562 | // the problem name | ||
563 | std::string name_; | ||
564 | // the permeability for output | ||
565 | std::vector<Scalar> permeability_; | ||
566 | |||
567 | // timing parameters | ||
568 | Scalar time_ = 0.0; | ||
569 | Scalar timeStepSize_ = 0.0; | ||
570 | int episodeIdx_ = 0; | ||
571 | }; | ||
572 | } // end namespace Dumux | ||
573 | // [[/details]] | ||
574 | // [[/content]] | ||
575 | #endif | ||
576 |