OGS
BHE_2U.cpp
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#include "BHE_2U.h"
5
6#include <numbers>
7
11#include "Physics.h"
13
14namespace ProcessLib
15{
16namespace HeatTransportBHE
17{
18namespace BHE
19{
24 PipeConfigurationUType const& pipes,
25 bool const use_python_bcs)
27 pipes, use_python_bcs}
28{
29 checkEqualPipeOutsideDiameters(_pipes.inlet, _pipes.outlet, "BHE 2U");
30 // Initialize thermal resistances.
31 auto values = visit(
32 [&](auto const& control)
33 {
34 return control(refrigerant.reference_temperature,
35 0. /* initial time */);
36 },
38 updateHeatTransferCoefficients(values.flow_rate);
39}
40
41std::array<double, BHE_2U::number_of_unknowns> BHE_2U::pipeHeatCapacities()
42 const
43{
44 double const rho_r = refrigerant.density;
45 double const specific_heat_capacity = refrigerant.specific_heat_capacity;
46 double const rho_g = grout.rho_g;
47 double const porosity_g = grout.porosity_g;
48 double const heat_cap_g = grout.heat_cap_g;
49
50 return {{/*i1*/ rho_r * specific_heat_capacity,
51 /*i2*/ rho_r * specific_heat_capacity,
52 /*o1*/ rho_r * specific_heat_capacity,
53 /*o2*/ rho_r * specific_heat_capacity,
54 /*g1*/ (1.0 - porosity_g) * rho_g * heat_cap_g,
55 /*g2*/ (1.0 - porosity_g) * rho_g * heat_cap_g,
56 /*g3*/ (1.0 - porosity_g) * rho_g * heat_cap_g,
57 /*g4*/ (1.0 - porosity_g) * rho_g * heat_cap_g}};
58}
59
60std::array<double, BHE_2U::number_of_unknowns> BHE_2U::pipeHeatConductions()
61 const
62{
63 double const lambda_r = refrigerant.thermal_conductivity;
64 double const rho_r = refrigerant.density;
65 double const Cp_r = refrigerant.specific_heat_capacity;
66 double const alpha_L = _pipes.longitudinal_dispersion_length;
67 double const porosity_g = grout.porosity_g;
68 double const lambda_g = grout.lambda_g;
69
70 double const velocity_norm = std::abs(flow_velocity);
71
72 auto const pipe_conduction =
73 lambda_r + rho_r * Cp_r * alpha_L * velocity_norm;
74 auto const grout_conduction = (1.0 - porosity_g) * lambda_g;
75 return {{pipe_conduction, // i1
76 pipe_conduction, // i2
77 pipe_conduction, // o1
78 pipe_conduction, // o2
79 grout_conduction, // g1
80 grout_conduction, // g2
81 grout_conduction, // g3
82 grout_conduction}}; // g4
83}
84
85std::array<Eigen::Vector3d, BHE_2U::number_of_unknowns>
86BHE_2U::pipeAdvectionVectors(Eigen::Vector3d const& elem_direction) const
87{
88 double const rho_r = refrigerant.density;
89 double const Cp_r = refrigerant.specific_heat_capacity;
90
91 auto const legs = flowLegs();
92 auto leg_adv = [&](double const v_signed) -> Eigen::Vector3d
93 { return rho_r * Cp_r * v_signed * elem_direction; };
94
95 return {{leg_adv(legs[0]), // i1
96 leg_adv(legs[1]), // i2
97 leg_adv(legs[2]), // o1
98 leg_adv(legs[3]), // o2
99 {0, 0, 0}, // g1
100 {0, 0, 0}, // g2
101 {0, 0, 0}, // g3
102 {0, 0, 0}}}; // g4
103}
104
106std::array<double, 4> thermalResistancesGroutSoil2U(double const chi,
107 double const R_ar_1,
108 double const R_ar_2,
109 double const R_g)
110{
111 double R_gs = computeRgs(chi, R_g);
112 double R_gg_1 = computeRgg(chi, R_gs, R_ar_1, R_g);
113 double R_gg_2 = computeRgg(chi, R_gs, R_ar_2, R_g);
114 double chi_new = chi;
115
116 auto constraint = [&]()
117 { return 1.0 / ((1.0 / R_gg_1) + (1.0 / (2.0 * R_gs))); };
118
119 std::array<double, 3> const multiplier{chi * 2.0 / 3.0, chi * 1.0 / 3.0,
120 0.0};
121 for (double m_chi : multiplier)
122 {
123 if (constraint() >= 0)
124 {
125 break;
126 }
127 DBUG(
128 "Warning! Correction procedure was applied due to negative thermal "
129 "resistance! Chi = {:f}.\n",
130 m_chi);
131 R_gs = computeRgs(m_chi, R_g);
132 R_gg_1 = computeRgg(m_chi, R_gs, R_ar_1, R_g);
133 R_gg_2 = computeRgg(m_chi, R_gs, R_ar_2, R_g);
134 chi_new = m_chi;
135 }
136
137 return {chi_new, R_gg_1, R_gg_2, R_gs};
138}
139
140void BHE_2U::updateHeatTransferCoefficients(double const flow_rate)
141{
143 _pipes.inlet, borehole_geometry.length, refrigerant, flow_rate);
144
145 flow_velocity = tm_flow.velocity;
146 cached_nu_ = tm_flow.nusselt_number;
147}
148
149std::vector<double> BHE_2U::thermalResistances(
150 ParameterLib::SpatialPosition const& pos) const
151{
153}
154
156 double const Nu, ParameterLib::SpatialPosition const& pos) const
157{
158 constexpr double pi = std::numbers::pi;
159
160 double const lambda_r = refrigerant.thermal_conductivity;
161 double const lambda_g = grout.lambda_g;
162 // t=0.0: borehole properties are physically time-invariant; genuinely
163 // time-varying parameter types are rejected in createPipe.
164 double const lambda_p_inlet =
165 sampleStrictPositive(_pipes.inlet.wall_thermal_conductivity, 0.0, pos,
166 "inlet wall_thermal_conductivity");
167 double const lambda_p_outlet =
168 sampleStrictPositive(_pipes.outlet.wall_thermal_conductivity, 0.0, pos,
169 "outlet wall_thermal_conductivity");
170
171 // thermal resistances due to advective flow of refrigerant in the _pipes
172 double const R_adv_i = 1.0 / (Nu * lambda_r * pi);
173 double const R_adv_o = 1.0 / (Nu * lambda_r * pi);
174
175 // thermal resistance due to thermal conductivity of the pipe wall material
176 double const R_con_a_inlet =
177 pipeWallThermalResistance(_pipes.inlet, lambda_p_inlet);
178 double const R_con_a_outlet =
179 pipeWallThermalResistance(_pipes.outlet, lambda_p_outlet);
180
181 // Single pipe outside diameter used by the resistance formulas; the U-type
182 // constructor enforces that inlet and outlet share it.
183 double const d0 = _pipes.inlet.outsideDiameter();
184 double const D = sampleStrictPositive(borehole_geometry.diameter, 0.0, pos,
185 "borehole_diameter");
186 // Context prefix shared by the acosh-argument checks below. `cause` names
187 // the physical degeneracy that drives the argument to <= 1 so the fatal
188 // message is actionable.
189 auto const geometry_context =
190 [&](std::string_view const equation, std::string_view const cause)
191 {
192 return uTypeGeometryContext(pos, D, d0, _pipes.distance, equation,
193 cause);
194 };
195 // Eq. 38: chi requires D > 2 * d0 so that log(D/(2*d0)) > 0.
196 checkBoreholeVsPipeDiameter(D, 2.0 * d0, pos,
197 "BHE 2U chi formula (Eq. 38)");
198 double const chi =
199 std::log(std::sqrt(D * D + 4 * d0 * d0) / 2 / std::sqrt(2) / d0) /
200 std::log(D / 2 / d0);
201 // Eq. 39
202 double const acosh_arg_R_g =
203 (D * D + d0 * d0 - 2 * _pipes.distance * _pipes.distance) /
204 (2 * D * d0);
206 acosh_arg_R_g,
207 geometry_context(
208 "BHE 2U R_g (Eq. 39)",
209 "The argument drops to <= 1 when a pipe reaches the borehole wall, "
210 "giving a zero grout resistance R_g and an infinite (1/R) assembly "
211 "coefficient."));
212 double const R_g = std::acosh(acosh_arg_R_g) / (2 * pi * lambda_g) *
213 (3.098 - 4.432 * std::sqrt(2) * _pipes.distance / D +
214 2.364 * 2 * _pipes.distance * _pipes.distance / D / D);
215
216 double const acosh_arg_R_ar_1 =
217 (2.0 * _pipes.distance * _pipes.distance - d0 * d0) / d0 / d0;
219 acosh_arg_R_ar_1,
220 geometry_context(
221 "BHE 2U R_ar_1",
222 "The argument drops to <= 1 when the pipes are too close, giving a "
223 "zero inter-grout resistance R_ar and an infinite (1/R) assembly "
224 "coefficient."));
225 double const R_ar_1 = std::acosh(acosh_arg_R_ar_1) / (2.0 * pi * lambda_g);
226
227 double const acosh_arg_R_ar_2 =
228 (2.0 * 2.0 * _pipes.distance * _pipes.distance - d0 * d0) / d0 / d0;
230 acosh_arg_R_ar_2,
231 geometry_context(
232 "BHE 2U R_ar_2",
233 "The argument drops to <= 1 when the pipes are too close, giving a "
234 "zero inter-grout resistance R_ar and an infinite (1/R) assembly "
235 "coefficient."));
236 double const R_ar_2 = std::acosh(acosh_arg_R_ar_2) / (2.0 * pi * lambda_g);
237
238 auto const [chi_new, R_gg_1, R_gg_2, R_gs] =
239 thermalResistancesGroutSoil2U(chi, R_ar_1, R_ar_2, R_g);
240
241 double const R_con_b = chi_new * R_g;
242
243 double const R_fig = R_adv_i + R_con_a_inlet + R_con_b;
244 double const R_fog = R_adv_o + R_con_a_outlet + R_con_b;
245
246 return {R_fig, R_fog, R_gg_1, R_gg_2, R_gs};
247}
248
249std::array<std::pair<std::size_t, int>, 2>
251 std::size_t const top_node_id,
252 std::size_t const /*bottom_node_id*/,
253 int const in_component_id)
254{
255 return {std::make_pair(top_node_id, in_component_id),
256 std::make_pair(top_node_id, in_component_id + 2)};
257}
258
259std::optional<std::array<std::pair<std::size_t, int>, 2>>
261 std::size_t const bottom_node_id,
262 int const in_component_id,
263 int const out_component_id)
264{
265 return {{std::make_pair(bottom_node_id, in_component_id),
266 std::make_pair(bottom_node_id, out_component_id)}};
267}
268
269std::array<double, BHE_2U::number_of_unknowns> BHE_2U::crossSectionAreas(
270 ParameterLib::SpatialPosition const& pos) const
271{
272 double const D = sampleStrictPositive(borehole_geometry.diameter, 0.0, pos,
273 "borehole_diameter");
274 double const borehole_area = Pipe::circleArea(D);
275 double const quarter_borehole_area = borehole_area / number_of_grout_zones;
276 double const grout_area_inlet = checkedGroutArea(
277 quarter_borehole_area, _pipes.inlet.outsideArea(), pos);
278 double const grout_area_outlet = checkedGroutArea(
279 quarter_borehole_area, _pipes.outlet.outsideArea(), pos);
280
281 return {{
282 _pipes.inlet.area(), // i1
283 _pipes.inlet.area(), // i2
284 _pipes.outlet.area(), // o1
285 _pipes.outlet.area(), // o2
286 grout_area_inlet, // g1
287 grout_area_inlet, // g2
288 grout_area_outlet, // g3
289 grout_area_outlet, // g4
290 }};
291}
292
293double BHE_2U::updateFlowRateAndTemperature(double const T_out,
294 double const current_time)
295{
296 auto values =
297 visit([&](auto const& control) { return control(T_out, current_time); },
299 updateHeatTransferCoefficients(values.flow_rate);
300 return values.temperature;
301}
302} // namespace BHE
303} // namespace HeatTransportBHE
304} // namespace ProcessLib
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
BHECommonUType(BoreholeGeometry const &borehole, RefrigerantProperties const &refrigerant, GroutParameters const &grout, FlowAndTemperatureControl const &flowAndTemperatureControl, PipeConfigurationUType const &pipes, bool const use_python_bcs)
RefrigerantProperties const refrigerant
Definition BHECommon.h:51
FlowAndTemperatureControl const flowAndTemperatureControl
Definition BHECommon.h:53
static std::array< std::pair< std::size_t, int >, 2 > getBHEInflowDirichletBCNodesAndComponents(std::size_t const top_node_id, std::size_t const, int const in_component_id)
Definition BHE_2U.cpp:250
std::array< double, number_of_flow_legs > flowLegs() const
Definition BHE_2U.h:56
std::array< double, number_of_unknowns > crossSectionAreas(ParameterLib::SpatialPosition const &pos) const
Definition BHE_2U.cpp:269
void updateHeatTransferCoefficients(double const flow_rate)
Definition BHE_2U.cpp:140
std::array< double, number_of_unknowns > pipeHeatConductions() const
Definition BHE_2U.cpp:60
std::vector< double > calcThermalResistances(double const Nu, ParameterLib::SpatialPosition const &pos) const
Definition BHE_2U.cpp:155
double updateFlowRateAndTemperature(double T_out, double current_time)
Return the inflow temperature for the boundary condition.
Definition BHE_2U.cpp:293
std::array< Eigen::Vector3d, number_of_unknowns > pipeAdvectionVectors(Eigen::Vector3d const &elem_direction) const
Definition BHE_2U.cpp:86
std::array< double, number_of_unknowns > pipeHeatCapacities() const
Definition BHE_2U.cpp:41
BHE_2U(BoreholeGeometry const &borehole, RefrigerantProperties const &refrigerant, GroutParameters const &grout, FlowAndTemperatureControl const &flowAndTemperatureControl, PipeConfigurationUType const &pipes, bool const use_python_bcs)
Definition BHE_2U.cpp:20
std::vector< double > thermalResistances(ParameterLib::SpatialPosition const &pos) const
Definition BHE_2U.cpp:149
static std::optional< std::array< std::pair< std::size_t, int >, 2 > > getBHEBottomDirichletBCNodesAndComponents(std::size_t const bottom_node_id, int const in_component_id, int const out_component_id)
Definition BHE_2U.cpp:260
static constexpr int number_of_grout_zones
Definition BHE_2U.h:50
void checkAcoshArg(double const arg, std::string_view const context)
double sampleStrictPositive(ParameterLib::Parameter< double > const &param, double const t, ParameterLib::SpatialPosition const &pos, std::string_view const param_role)
void checkBoreholeVsPipeDiameter(double const D, double const min_diameter, ParameterLib::SpatialPosition const &pos, std::string_view const context)
std::string uTypeGeometryContext(ParameterLib::SpatialPosition const &pos, double const D, double const d0, double const distance, std::string_view const equation, std::string_view const cause)
double checkedGroutArea(double const borehole_area_fraction, double const pipe_outside_area, ParameterLib::SpatialPosition const &pos)
double computeRgg(double const chi, double const R_gs, double const R_ar, double const R_g)
std::variant< InflowTemperature, Power, BuildingPower, BuildingPowerHotWaterActiveCooling, BuildingPowerHotWaterPassiveCooling, BuildingPowerHotWater, BuildingPowerActiveCooling, BuildingPowerPassiveCooling, ActiveCooling > FlowAndTemperatureControl
double computeRgs(double const chi, double const R_g)
Grout-soil thermal resistance: R_gs = (1 - chi) * R_g.
void checkEqualPipeOutsideDiameters(Pipe const &inlet, Pipe const &outlet, std::string_view const context)
double pipeWallThermalResistance(Pipe const &pipe, double const wall_thermal_conductivity)
ThermoMechanicalFlowProperties calculateThermoMechanicalFlowPropertiesPipe(Pipe const &pipe, double const length, RefrigerantProperties const &fluid, double const flow_rate)
std::array< double, 4 > thermalResistancesGroutSoil2U(double const chi, double const R_ar_1, double const R_ar_2, double const R_g)
Thermal resistances due to grout-soil exchange.
Definition BHE_2U.cpp:106
static double circleArea(double const d)
Definition Pipe.h:46