OGS
CreateHeatTransportBHEProcess.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
5
6#include <pybind11/pybind11.h>
7
8#include <algorithm>
9#include <cmath>
10#include <map>
11#include <ranges>
12#include <vector>
13
14#include "BHE/BHETypes.h"
15#include "BHE/CreateBHE1PType.h"
17#include "BHE/CreateBHEUType.h"
21#include "ParameterLib/Utils.h"
24
25namespace ProcessLib
26{
27namespace HeatTransportBHE
28{
29using BHECreatorFunc = std::function<BHE::BHETypes(
31 std::vector<std::unique_ptr<ParameterLib::ParameterBase>>&,
32 std::map<std::string,
33 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&,
34 std::vector<MeshLib::Node*> const&)>;
35
36std::map<std::string_view, BHECreatorFunc> bheCreators = {
37 {"1U",
38 [](auto& config, auto& parameters, auto& curves, auto& bhe_nodes)
39 {
41 config, parameters, curves, bhe_nodes));
42 }},
43 {"2U",
44 [](auto& config, auto& parameters, auto& curves, auto& bhe_nodes)
45 {
47 config, parameters, curves, bhe_nodes));
48 }},
49 {"CXA",
50 [](auto& config, auto& parameters, auto& curves, auto& bhe_nodes)
51 {
53 config, parameters, curves, bhe_nodes));
54 }},
55 {"CXC",
56 [](auto& config, auto& parameters, auto& curves, auto& bhe_nodes)
57 {
59 config, parameters, curves, bhe_nodes));
60 }},
61 {"1P", [](auto& config, auto& parameters, auto& curves, auto& bhe_nodes)
62 {
64 config, parameters, curves, bhe_nodes));
65 }}};
66
68 const std::string& bhe_type,
69 const std::vector<int>& bhe_ids_of_this_bhe,
70 const BaseLib::ConfigTree& bhe_config,
71 std::vector<std::unique_ptr<ParameterLib::ParameterBase>>& parameters,
72 std::map<std::string,
73 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
74 curves,
75 BHEMeshData const& bhe_mesh_data,
76 std::map<int, BHE::BHETypes>& bhes_map)
77{
78 auto bhe_creator_it = bheCreators.find(bhe_type);
79 if (bhe_creator_it == bheCreators.end())
80 {
81 OGS_FATAL("Unknown BHE type: {:s}", bhe_type);
82 }
83 for (auto const& id : bhe_ids_of_this_bhe)
84 {
85 if (id < 0 || id >= static_cast<int>(bhe_mesh_data.BHE_nodes.size()))
86 {
88 "BHE id {:d} is out of range. The mesh contains {:d} "
89 "BHE(s) (valid ids: 0 to {:d}).",
90 id, bhe_mesh_data.BHE_nodes.size(),
91 static_cast<int>(bhe_mesh_data.BHE_nodes.size()) - 1);
92 }
93 std::pair<std::map<int, BHE::BHETypes>::iterator, bool> result;
94 if (id == bhe_ids_of_this_bhe[0])
95 {
96 auto const& bhe_nodes =
97 bhe_mesh_data.BHE_topology_ordered_nodes[id];
98 result = bhes_map.try_emplace(
99 id, bhe_creator_it->second(bhe_config, parameters, curves,
100 bhe_nodes));
101 }
102 else
103 {
104 // ConfigTree enforces single-read semantics, so we cannot
105 // re-parse the same config block. Grouped BHEs share all
106 // configuration except the borehole geometry, which is rebuilt
107 // per-ID from each BHE's own node set.
108 auto const& first_bhe =
109 bhes_map.find(bhe_ids_of_this_bhe[0])->second;
110 auto const& bhe_nodes =
111 bhe_mesh_data.BHE_topology_ordered_nodes[id];
112 result = bhes_map.try_emplace(
113 id,
114 std::visit(
115 [&](auto const& bhe) -> BHE::BHETypes
116 {
117 return bhe.withGeometry(
118 bhe.borehole_geometry.rebuildForNodes(bhe_nodes));
119 },
120 first_bhe));
121 }
122 if (!result.second)
123 {
124 OGS_FATAL(
125 "BHE with id '{:d}' is already present in the list! Check for "
126 "duplicate definitions of BHE ids.",
127 id);
128 }
129 }
130}
131
132std::unique_ptr<Process> createHeatTransportBHEProcess(
133 std::string const& name,
134 MeshLib::Mesh& mesh,
135 std::unique_ptr<ProcessLib::AbstractJacobianAssembler>&& jacobian_assembler,
136 std::vector<ProcessVariable> const& variables,
137 std::vector<std::unique_ptr<ParameterLib::ParameterBase>>& parameters,
138 unsigned const integration_order,
139 BaseLib::ConfigTree const& config,
140 std::map<std::string,
141 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
142 curves,
143 std::map<int, std::shared_ptr<MaterialPropertyLib::Medium>> const& media)
144{
146 config.checkConfigParameter("type", "HEAT_TRANSPORT_BHE");
147
148 DBUG("Create HeatTransportBHE Process.");
149
151
153 auto const pv_config = config.getConfigSubtree("process_variables");
154 std::vector<std::vector<std::reference_wrapper<ProcessVariable>>>
155 process_variables;
156
157 // reading primary variables for each
158 // BHE----------------------------------------------------------
160 auto range =
162 pv_config.getConfigParameterList<std::string>("process_variable");
163 std::vector<std::reference_wrapper<ProcessVariable>> per_process_variables;
164
165 for (std::string const& pv_name : range)
166 {
167 if (pv_name != "temperature_soil" &&
168 pv_name.find("temperature_BHE") == std::string::npos)
169 {
170 OGS_FATAL(
171 "Found a process variable name '{}'. It should be "
172 "'temperature_soil' or 'temperature_BHE_X'",
173 pv_name);
174 }
175 auto variable = std::find_if(variables.cbegin(), variables.cend(),
176 [&pv_name](ProcessVariable const& v)
177 { return v.getName() == pv_name; });
178
179 if (variable == variables.end())
180 {
181 OGS_FATAL(
182 "Could not find process variable '{:s}' in the provided "
183 "variables list for config tag <{:s}>.",
184 pv_name, "process_variable");
185 }
186 DBUG("Found process variable '{:s}' for config tag <{:s}>.",
187 variable->getName(), "process_variable");
188
189 per_process_variables.emplace_back(
190 const_cast<ProcessVariable&>(*variable));
191 }
192 process_variables.push_back(std::move(per_process_variables));
193 // end of reading primary variables for each
194 // BHE----------------------------------------------------------
195
197 // reading BHE parameters --------------------------------------------------
198
199 auto bhe_mesh_data = getBHEDataInMesh(mesh);
200
201 auto const& bhe_configs =
203 config.getConfigSubtree("borehole_heat_exchangers");
204
205 auto const using_server_communication =
207 config.getConfigParameter<bool>("use_server_communication", false);
208
209 auto const mass_lumping =
211 config.getConfigParameter<bool>("mass_lumping", false);
212
213 auto const using_algebraic_bc =
215 config.getConfigParameter<bool>("use_algebraic_bc", false);
216
217 auto const weighting_factor =
219 config.getConfigParameter<float>("weighting_factor", 1000.0);
220
221 auto const is_linear =
223 config.getConfigParameter<bool>("linear", false);
224 if (is_linear)
225 {
226 if (!using_algebraic_bc)
227 {
228 WARN(
229 "You specified that the process simulated by OGS is linear. "
230 "With that optimization the process will be assembled only "
231 "once and the non-linear solver will only do iterations per "
232 "time step to fulfill the BHE boundary conditions. No other "
233 "non-linearities will be resolved and OGS will not detect if "
234 "there are any non-linearities. It is your responsibility to "
235 "ensure that the assembled equation systems are linear, "
236 "indeed! There is no safety net!");
237 }
238 else
239 {
240 WARN(
241 "You specified that the process simulated by OGS is linear. "
242 "With that optimization the process will be assembled only "
243 "once and the non-linear solver will do only one iteration per "
244 "time step. No non-linearities will be resolved and OGS will "
245 "not detect if there are any non-linearities. It is your "
246 "responsibility to ensure that the assembled equation systems "
247 "are linear, indeed! There is no safety net!");
248 }
249 }
250
251 std::map<int, BHE::BHETypes> bhes_map;
252
253 int bhe_iterator = 0;
254
255 for (
256 auto const& bhe_config :
258 bhe_configs.getConfigSubtreeList("borehole_heat_exchanger"))
259 {
260 auto const bhe_id_string =
262 bhe_config.getConfigAttribute<std::string>(
263 "id", std::to_string(bhe_iterator));
264
265 std::vector<int> bhe_ids_of_this_bhe;
266
267 if (bhe_id_string == "*")
268 {
269 int const size = static_cast<int>(bhe_mesh_data.BHE_mat_IDs.size());
270 bhe_ids_of_this_bhe.resize(size);
271 std::iota(bhe_ids_of_this_bhe.begin(), bhe_ids_of_this_bhe.end(),
272 0);
273 }
274 else
275 {
276 bhe_ids_of_this_bhe =
278 }
279
280 // read in the parameters
281 const std::string bhe_type =
283 bhe_config.getConfigParameter<std::string>("type");
284
285 createAndInsertBHE(bhe_type, bhe_ids_of_this_bhe, bhe_config,
286 parameters, curves, bhe_mesh_data, bhes_map);
287 bhe_iterator++;
288 }
289
290 if (static_cast<int>(bhes_map.size()) - 1 != bhes_map.rbegin()->first)
291 {
292 OGS_FATAL(
293 "The maximum given BHE id '{:d}' did not match the number of given "
294 "BHE definitions '{:d}'. The BHE ids needs to be defined starting "
295 "from 0, so the maximum BHE id needs to be number of BHE "
296 "definitions minus 1. After all definitions there are no gaps "
297 "allowed between the given ids.",
298 bhes_map.rbegin()->first, bhes_map.size());
299 }
300
301 std::vector<BHE::BHETypes> bhes;
302 bhes.reserve(bhes_map.size());
303 std::ranges::copy(bhes_map | std::views::values, std::back_inserter(bhes));
304 bhe_mesh_data.updateElementSectionIndices(bhes);
305 // end of reading BHE parameters
306 // -------------------------------------------
307
308 auto media_map =
310
311 // find if bhe uses python boundary condition
312 auto const using_tespy =
313 visit([](auto const& bhe) { return bhe.use_python_bcs; }, bhes[0]);
314
317 // create a pythonBoundaryCondition object
318 if (using_tespy || using_server_communication)
319 {
320 // Evaluate Python code in scope of main module
321 pybind11::object scope =
322 pybind11::module::import("__main__").attr("__dict__");
323
324 if (!scope.contains("bc_bhe"))
325 OGS_FATAL(
326 "Function 'bc_bhe' is not defined in the python script file, "
327 "or there was no python script file specified.");
328
329 py_object =
330 scope["bc_bhe"]
332
333 if (py_object == nullptr)
334 OGS_FATAL(
335 "Not able to access the correct bc pointer from python script "
336 "file specified.");
337
338 // create BHE network dataframe from Python
339 py_object->dataframe_network = py_object->initializeDataContainer();
340 if (!py_object->isOverriddenEssential())
341 {
342 DBUG(
343 "Method `initializeDataContainer' not overridden in Python "
344 "script.");
345 }
346 // clear ogs bc_node_id memory in dataframe
347 std::get<3>(py_object->dataframe_network).clear(); // ogs_bc_node_id
348
349 // here calls the tespyHydroSolver to get the pipe flow velocity in bhe
350 // network
351 /* for 2U type the flowrate initialization process below causes conflict
352 // replace the value in flow velocity Matrix _u
353 auto const tespy_flow_rate = std::get<4>(py_object->dataframe_network);
354 const std::size_t n_bhe = tespy_flow_rate.size();
355 if (bhes.size() != n_bhe)
356 OGS_FATAL(
357 "The number of BHEs defined in OGS and TESPy are not the "
358 "same!");
359
360 for (std::size_t idx_bhe = 0; idx_bhe < n_bhe; idx_bhe++)
361 {
362 // the flow_rate in OGS should be updated from the flow_rate
363 // computed by TESPy.
364 auto update_flow_rate = [&](auto& bhe) {
365 bhe.updateHeatTransferCoefficients(tespy_flow_rate[idx_bhe]);
366 };
367 visit(update_flow_rate, bhes[idx_bhe]);
368 }
369 */
370 }
371
372 HeatTransportBHEProcessData process_data(
373 std::move(media_map), std::move(bhes), py_object, using_tespy,
374 using_server_communication, mass_lumping,
375 {using_algebraic_bc, weighting_factor}, is_linear);
376
377 SecondaryVariableCollection secondary_variables;
378
379 ProcessLib::createSecondaryVariables(config, secondary_variables);
380
381 return std::make_unique<HeatTransportBHEProcess>(
382 std::move(name), mesh, std::move(jacobian_assembler), parameters,
383 integration_order, std::move(process_variables),
384 std::move(process_data), std::move(secondary_variables),
385 std::move(bhe_mesh_data));
386}
387} // namespace HeatTransportBHE
388} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:19
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
T getConfigParameter(std::string const &param) const
ConfigTree getConfigSubtree(std::string const &root) const
Range< ValueIterator< T > > getConfigParameterList(std::string const &param) const
void checkConfigParameter(std::string const &param, std::string_view const value) const
Handles configuration of several secondary variables from the project file.
std::vector< int > splitMaterialIdString(std::string const &material_id_string)
MaterialSpatialDistributionMap createMaterialSpatialDistributionMap(std::map< int, std::shared_ptr< Medium > > const &media, MeshLib::Mesh const &mesh)
T_BHE createBHECoaxial(BaseLib::ConfigTree const &config, std::vector< std::unique_ptr< ParameterLib::ParameterBase > > &parameters, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves, std::vector< MeshLib::Node * > const &bhe_nodes)
std::variant< BHE_1U, BHE_CXA, BHE_CXC, BHE_2U, BHE_1P > BHETypes
Definition BHETypes.h:19
T_BHE createBHEUType(BaseLib::ConfigTree const &config, std::vector< std::unique_ptr< ParameterLib::ParameterBase > > &parameters, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves, std::vector< MeshLib::Node * > const &bhe_nodes)
T_BHE createBHE1PType(BaseLib::ConfigTree const &config, std::vector< std::unique_ptr< ParameterLib::ParameterBase > > &parameters, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves, std::vector< MeshLib::Node * > const &bhe_nodes)
std::function< BHE::BHETypes( BaseLib::ConfigTree const &, std::vector< std::unique_ptr< ParameterLib::ParameterBase > > &, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &, std::vector< MeshLib::Node * > const &)> BHECreatorFunc
std::map< std::string_view, BHECreatorFunc > bheCreators
void createAndInsertBHE(const std::string &bhe_type, const std::vector< int > &bhe_ids_of_this_bhe, const BaseLib::ConfigTree &bhe_config, std::vector< std::unique_ptr< ParameterLib::ParameterBase > > &parameters, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves, BHEMeshData const &bhe_mesh_data, std::map< int, BHE::BHETypes > &bhes_map)
std::unique_ptr< Process > createHeatTransportBHEProcess(std::string const &name, MeshLib::Mesh &mesh, std::unique_ptr< ProcessLib::AbstractJacobianAssembler > &&jacobian_assembler, std::vector< ProcessVariable > const &variables, std::vector< std::unique_ptr< ParameterLib::ParameterBase > > &parameters, unsigned const integration_order, BaseLib::ConfigTree const &config, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves, std::map< int, std::shared_ptr< MaterialPropertyLib::Medium > > const &media)
BHEMeshData getBHEDataInMesh(MeshLib::Mesh const &mesh)
void createSecondaryVariables(BaseLib::ConfigTree const &config, SecondaryVariableCollection &secondary_variables)
std::vector< std::vector< MeshLib::Node * > > BHE_nodes
std::vector< std::vector< MeshLib::Node * > > BHE_topology_ordered_nodes