Line data Source code
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 :
10 : #include "FlowChannelBase.h"
11 : #include "HeatTransferBase.h"
12 : #include "ClosuresBase.h"
13 : #include "ThermalHydraulicsApp.h"
14 : #include "THMMesh.h"
15 :
16 : #include "libmesh/edge_edge2.h"
17 : #include "libmesh/edge_edge3.h"
18 :
19 : const std::map<std::string, FlowChannelBase::EConvHeatTransGeom>
20 : FlowChannelBase::_heat_transfer_geom_to_enum{
21 : {"PIPE", PIPE}, {"ROD_BUNDLE", ROD_BUNDLE}, {"HEX_ROD_BUNDLE", HEX_ROD_BUNDLE}};
22 :
23 : const std::map<std::string, FlowChannelBase::EPipeType> FlowChannelBase::_pipe_type_to_enum{
24 : {"STRAIGHT", STRAIGHT}, {"CURVED", CURVED}, {"DOWNCOMER", DOWNCOMER}};
25 :
26 : const std::map<std::string, FlowChannelBase::EPipeLocation> FlowChannelBase::_pipe_location_to_enum{
27 : {"INTERIOR", INTERIOR}, {"EDGE", EDGE}, {"CORNER", CORNER}};
28 :
29 : MooseEnum
30 4332 : FlowChannelBase::getConvHeatTransGeometry(const std::string & name)
31 : {
32 4332 : return THM::getMooseEnum<EConvHeatTransGeom>(name, _heat_transfer_geom_to_enum);
33 : }
34 :
35 : MooseEnum
36 0 : FlowChannelBase::getPipeType(const std::string & name)
37 : {
38 0 : return THM::getMooseEnum<EPipeType>(name, _pipe_type_to_enum);
39 : }
40 :
41 : MooseEnum
42 4332 : FlowChannelBase::getPipeLocation(const std::string & name)
43 : {
44 4332 : return THM::getMooseEnum<EPipeLocation>(name, _pipe_location_to_enum);
45 : }
46 :
47 : template <>
48 : FlowChannelBase::EConvHeatTransGeom
49 2165 : THM::stringToEnum(const std::string & s)
50 : {
51 2165 : return stringToEnum<FlowChannelBase::EConvHeatTransGeom>(
52 2165 : s, FlowChannelBase::_heat_transfer_geom_to_enum);
53 : }
54 :
55 : template <>
56 : FlowChannelBase::EPipeType
57 0 : THM::stringToEnum(const std::string & s)
58 : {
59 0 : return stringToEnum<FlowChannelBase::EPipeType>(s, FlowChannelBase::_pipe_type_to_enum);
60 : }
61 :
62 : template <>
63 : FlowChannelBase::EPipeLocation
64 2165 : THM::stringToEnum(const std::string & s)
65 : {
66 2165 : return stringToEnum<FlowChannelBase::EPipeLocation>(s, FlowChannelBase::_pipe_location_to_enum);
67 : }
68 :
69 : InputParameters
70 4332 : FlowChannelBase::validParams()
71 : {
72 4332 : InputParameters params = Component1D::validParams();
73 4332 : params += GravityInterface::validParams();
74 :
75 8664 : params.addRequiredParam<UserObjectName>("fp", "Fluid properties user object");
76 8664 : params.addRequiredParam<FunctionName>(
77 : "A", "Area of the flow channel, can be a constant or a function");
78 8664 : params.addParam<Real>("roughness", 0.0, "Roughness [m]");
79 8664 : params.addParam<FunctionName>("f", "Wall friction factor [-]");
80 8664 : params.addParam<MooseEnum>("heat_transfer_geom",
81 12996 : FlowChannelBase::getConvHeatTransGeometry("PIPE"),
82 : "Convective heat transfer geometry");
83 8664 : params.addParam<MooseEnum>("pipe_location",
84 12996 : FlowChannelBase::getPipeLocation("INTERIOR"),
85 : "Pipe location within the bundle");
86 8664 : params.addParam<Real>("PoD", 1, "Pitch-to-diameter ratio for parallel bundle heat transfer [-]");
87 8664 : params.addParam<bool>(
88 : "pipe_pars_transferred",
89 8664 : false,
90 : "Set to true if Dh, P_hf and A are going to be transferred in from an external source");
91 8664 : params.addParam<std::vector<std::string>>(
92 : "closures",
93 : {},
94 : "Closures object(s). This is optional since closure relations can be supplied directly by "
95 : "Materials as well.");
96 8664 : params.addParam<bool>("name_multiple_ht_by_index",
97 8664 : true,
98 : "If true, when there are multiple heat transfer components connected to "
99 : "this flow channel, use their index for naming related quantities; "
100 : "otherwise, use the name of the heat transfer component.");
101 8664 : params.addParam<std::vector<VariableName>>(
102 : "vpp_vars", {}, "Variables to add in an ElementValueSampler");
103 :
104 8664 : params.setDocString(
105 : "orientation",
106 : "Direction of flow channel from start position to end position (no need to normalize). For "
107 : "curved flow channels, it is the (tangent) direction at the start position.");
108 :
109 8664 : params.addPrivateParam<std::string>("component_type", "pipe");
110 :
111 4332 : return params;
112 0 : }
113 :
114 2165 : FlowChannelBase::FlowChannelBase(const InputParameters & params)
115 : : Component1D(params),
116 : GravityInterface(params),
117 :
118 : _flow_model(nullptr),
119 4330 : _fp_name(getParam<UserObjectName>("fp")),
120 2165 : _gravity_angle(MooseUtils::absoluteFuzzyEqual(_gravity_magnitude, 0.0)
121 2165 : ? 0.0
122 623 : : std::acos(_dir * _gravity_vector / (_dir.norm() * _gravity_magnitude)) *
123 : 180 / M_PI),
124 4330 : _pipe_pars_transferred(getParam<bool>("pipe_pars_transferred")),
125 4330 : _roughness(getParam<Real>("roughness")),
126 2165 : _HT_geometry(getEnumParam<EConvHeatTransGeom>("heat_transfer_geom")),
127 2165 : _pipe_location(getEnumParam<EPipeLocation>("pipe_location")),
128 4330 : _PoD(getParam<Real>("PoD")),
129 4330 : _has_PoD(isParamValid("PoD")),
130 2165 : _temperature_mode(false),
131 2165 : _n_heat_transfer_connections(0)
132 : {
133 2165 : }
134 :
135 : std::shared_ptr<const FlowModel>
136 3386 : FlowChannelBase::getFlowModel() const
137 : {
138 3386 : checkSetupStatus(INITIALIZED_PRIMARY);
139 :
140 3384 : return _flow_model;
141 : }
142 :
143 : const FunctionName &
144 2480 : FlowChannelBase::getAreaFunctionName() const
145 : {
146 2480 : checkSetupStatus(INITIALIZED_PRIMARY);
147 :
148 2480 : return _area_function;
149 : }
150 :
151 : FunctionName
152 2142 : FlowChannelBase::createAreaFunctionAndGetName()
153 : {
154 : // Area function has already been created; just need to return its name
155 4284 : return getParam<FunctionName>("A");
156 : }
157 :
158 : void
159 2142 : FlowChannelBase::init()
160 : {
161 : Component1D::init();
162 :
163 2142 : _area_function = createAreaFunctionAndGetName();
164 :
165 4284 : _flow_model = buildFlowModel();
166 2142 : if (_flow_model)
167 : {
168 2142 : _flow_model->init();
169 :
170 4284 : const auto & closures_names = getParam<std::vector<std::string>>("closures");
171 4293 : for (const auto & closures_name : closures_names)
172 4302 : _closures_objects.push_back(getTHMProblem().getClosures(closures_name));
173 : }
174 2142 : }
175 :
176 : void
177 2142 : FlowChannelBase::initSecondary()
178 : {
179 : Component1D::initSecondary();
180 :
181 : // Determine heat transfer mode based on connected heat transfer components;
182 : // if at least one heat transfer component of temperature component is
183 : // connected, then it's temperature mode. Otherwise, it's heat flux mode, even
184 : // if no heat transfer components at all were provided - in that case, a zero
185 : // heat flux is added.
186 2681 : for (unsigned int i = 0; i < _heat_transfer_names.size(); i++)
187 : {
188 : const HeatTransferBase & heat_transfer =
189 : getComponentByName<HeatTransferBase>(_heat_transfer_names[i]);
190 539 : if (heat_transfer.isTemperatureType())
191 471 : _temperature_mode = true;
192 : }
193 2142 : }
194 :
195 : void
196 2088 : FlowChannelBase::check() const
197 : {
198 2088 : Component1D::check();
199 :
200 4185 : for (const auto & closures : _closures_objects)
201 2097 : closures->checkFlowChannel(*this);
202 :
203 : // check types of heat transfer for all sources; must be all of same type
204 2088 : if (_temperature_mode)
205 891 : for (unsigned int i = 0; i < _heat_transfer_names.size(); i++)
206 : {
207 : const HeatTransferBase & heat_transfer =
208 : getComponentByName<HeatTransferBase>(_heat_transfer_names[i]);
209 458 : if (!heat_transfer.isTemperatureType())
210 2 : logError("Heat sources for a flow channel must be all of temperature type or all of heat "
211 : "flux type");
212 : }
213 2088 : }
214 :
215 : void
216 2046 : FlowChannelBase::addVariables()
217 : {
218 : // This should be called after initSecondary() because it relies on the names
219 : // generated in initSecondary() of heat transfer components
220 2046 : getHeatTransferVariableNames();
221 :
222 2046 : _flow_model->addVariables();
223 :
224 : // total heat flux perimeter
225 2046 : if (_n_heat_transfer_connections > 1 && !_app.isRestarting())
226 : {
227 48 : const std::string class_name = "SumIC";
228 48 : InputParameters params = _factory.getValidParams(class_name);
229 96 : params.set<VariableName>("variable") = FlowModel::HEAT_FLUX_PERIMETER;
230 48 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
231 48 : params.set<std::vector<VariableName>>("values") = _P_hf_names;
232 96 : getTHMProblem().addSimInitialCondition(class_name, genName(name(), "P_hf_ic"), params);
233 48 : }
234 :
235 2046 : _flow_model->addInitialConditions();
236 2046 : }
237 :
238 : void
239 2046 : FlowChannelBase::addCommonObjects()
240 : {
241 2046 : ExecFlagEnum ts_execute_on(MooseUtils::getDefaultExecFlagEnum());
242 8184 : ts_execute_on = {EXEC_TIMESTEP_BEGIN, EXEC_INITIAL};
243 :
244 : {
245 2046 : std::string class_name = "DirectionMaterial";
246 2046 : InputParameters params = _factory.getValidParams(class_name);
247 2046 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
248 4092 : getTHMProblem().addMaterial(class_name, genName(name(), "dir_mat"), params);
249 2046 : }
250 :
251 2046 : if (!_pipe_pars_transferred)
252 : {
253 : // Area
254 : {
255 2046 : std::string class_name = "FunctionAux";
256 2046 : InputParameters params = _factory.getValidParams(class_name);
257 4092 : params.set<AuxVariableName>("variable") = FlowModel::AREA_LINEAR;
258 2046 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
259 2046 : params.set<FunctionName>("function") = _area_function;
260 2046 : params.set<ExecFlagEnum>("execute_on") = ts_execute_on;
261 4092 : const std::string aux_kernel_name = genName(name(), "area_linear_aux");
262 2046 : getTHMProblem().addAuxKernel(class_name, aux_kernel_name, params);
263 2046 : }
264 : {
265 2046 : const std::string class_name = "ProjectionAux";
266 2046 : InputParameters params = _factory.getValidParams(class_name);
267 4092 : params.set<AuxVariableName>("variable") = FlowModel::AREA;
268 4092 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
269 6138 : params.set<std::vector<VariableName>>("v") = {FlowModel::AREA_LINEAR};
270 2046 : params.set<ExecFlagEnum>("execute_on") = ts_execute_on;
271 4092 : const std::string aux_kernel_name = genName(name(), "area_aux");
272 2046 : getTHMProblem().addAuxKernel(class_name, aux_kernel_name, params);
273 2046 : }
274 : }
275 4092 : }
276 :
277 : void
278 2046 : FlowChannelBase::addMooseObjects()
279 : {
280 2046 : addCommonObjects();
281 :
282 2046 : ExecFlagEnum execute_on_initial_linear(MooseUtils::getDefaultExecFlagEnum());
283 8184 : execute_on_initial_linear = {EXEC_INITIAL, EXEC_LINEAR};
284 :
285 : // total heat flux perimeter aux kernel
286 2046 : if (_n_heat_transfer_connections > 1)
287 : {
288 48 : const std::string class_name = "SumAux";
289 48 : InputParameters params = _factory.getValidParams(class_name);
290 96 : params.set<AuxVariableName>("variable") = FlowModel::HEAT_FLUX_PERIMETER;
291 48 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
292 48 : params.set<std::vector<VariableName>>("values") = _P_hf_names;
293 48 : params.set<ExecFlagEnum>("execute_on") = execute_on_initial_linear;
294 96 : getTHMProblem().addAuxKernel(class_name, genName(name(), "P_hf_auxkernel"), params);
295 48 : }
296 :
297 : // weighted average wall heat flux aux kernel
298 2046 : if (!_temperature_mode)
299 : {
300 1634 : if (_n_heat_transfer_connections > 1)
301 : {
302 25 : const std::string class_name = "ADWeightedAverageMaterial";
303 25 : InputParameters params = _factory.getValidParams(class_name);
304 50 : params.set<MaterialPropertyName>("prop_name") = FlowModel::HEAT_FLUX_WALL;
305 25 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
306 25 : params.set<std::vector<MaterialPropertyName>>("values") = _q_wall_names;
307 25 : params.set<std::vector<VariableName>>("weights") = _P_hf_names;
308 50 : getTHMProblem().addMaterial(
309 25 : class_name, genName(name(), FlowModel::HEAT_FLUX_WALL, "w_avg_mat"), params);
310 25 : }
311 1609 : else if (_n_heat_transfer_connections == 0)
312 : {
313 1593 : const std::string class_name = "ADConstantMaterial";
314 1593 : InputParameters params = _factory.getValidParams(class_name);
315 1593 : params.set<std::string>("property_name") = FlowModel::HEAT_FLUX_WALL;
316 1593 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
317 1593 : params.set<Real>("value") = 0;
318 3186 : getTHMProblem().addMaterial(
319 1593 : class_name, genName(name(), FlowModel::HEAT_FLUX_WALL, "zero_mat"), params);
320 1593 : }
321 : }
322 :
323 4092 : const auto & vpp_vars = getParam<std::vector<VariableName>>("vpp_vars");
324 2046 : if (vpp_vars.size() > 0)
325 : {
326 45 : const std::string class_name = "ElementValueSampler";
327 45 : InputParameters params = _factory.getValidParams(class_name);
328 45 : params.set<std::vector<SubdomainName>>("block") = getSubdomainNames();
329 45 : params.set<std::vector<VariableName>>("variable") = vpp_vars;
330 90 : params.set<std::string>("sort_by") = sortBy();
331 180 : params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_TIMESTEP_END};
332 45 : getTHMProblem().addVectorPostprocessor(class_name, name() + "_vars_vpp", params);
333 45 : }
334 :
335 2046 : _flow_model->addMooseObjects();
336 :
337 4101 : for (const auto & closures : _closures_objects)
338 2055 : closures->addMooseObjectsFlowChannel(*this);
339 4137 : }
340 :
341 : void
342 539 : FlowChannelBase::addHeatTransferName(const std::string & name) const
343 : {
344 539 : _heat_transfer_names.push_back(name);
345 539 : _n_heat_transfer_connections++;
346 539 : }
347 :
348 : void
349 2046 : FlowChannelBase::getHeatTransferVariableNames()
350 : {
351 2547 : for (unsigned int i = 0; i < _n_heat_transfer_connections; i++)
352 : {
353 : const HeatTransferBase & heat_transfer =
354 501 : getComponentByName<HeatTransferBase>(_heat_transfer_names[i]);
355 :
356 501 : _P_hf_names.push_back(heat_transfer.getHeatedPerimeterName());
357 501 : _T_wall_names.push_back(heat_transfer.getWallTemperatureName());
358 501 : _T_wall_mat_names.push_back(heat_transfer.getWallTemperatureMatName());
359 501 : _q_wall_names.push_back(heat_transfer.getWallHeatFluxName());
360 : }
361 2046 : }
362 :
363 : std::string
364 1009 : FlowChannelBase::getHeatTransferNamesSuffix(const std::string & ht_name) const
365 : {
366 1009 : checkSetupStatus(INITIALIZED_PRIMARY);
367 :
368 : // if there is more than one connected heat transfer component, then number them
369 1009 : if (_n_heat_transfer_connections > 1)
370 : {
371 : // determine index of heat transfer name based on when it was added
372 200 : auto it = std::find(_heat_transfer_names.begin(), _heat_transfer_names.end(), ht_name);
373 200 : if (it != _heat_transfer_names.end())
374 : {
375 200 : const unsigned int index = std::distance(_heat_transfer_names.begin(), it);
376 :
377 200 : std::string suffix = ":";
378 400 : if (getParam<bool>("name_multiple_ht_by_index"))
379 336 : suffix += std::to_string(index + 1);
380 : else
381 32 : suffix += _heat_transfer_names[index];
382 :
383 200 : return suffix;
384 : }
385 : else
386 0 : mooseError(
387 : "Heat transfer component '", ht_name, "' was not added to flow channel '", name(), "'");
388 : }
389 : // else, don't add a suffix; there is no need
390 : else
391 809 : return "";
392 : }
393 :
394 : std::vector<std::string>
395 0 : FlowChannelBase::getHeatTransferNames() const
396 : {
397 0 : checkSetupStatus(INITIALIZED_PRIMARY);
398 :
399 0 : return _heat_transfer_names;
400 : }
|