OGS
WellboreCompensateNeumannBoundaryConditionLocalAssembler.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#pragma once
5
6#include <spdlog/fmt/fmt.h>
7
8#include <cmath>
9#include <limits>
10#include <optional>
11
21#include "NumLib/Exceptions.h"
25
26namespace ProcessLib
27{
28
30{
31 double pressure;
32 double velocity;
33 double enthalpy;
34};
35
37{
39
40 // Used for mapping boundary nodes to bulk nodes.
41 std::unique_ptr<NumLib::LocalToGlobalIndexMap> dof_table_boundary_pressure;
42 std::unique_ptr<NumLib::LocalToGlobalIndexMap> dof_table_boundary_velocity;
43 std::unique_ptr<NumLib::LocalToGlobalIndexMap> dof_table_boundary_enthalpy;
44
46};
47
48template <typename ShapeFunction, int GlobalDim>
51 GlobalDim>
52{
53 using Base =
57
58public:
62 MeshLib::Element const& e,
63 std::size_t const local_matrix_size,
64 NumLib::GenericIntegrationMethod const& integration_method,
65 bool const is_axially_symmetric,
67 : Base(e, is_axially_symmetric, integration_method),
68 _element(e),
69 _data(data),
70 _local_matrix_size(local_matrix_size)
71 {
72 }
73
74 void assemble(std::size_t const mesh_item_id,
75 NumLib::LocalToGlobalIndexMap const& dof_table_boundary,
76 double const /*t*/, std::vector<GlobalVector*> const& x,
77 int const process_id, GlobalMatrix* /*K*/, GlobalVector& b,
78 GlobalMatrix* /*Jac*/) override
79 {
81 _local_rhs.setZero();
82
83 unsigned const n_integration_points =
84 Base::_integration_method.getNumberOfPoints();
85
86 auto const indices_current_variable =
87 NumLib::getIndices(mesh_item_id, dof_table_boundary);
88 auto const indices_pressure = NumLib::getIndices(
89 mesh_item_id, *_data.dof_table_boundary_pressure);
90 auto const indices_velocity = NumLib::getIndices(
91 mesh_item_id, *_data.dof_table_boundary_velocity);
92 auto const indices_enthalpy = NumLib::getIndices(
93 mesh_item_id, *_data.dof_table_boundary_enthalpy);
94
95 std::vector<double> const local_pressure =
96 x[process_id]->get(indices_pressure);
97 std::vector<double> const local_velocity =
98 x[process_id]->get(indices_velocity);
99 std::vector<double> const local_enthalpy =
100 x[process_id]->get(indices_enthalpy);
101
102 auto const& medium = *_data.media_map.getMedium(_element.getID());
103 auto const& liquid_phase =
105 auto const& gas_phase =
107
109 pos.setElementID(_element.getID());
110
112
113 for (unsigned ip = 0; ip < n_integration_points; ip++)
114 {
115 auto const& n_and_weight = Base::_ns_and_weights[ip];
116 auto const& N = n_and_weight.N;
117 auto const& w = n_and_weight.weight;
118
119 double pressure_int_pt = 0.0;
120 double velocity_int_pt = 0.0;
121 double enthalpy_int_pt = 0.0;
122
123 NumLib::shapeFunctionInterpolate(local_pressure, N,
124 pressure_int_pt);
125 NumLib::shapeFunctionInterpolate(local_velocity, N,
126 velocity_int_pt);
127 NumLib::shapeFunctionInterpolate(local_enthalpy, N,
128 enthalpy_int_pt);
129
130 vars.liquid_phase_pressure = pressure_int_pt;
131 vars.enthalpy = enthalpy_int_pt;
132
133 // Above the critical pressure the region 4 saturation line ends,
134 // so there is no two-phase state to describe and the saturation
135 // properties are not evaluated at all: they would be
136 // extrapolated, and the closure they feed has no admissible void
137 // fraction there. Such a section is compressed liquid, which the
138 // region 1 properties of the liquid phase describe, and it is
139 // solved as one, as in the process this boundary condition
140 // compensates. Below the lower bound of the saturation line there
141 // is no such fall-back, so the range check of the saturation
142 // properties aborts the assembly as before.
143 double dryness = 0.;
144 double T_int_pt = 0.;
145 double liquid_water_density = 0.;
146 double vapour_water_density = 0.;
147 double alpha = 0.;
148 std::optional<MaterialPropertyLib::DriftFluxState> drift_flux_state;
149
150 if (pressure_int_pt >
152 {
153 T_int_pt =
154 liquid_phase
155 .property(
157 .template value<double>(vars, pos, 0, 0);
158 vars.temperature = T_int_pt;
159
161 pressure_int_pt, T_int_pt,
162 "the compressed liquid state of the "
163 "WellboreCompensateNeumann boundary condition");
164
165 liquid_water_density =
166 liquid_phase
168 .template value<double>(vars, pos, 0, 0);
169 }
170 else
171 {
172 liquid_water_density =
173 liquid_phase
175 saturation_density)
176 .template value<double>(vars, pos, 0, 0);
177
178 vapour_water_density =
179 gas_phase
181 saturation_density)
182 .template value<double>(vars, pos, 0, 0);
183
184 double const h_sat_liq_w =
185 liquid_phase
187 saturation_enthalpy)
188 .template value<double>(vars, pos, 0, 0);
189
190 double const h_sat_vap_w =
191 gas_phase
193 saturation_enthalpy)
194 .template value<double>(vars, pos, 0, 0);
195
197 enthalpy_int_pt, h_sat_liq_w, h_sat_vap_w);
198
199 T_int_pt =
200 (dryness == 0)
201 ? liquid_phase
203 temperature)
204 .template value<double>(vars, pos, 0, 0)
205 : gas_phase
207 saturation_temperature)
208 .template value<double>(vars, pos, 0, 0);
209
210 vars.temperature = T_int_pt;
211
212 // For the calculation of the void fraction of vapour,
213 // see Rohuani, Z., and E. Axelsson. "Calculation of volume
214 // void fraction in a subcooled and quality region."
215 // International Journal of Heat and Mass Transfer 17 (1970):
216 // 383-393.
217
218 // The drift is aligned with the mixture flow so that the
219 // closure below and the slip momentum term further down are
220 // consistent, see
221 // MaterialPropertyLib::alignedDriftFluxVelocity().
222 drift_flux_state = MaterialPropertyLib::driftFluxState(
223 dryness, T_int_pt, vapour_water_density,
224 liquid_water_density, velocity_int_pt);
225
226 // solving void fraction of vapour: Rouhani-Axelsson
227 auto const alpha_solution =
229 *drift_flux_state);
230
231 if (!alpha_solution)
232 {
233 throw NumLib::AssemblyException(fmt::format(
234 "The drift-flux closure of the "
235 "WellboreCompensateNeumann boundary condition has no "
236 "admissible vapour void fraction in element {:d}, "
237 "integration point {:d}: pressure {:g} Pa, mixture "
238 "velocity {:g} m/s, specific enthalpy {:g} J/kg, "
239 "temperature {:g} K, {}",
240 _element.getID(), ip, pressure_int_pt, velocity_int_pt,
241 enthalpy_int_pt, T_int_pt,
243 *drift_flux_state)));
244 }
245
246 alpha = *alpha_solution;
247
248 if (alpha == 0)
249 {
250 liquid_water_density =
251 liquid_phase
252 .property(
254 .template value<double>(vars, pos, 0, 0);
255 }
256 }
257
258 double const mix_density = vapour_water_density * alpha +
259 liquid_water_density * (1 - alpha);
260
261 double const gamma =
263 alpha, *drift_flux_state)
264 : 0.;
265
266 double const neumann_ip_values =
267 _data.coefficients.pressure * mix_density * velocity_int_pt +
268 _data.coefficients.velocity *
269 (mix_density * velocity_int_pt * velocity_int_pt + gamma) +
270 _data.coefficients.enthalpy * mix_density * velocity_int_pt *
271 velocity_int_pt * velocity_int_pt * 0.5;
272 _local_rhs.noalias() += N.transpose() * neumann_ip_values * w;
273 }
274
275 b.add(indices_current_variable, _local_rhs);
276 }
277
278private:
282};
283
284} // namespace ProcessLib
MathLib::EigenMatrix GlobalMatrix
MathLib::EigenVector GlobalVector
void add(IndexType rowId, double v)
add entry
Definition EigenVector.h:70
void setElementID(std::size_t element_id)
GenericNaturalBoundaryConditionLocalAssembler(MeshLib::Element const &e, bool is_axially_symmetric, NumLib::GenericIntegrationMethod const &integration_method)
std::vector< NAndWeight, Eigen::aligned_allocator< NAndWeight > > const _ns_and_weights
WellboreCompensateNeumannBoundaryConditionLocalAssembler(MeshLib::Element const &e, std::size_t const local_matrix_size, NumLib::GenericIntegrationMethod const &integration_method, bool const is_axially_symmetric, WellboreCompensateNeumannBoundaryConditionData const &data)
void assemble(std::size_t const mesh_item_id, NumLib::LocalToGlobalIndexMap const &dof_table_boundary, double const, std::vector< GlobalVector * > const &x, int const process_id, GlobalMatrix *, GlobalVector &b, GlobalMatrix *) override
void checkStateInRange(double const pressure, double const temperature, std::string_view const quantity)
double mixtureSlipParameter(double const alpha, DriftFluxState const &state)
std::string voidFractionClosureDiagnostics(DriftFluxState const &state)
std::optional< double > computeVapourVoidFraction(DriftFluxState const &state)
double steamDryness(double const enthalpy, double const h_sat_liquid, double const h_sat_vapour)
DriftFluxState driftFluxState(double const dryness, double const temperature, double const vapour_water_density, double const liquid_water_density, double const v_mix)
void shapeFunctionInterpolate(const NodalValues &, const ShapeMatrix &)
std::vector< GlobalIndexType > getIndices(std::size_t const mesh_item_id, NumLib::LocalToGlobalIndexMap const &dof_table)