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 "HSCoupler2D2DRadiation.h"
11 : #include "HeatStructureCylindricalBase.h"
12 : #include "THMMesh.h"
13 : #include "MooseUtils.h"
14 :
15 : registerMooseObject("ThermalHydraulicsApp", HSCoupler2D2DRadiation);
16 :
17 : InputParameters
18 116 : HSCoupler2D2DRadiation::validParams()
19 : {
20 116 : InputParameters params = BoundaryBase::validParams();
21 :
22 232 : params.addRequiredParam<std::vector<std::string>>("heat_structures",
23 : "The heat structures to couple");
24 232 : params.addRequiredParam<std::vector<BoundaryName>>(
25 : "boundaries", "The boundaries of the heat structures to couple");
26 232 : params.addRequiredRangeCheckedParam<std::vector<Real>>(
27 : "emissivities",
28 : "emissivities > 0 & emissivities < 1",
29 : "Emissivities of each heat structure surface");
30 232 : params.addRequiredRangeCheckedParam<std::vector<std::vector<Real>>>(
31 : "view_factors",
32 : "view_factors >= 0 & view_factors <= 1",
33 : "The view factors between each surface, as a matrix. The row/column ordering corresponds to "
34 : "the ordering in 'heat_structures', with an additional row and column if "
35 : "'include_environment' is set to 'true'. Each row must sum to one.");
36 232 : params.addRequiredParam<bool>(
37 : "include_environment",
38 : "Whether or not to include an environment surrounding all of the surfaces. If the heat "
39 : "structure surfaces themselves form an enclosure, then set this parameter to 'false'.");
40 232 : params.addParam<Real>(
41 : "T_environment",
42 : "Environment temperature [K]. Only set if 'include_environment' is set to true.");
43 :
44 116 : params.addClassDescription("Couples boundaries of multiple 2D heat structures via radiation");
45 :
46 116 : return params;
47 0 : }
48 :
49 58 : HSCoupler2D2DRadiation::HSCoupler2D2DRadiation(const InputParameters & parameters)
50 : : BoundaryBase(parameters),
51 :
52 58 : _hs_names(getParam<std::vector<std::string>>("heat_structures")),
53 116 : _hs_boundaries(getParam<std::vector<BoundaryName>>("boundaries")),
54 116 : _include_environment(getParam<bool>("include_environment")),
55 58 : _n_hs(_hs_names.size()),
56 58 : _n_surfaces(_include_environment ? _n_hs + 1 : _n_hs),
57 :
58 116 : _mesh_alignment(constMesh())
59 : {
60 210 : for (unsigned int i = 0; i < _n_hs; ++i)
61 152 : addDependency(_hs_names[i]);
62 58 : }
63 :
64 : void
65 58 : HSCoupler2D2DRadiation::setupMesh()
66 : {
67 58 : BoundaryBase::setupMesh();
68 :
69 : // If there is more than one heat structure, initialize mesh alignment and
70 : // augment the sparsity pattern
71 58 : if (_n_hs >= 2)
72 : {
73 : // get boundary info vectors for all boundaries
74 : std::vector<std::vector<std::tuple<dof_id_type, unsigned short int>>> boundary_infos;
75 210 : for (const auto i : index_range(_hs_names))
76 : {
77 : if (hasComponentByName<HeatStructureBase>(_hs_names[i]))
78 : {
79 : const auto & hs = getComponentByName<HeatStructureBase>(_hs_names[i]);
80 152 : if (i < _hs_boundaries.size() && hs.hasBoundary(_hs_boundaries[i]))
81 152 : boundary_infos.push_back(hs.getBoundaryInfo(_hs_boundaries[i]));
82 : }
83 : }
84 :
85 : // Initialize the alignment mapping
86 : if (hasComponentByName<HeatStructureBase>(_hs_names[0]))
87 : {
88 : const auto & hs = getComponentByName<HeatStructureBase>(_hs_names[0]);
89 58 : _mesh_alignment.initialize(boundary_infos, hs.getPosition(), hs.getDirection());
90 : }
91 :
92 : // Add entries to sparsity pattern for coupling
93 58 : if (_mesh_alignment.meshesAreAligned())
94 638 : for (const auto & primary_elem_id : _mesh_alignment.getPrimaryElemIDs())
95 : {
96 580 : const auto & coupled_elem_ids = _mesh_alignment.getCoupledSecondaryElemIDs(primary_elem_id);
97 : std::vector<dof_id_type> elem_ids;
98 580 : elem_ids.push_back(primary_elem_id);
99 580 : elem_ids.insert(elem_ids.end(), coupled_elem_ids.begin(), coupled_elem_ids.end());
100 :
101 2100 : for (unsigned int i = 0; i < elem_ids.size(); ++i)
102 3000 : for (unsigned int j = i + 1; j < elem_ids.size(); ++j)
103 1480 : getTHMProblem().augmentSparsity(elem_ids[i], elem_ids[j]);
104 : }
105 58 : }
106 58 : }
107 :
108 : void
109 58 : HSCoupler2D2DRadiation::check() const
110 : {
111 58 : BoundaryBase::check();
112 :
113 116 : checkEqualSize<BoundaryName, std::string>("boundaries", "heat_structures");
114 116 : checkEqualSize<Real, std::string>("emissivities", "heat_structures");
115 :
116 : // check view factor matrix dimensions
117 116 : const auto & view_factors = getParam<std::vector<std::vector<Real>>>("view_factors");
118 : bool correct_size = true;
119 58 : if (view_factors.size() == _n_surfaces)
120 : {
121 228 : for (const auto i : index_range(view_factors))
122 170 : if (view_factors[i].size() != _n_surfaces)
123 : correct_size = false;
124 : }
125 : else
126 : correct_size = false;
127 58 : if (!correct_size)
128 2 : logError("The parameter 'view_factors' must be a square matrix of size ",
129 2 : _n_surfaces,
130 : ". For example, a size 2 matrix is provided as '0.2 0.8; 0.7 0.3'. The row/column "
131 : "ordering corresponds to the ordering in 'heat_structures', with an additional "
132 : "row/column if 'include_environment' is set to 'true'.");
133 :
134 : // check that all view factor matrix rows sum to one
135 : bool all_row_sums_unity = true;
136 228 : for (const auto i : index_range(view_factors))
137 170 : if (!MooseUtils::absoluteFuzzyEqual(
138 : std::accumulate(view_factors[i].begin(), view_factors[i].end(), 0.0), 1.0))
139 : all_row_sums_unity = false;
140 58 : if (!all_row_sums_unity)
141 2 : logError("All rows in 'view_factors' must sum to one.");
142 :
143 58 : if (_n_hs > 1 && !_mesh_alignment.meshesAreAligned())
144 0 : logError("The meshes of the heat structures are not aligned.");
145 :
146 210 : for (const auto i : index_range(_hs_names))
147 : {
148 : // for now we only allow cylindrical heat structures
149 152 : checkComponentOfTypeExistsByName<HeatStructureCylindricalBase>(_hs_names[i]);
150 :
151 : if (hasComponentByName<HeatStructureBase>(_hs_names[i]))
152 : {
153 : const auto & hs = getComponentByName<HeatStructureBase>(_hs_names[i]);
154 152 : if (i < _hs_boundaries.size() && !hs.hasBoundary(_hs_boundaries[i]))
155 0 : logError("The heat structure '",
156 : _hs_names[i],
157 : "' does not have the boundary '",
158 : _hs_boundaries[i],
159 : "'.");
160 : }
161 : }
162 58 : }
163 :
164 : void
165 54 : HSCoupler2D2DRadiation::addMooseObjects()
166 : {
167 : // add side UO on 2D boundary to cache temperature values by element ID
168 108 : const UserObjectName temperature_uo_name = genName(name(), "temperature_uo");
169 : {
170 54 : const std::string class_name = "StoreVariableByElemIDSideUserObject";
171 54 : InputParameters params = _factory.getValidParams(class_name);
172 108 : params.set<std::vector<BoundaryName>>("boundary") = _hs_boundaries;
173 162 : params.set<std::vector<VariableName>>("variable") = {HeatConductionModel::TEMPERATURE};
174 270 : params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR, EXEC_NONLINEAR};
175 : // This UO needs to execute before the UO below
176 54 : params.set<int>("execution_order_group") = -1;
177 54 : getTHMProblem().addUserObject(class_name, temperature_uo_name, params);
178 54 : }
179 :
180 : // add side UO to compute heat fluxes across each boundary
181 108 : const UserObjectName hs_coupler_2d2d_uo_name = genName(name(), "uo");
182 : {
183 54 : const std::string class_name = "HSCoupler2D2DRadiationUserObject";
184 54 : InputParameters params = _factory.getValidParams(class_name);
185 162 : params.set<std::vector<BoundaryName>>("boundary") = {_hs_boundaries[0]};
186 270 : params.applySpecificParameters(
187 : parameters(), {"emissivities", "view_factors", "include_environment", "T_environment"});
188 54 : params.set<UserObjectName>("temperature_uo") = temperature_uo_name;
189 54 : params.set<MeshAlignment2D2D *>("mesh_alignment") = &_mesh_alignment;
190 270 : params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR, EXEC_NONLINEAR};
191 54 : getTHMProblem().addUserObject(class_name, hs_coupler_2d2d_uo_name, params);
192 54 : }
193 :
194 : // BCs
195 198 : for (unsigned int i = 0; i < _n_hs; ++i)
196 : {
197 144 : const auto & hs = getComponentByName<HeatStructureCylindricalBase>(_hs_names[i]);
198 :
199 144 : const std::string class_name = "HSCoupler2D2DRadiationRZBC";
200 144 : InputParameters params = _factory.getValidParams(class_name);
201 288 : params.set<NonlinearVariableName>("variable") = HeatConductionModel::TEMPERATURE;
202 432 : params.set<std::vector<BoundaryName>>("boundary") = {_hs_boundaries[i]};
203 144 : params.set<UserObjectName>("hs_coupler_2d2d_uo") = hs_coupler_2d2d_uo_name;
204 144 : params.set<Point>("axis_point") = hs.getPosition();
205 144 : params.set<RealVectorValue>("axis_dir") = hs.getDirection();
206 144 : getTHMProblem().addBoundaryCondition(class_name, genName(name(), class_name, i), params);
207 144 : }
208 270 : }
|