https://mooseframework.inl.gov
Loading...
Searching...
No Matches
LinearPWCNSFVMomentumFlux.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
14
15#include <cmath>
16
18
21{
23 params.addClassDescription("Momentum flux kernel with porous-specific advection handling.");
24 return params;
25}
26
29 _porous_mass_flux_provider(getUserObject<PorousRhieChowMassFlux>("rhie_chow_user_object"))
30{
31}
32
33void
35{
36 // porosity jumps are always internal to the domain
37 if (_current_face_type != FaceInfo::VarFaceNeighbors::BOTH)
38 {
40 return;
41 }
42
45
48
49 BaffleStressData stress;
51 stress = baffleStressData();
52 else
53 {
54 const Real transmissibility = computeInternalStressMatrixContribution();
55 stress = {transmissibility, -transmissibility, -transmissibility, transmissibility, 0.0, 0.0};
56 }
57
58 const Real scale_elem = inversePorosity(/*elem_side=*/true);
59 const Real scale_neighbor = inversePorosity(/*elem_side=*/false);
60
61 // No per-side block check is needed: the base class requires the kernel and the variable to
62 // share a block restriction, so on a face with the variable on both sides both cells are in
63 // this kernel's blocks.
64 _matrix_contribution(0, 0) = (adv_elem * scale_elem + stress.elem_elem) * _current_face_area;
66 (adv_neighbor * scale_elem + stress.elem_neighbor) * _current_face_area;
68 (-adv_elem * scale_neighbor + stress.neighbor_elem) * _current_face_area;
70 (-adv_neighbor * scale_neighbor + stress.neighbor_neighbor) * _current_face_area;
71
72 for (auto & matrix : _matrices)
73 (*matrix).add_matrix(_matrix_contribution, _dof_indices.get_values());
74}
75
76Real
78{
80 const Real stress_rhs =
83 const Real advection_rhs = correct_baffle
84 ? 0.0
86 inversePorosity(/*elem_side=*/true) * _current_face_area;
87 return stress_rhs + advection_rhs + computeBaffleAdvectionExplicitCorrection(/*elem_side=*/true);
88}
89
90Real
92{
94 const Real stress_rhs =
97 const Real advection_rhs = correct_baffle
98 ? 0.0
100 inversePorosity(/*elem_side=*/false) * _current_face_area;
101 return stress_rhs + advection_rhs + computeBaffleAdvectionExplicitCorrection(/*elem_side=*/false);
102}
103
104Real
107{
108 const auto boundary_value_matrix_contrib = bc->computeBoundaryValueMatrixContribution();
109 const bool elem_side = _current_face_type != FaceInfo::VarFaceNeighbors::NEIGHBOR;
110 return boundary_value_matrix_contrib * _face_mass_flux * inversePorosity(elem_side);
111}
112
113Real
116{
117 const auto boundary_value_rhs_contrib = bc->computeBoundaryValueRHSContribution();
118 const bool elem_side = _current_face_type != FaceInfo::VarFaceNeighbors::NEIGHBOR;
119 return -boundary_value_rhs_contrib * _face_mass_flux * inversePorosity(elem_side);
120}
121
122Real
124{
126 return 0.0;
127
128 const auto state_arg = determineState();
129
130 const auto & fi = *_current_face_info;
131 const Real u_elem = _var.getElemValue(*fi.elemInfo(), state_arg);
132 const Real u_neighbor = _var.getElemValue(*fi.neighborInfo(), state_arg);
133 // Keep the shared internal-face stencil, then explicitly replace the interpolated superficial
134 // velocity on baffle faces. Interpolating across the baffle would mix the two sides of the
135 // velocity jump, and the cell value alone would miss the variation between the cell center and
136 // the face.
137 const Real shared_advected_velocity = _adv_interp_result.weights_matrix.first * u_elem +
138 _adv_interp_result.weights_matrix.second * u_neighbor;
139
140 // With V_s the velocity of side s extrapolated to the face, both sides advect the interstitial
141 // tangential velocity u_t,f = (I - n n) V_up / eps_up of the upstream side, so the tangential
142 // momentum flux mdot u_t,f leaving one side enters the other and the face is no source of
143 // tangential momentum. Mass conservation fixes the normal superficial velocity from the shared
144 // face mass flux, U_n = mdot / rho_f, with the face-interpolated density rho_f standing in for
145 // both sides because density is assumed to vary smoothly across the baffle. The pressure jump
146 // balances the normal momentum difference. Side s therefore advects
147 // V*_s = eps_s u_t,f + U_n n.
148 const auto & normal = fi.normal();
149 const bool upstream_elem_side = _face_mass_flux >= 0.0;
150 const auto upstream_velocity =
151 _porous_mass_flux_provider.baffleFaceVelocity(fi, upstream_elem_side);
152 const auto interstitial_tangential_velocity =
153 (upstream_velocity - (upstream_velocity * normal) * normal) *
154 inversePorosity(upstream_elem_side);
155 const Real normal_velocity = _porous_mass_flux_provider.baffleNormalVelocity(fi);
156 const Real baffle_advected_velocity =
157 interstitial_tangential_velocity(_index) / inversePorosity(elem_side) +
158 normal_velocity * normal(_index);
159 const Real factor = elem_side ? 1.0 : -1.0;
160
161 return factor * _face_mass_flux * inversePorosity(elem_side) *
162 (shared_advected_velocity - baffle_advected_velocity) * _current_face_area;
163}
164
167{
168 const auto & fi = *_current_face_info;
169 const auto & elem_info = *fi.elemInfo();
170 const auto & neighbor_info = *fi.neighborInfo();
171 const auto state = determineState();
172 const auto & normal = fi.normal();
173
174 // A side of unit porosity is clear fluid; any other side is a porous medium. The viscous
175 // (Brinkman) layer on the porous side of an interface is of the order of the square root of the
176 // permeability, much thinner than the cells next to a lumped interface. Clear fluid therefore
177 // shears against the porous side as against a rough wall moving with the superficial velocity
178 // of the porous medium at the interface, and two porous sides transmit no shear.
179 const bool elem_clear = isClearFluidSide(/*elem_side=*/true);
180 const bool neighbor_clear = isClearFluidSide(/*elem_side=*/false);
181 if (!elem_clear && !neighbor_clear)
182 return {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
183
184 // Viscosity varies continuously, so the face value serves both half-cells, as on ordinary faces.
185 const Real face_viscosity = _mu(makeCDFace(fi), state);
186 if (face_viscosity == 0.0)
187 return {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
188
189 const auto elem_to_face = fi.faceCentroid() - elem_info.centroid();
190 const auto face_to_neighbor = neighbor_info.centroid() - fi.faceCentroid();
191 const Real elem_distance =
192 _use_nonorthogonal_correction ? std::abs(elem_to_face * normal) : elem_to_face.norm();
193 const Real neighbor_distance =
194 _use_nonorthogonal_correction ? std::abs(face_to_neighbor * normal) : face_to_neighbor.norm();
195
196 // The shear traction q_t = T (I - n n) (U_N,f - U_P,f) acts between the superficial face
197 // velocities of the two sides. A clear side extrapolates its face velocity across its half-cell,
198 // whose resistance is d_s / mu_f; a porous side adds no resistance. Hence
199 // 1 / T = sum of d_s / mu_f over the clear sides,
200 // which for two clear sides is the ordinary two-point transmissibility.
201 Real distance = 0.0;
202 if (elem_clear)
203 distance += elem_distance;
204 if (neighbor_clear)
205 distance += neighbor_distance;
206 const Real transmissibility = face_viscosity / distance;
207
208 // The explicit nonorthogonal corrections c_s = grad(U)_s . (n - d_Pf,s / d_s) of each clear
209 // half-cell enter the same balance as the face values.
210 RealVectorValue elem_correction;
211 RealVectorValue neighbor_correction;
213 {
214 const auto elem_correction_vector = normal - elem_to_face / elem_distance;
215 const auto neighbor_correction_vector = normal - face_to_neighbor / neighbor_distance;
216 for (const auto component : make_range(_dim))
217 {
218 const auto & gradient = velocityGradientField(component);
219 if (elem_clear)
220 elem_correction(component) = gradient.gradient(elem_info) * elem_correction_vector;
221 if (neighbor_clear)
222 neighbor_correction(component) =
223 gradient.gradient(neighbor_info) * neighbor_correction_vector;
224 }
225 }
226
227 RealVectorValue elem_velocity;
228 RealVectorValue neighbor_velocity;
229 for (const auto component : make_range(_dim))
230 {
231 const auto & velocity = velocityVar(component);
232 elem_velocity(component) = velocity.getElemValue(elem_info, state);
233 neighbor_velocity(component) = velocity.getElemValue(neighbor_info, state);
234 }
235
236 // A porous side's face velocity is the extrapolated face state that the baffle advection also
237 // uses, so the clear fluid shears against the porous velocity at the interface rather than at
238 // the cell center. It is exact for a linear velocity and so needs no nonorthogonal correction.
239 const auto elem_face_velocity =
240 elem_clear ? RealVectorValue(elem_velocity - elem_distance * elem_correction)
241 : _porous_mass_flux_provider.baffleFaceVelocity(fi, /*elem_side=*/true);
242 const auto neighbor_face_velocity =
243 neighbor_clear ? RealVectorValue(neighbor_velocity + neighbor_distance * neighbor_correction)
244 : _porous_mass_flux_provider.baffleFaceVelocity(fi, /*elem_side=*/false);
245 const auto face_velocity_difference = neighbor_face_velocity - elem_face_velocity;
246 const auto shear_traction =
247 transmissibility * (face_velocity_difference - (face_velocity_difference * normal) * normal);
248
249 // There is no normal viscous stress on a baffle face: the pressure jump carries the normal
250 // momentum balance, and a normal stress of the interstitial velocity would read the jump of
251 // mdot / (rho_f eps_s) as a gradient. The element row adds -q_t, the neighbor row +q_t. The part
252 // proportional to the kernel variable is implicit; everything else, including the coupling to the
253 // other velocity components, is lagged on the right-hand side.
254 const Real n_i = normal(_index);
255 const Real coefficient = transmissibility * (1.0 - n_i * n_i);
256
257 BaffleStressData data;
258 data.elem_elem = coefficient;
259 data.elem_neighbor = -coefficient;
260 data.neighbor_elem = -coefficient;
261 data.neighbor_neighbor = coefficient;
262 data.elem_rhs =
263 shear_traction(_index) + coefficient * (elem_velocity(_index) - neighbor_velocity(_index));
264 data.neighbor_rhs =
265 -shear_traction(_index) + coefficient * (neighbor_velocity(_index) - elem_velocity(_index));
266
267 // The deviatoric stress is lagged in each clear half-cell.
269 {
270 if (elem_clear)
271 data.elem_rhs +=
272 face_viscosity * computeCellStressExplicitCorrection(elem_info, RealVectorValue());
273 if (neighbor_clear)
274 data.neighbor_rhs -=
275 face_viscosity * computeCellStressExplicitCorrection(neighbor_info, RealVectorValue());
276 }
277
278 return data;
279}
280
281bool
283{
284 return MooseUtils::absoluteFuzzyEqual(_porous_mass_flux_provider.getFaceSidePorosity(
285 *_current_face_info, elem_side, determineState()),
286 1.0);
287}
288
289Real
291{
293 *_current_face_info, elem_side, determineState());
294 if (porosity <= 0.0)
295 mooseError("porosity must be positive on face ", _current_face_info->id(), ".");
296
297 return 1.0 / porosity;
298}
registerMooseObject("NavierStokesApp", LinearPWCNSFVMomentumFlux)
const std::vector< std::vector< dof_id_type > > & dofIndices() const
Moose::FaceArg makeCDFace(const FaceInfo &fi, const bool correct_skewness=false) const
const ElemInfo * elemInfo() const
const ElemInfo * neighborInfo() const
dof_id_type id() const
void addClassDescription(const std::string &doc_string)
virtual Real computeBoundaryValueMatrixContribution() const=0
virtual Real computeBoundaryValueRHSContribution() const=0
FaceInfo::VarFaceNeighbors _current_face_type
DenseMatrix< Real > _matrix_contribution
DenseVector< dof_id_type > _dof_indices
virtual void addMatrixContribution() override
const FaceInfo * _current_face_info
MooseLinearVariableFV< Real > & _var
const unsigned int _sys_num
const unsigned int _var_num
Momentum flux kernel for porous equations written in superficial velocity.
LinearPWCNSFVMomentumFlux(const InputParameters &params)
Construct the porous momentum flux kernel.
Real computeElemRightHandSideContribution() override
Compute the element-row internal-face right-hand-side contribution.
Real computeAdvectionBoundaryMatrixContribution(const LinearFVAdvectionDiffusionBC *bc) override
Compute the boundary advection matrix contribution with local porosity scaling.
bool isClearFluidSide(bool elem_side) const
Whether one side of the current face is clear fluid, i.e.
const PorousRhieChowMassFlux & _porous_mass_flux_provider
Porous Rhie-Chow object supplying porosity and pressure-baffle data.
void addMatrixContribution() override
Assemble the internal-face matrix with side-specific porosity scaling of advection.
Real computeNeighborRightHandSideContribution() override
Compute the neighbor-row internal-face right-hand-side contribution.
Real computeAdvectionBoundaryRHSContribution(const LinearFVAdvectionDiffusionBC *bc) override
Compute the boundary advection right-hand-side contribution with local porosity scaling.
static InputParameters validParams()
Real inversePorosity(bool elem_side) const
Return the inverse porosity on one side of the current face.
BaffleStressData baffleStressData() const
Compute the stress on the current baffle face, per unit face area.
Real computeBaffleAdvectionExplicitCorrection(bool elem_side) const
Compute the correction that replaces the shared advected state with the baffle face state: the upstre...
std::vector< SparseMatrix< Number > * > _matrices
Kernel that implements the stress tensor and advection terms for the momentum equation.
Real computeInternalStressMatrixContribution()
Computes the matrix contribution of the stress term on the current face when the face is an internal ...
const MooseLinearVariableFVReal & velocityVar(unsigned int dir) const
Helper to access the velocity variable for a given direction.
Real _face_mass_flux
Container for the mass flux on the face which will be reused in the advection term's matrix and right...
Real computeInternalAdvectionElemMatrixContribution()
Computes the matrix contribution of the advective flux on the element side of current face when the f...
FVAdvectedInterpolationMethod::AdvectedSystemContribution _adv_interp_result
Current advected interpolation contribution on the face.
const Moose::Functor< Real > & _mu
The functor for the dynamic viscosity.
Real computeCellStressExplicitCorrection(const ElemInfo &cell_info, const RealVectorValue &nonorthogonal_correction_vector) const
Compute the explicit stress correction reconstructed from one adjacent cell.
const LinearFVGradientReader & velocityGradientField(unsigned int dir) const
Helper to access the velocity gradient field for a given direction.
const unsigned int _index
Index x|y|z, this is mainly to handle the deviatoric parts correctly in in the stress term.
const bool _use_deviatoric_terms
Switch to enable/disable deviatoric parts in the stress term.
Real computeInternalStressRHSContribution()
Computes the right hand side contribution of the stress term on the current face when the face is an ...
Real computeInternalAdvectionNeighborMatrixContribution()
Computes the matrix contribution of the advective flux on the neighbor side of current face when the ...
const unsigned int _dim
The dimension of the mesh.
const bool _use_nonorthogonal_correction
Switch to enable/disable nonorthogonal correction in the stress term.
static InputParameters validParams()
void mooseError(Args &&... args) const
Real getElemValue(const ElemInfo &elem_info, const StateArg &state) const
virtual const OutputTools< Real >::VariableGradient & gradient()
Computes Rhie-Chow face mass fluxes for porous flow with optional internal pressure jumps.
bool faceIsBaffle(const FaceInfo &fi) const
Whether a face is governed by a pressure-jump model.
Real baffleNormalVelocity(const FaceInfo &fi) const
Return the normal superficial velocity at a baffle face, recovered from the shared face mass flux and...
Real getFaceSidePorosity(const FaceInfo &fi, bool elem_side, const Moose::StateArg &time) const
Return the porosity on one side of a face.
RealVectorValue baffleFaceVelocity(const FaceInfo &fi, bool elem_side) const
Return the superficial velocity at a baffle face as seen from one side.
Moose::StateArg determineState() const
std::vector< T > & get_values()
Implicit coefficients and deferred right-hand sides of the stress on a baffle face.
Real elem_rhs
Explicit parts of the element and neighbor rows.
Real elem_elem
Coefficients of the element row on the element and neighbor values.
Real neighbor_elem
Coefficients of the neighbor row on the element and neighbor values.
Real distance(const Point &p)