https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MultiControlDrumFunction.C
Go to the documentation of this file.
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
11
13
16{
19 "A function that returns an absorber fraction for multiple control drums application.");
20 params.addRequiredParam<MeshGeneratorName>(
21 "mesh_generator",
22 "Name of the mesh generator to be used to retrieve control drums information.");
23 params.addRequiredParam<std::vector<Real>>(
24 "angular_speeds", "Vector of angular rotation speeds of the control drum.");
25 params.addRequiredParam<std::vector<Real>>(
26 "start_angles",
27 "Vector of initial angular positions of the beginning of the absorber components.");
28 params.addRequiredParam<std::vector<Real>>(
29 "angle_ranges", "Vector of angular ranges of the beginning of the absorber components.");
30 params.addParam<Real>(
31 "rotation_start_time", 0.0, "The time point at which the control drums start rotating.");
32 params.addParam<Real>("rotation_end_time",
33 std::numeric_limits<Real>::max(),
34 "The time point at which the control drums stop rotating.");
35 params.addParam<bool>(
36 "use_control_drum_id", true, "Whether extra element id user_control_drum_id is used.");
37 params.addParamNamesToGroup("use_control_drum_id", "Advanced");
38
39 return params;
40}
41
43 : Function(parameters),
46 _mesh_generator(getParam<MeshGeneratorName>("mesh_generator")),
47 _angular_speeds(getParam<std::vector<Real>>("angular_speeds")),
48 _start_angles(getParam<std::vector<Real>>("start_angles")),
49 _angle_ranges(getParam<std::vector<Real>>("angle_ranges")),
50 _rotation_start_time(getParam<Real>("rotation_start_time")),
51 _rotation_end_time(getParam<Real>("rotation_end_time")),
52 _use_control_drum_id(getParam<bool>("use_control_drum_id")),
53 _control_drum_id(_use_control_drum_id ? getElementIDByName("control_drum_id")
54 : DofObject::invalid_id),
55 _control_drum_positions(
56 getMeshProperty<std::vector<Point>>("control_drum_positions", _mesh_generator)),
57 _control_drums_azimuthal_meta(getMeshProperty<std::vector<std::vector<Real>>>(
58 "control_drums_azimuthal_meta", _mesh_generator))
59{
60 if (_angular_speeds.size() != _start_angles.size())
61 paramError("start_angles",
62 "the size of 'start_angles' must be identical to the size of 'angular_speeds'");
63 if (_angular_speeds.size() != _angle_ranges.size())
64 paramError("angle_ranges",
65 "the size of 'angle_ranges' must be identical to the size of 'angular_speeds'");
66 if (_angular_speeds.size() != _control_drum_positions.size())
67 paramError("angular_speeds",
68 "the size of this parameter must be identical to the control drum number recorded "
69 "in the MeshMetaData.");
71 paramError("rotation_end_time", "this parameter must be larger than rotation_start_time.");
72}
73
74Real
75MultiControlDrumFunction::value(Real t, const Point & p) const
76{
77 // Calculate the effective rotation time
78 const Real t_eff = t < _rotation_start_time
79 ? 0.0
82 // Find the closest control drum for a given Point p; only needed if control drum id is not used.
83 std::vector<std::pair<Real, unsigned int>> meshcontrol_drum_dist_vec;
85 {
86 for (unsigned int i = 0; i < _control_drum_positions.size(); i++)
87 {
88 Real cd_dist = std::sqrt(Utility::pow<2>(p(0) - _control_drum_positions[i](0)) +
89 Utility::pow<2>(p(1) - _control_drum_positions[i](1)));
90 meshcontrol_drum_dist_vec.push_back(std::make_pair(cd_dist, i));
91 }
92 std::sort(meshcontrol_drum_dist_vec.begin(), meshcontrol_drum_dist_vec.end());
93 }
94 const unsigned int cd_id = _use_control_drum_id
95 ? (_control_drum_id > 0 ? _control_drum_id - 1 : 0)
96 : meshcontrol_drum_dist_vec.front().second;
97 const auto & cd_pos = _control_drum_positions[cd_id];
98
99 Real dynamic_start_angle = (_angular_speeds[cd_id] * t_eff + _start_angles[cd_id]) / 180.0 * M_PI;
100 Real dynamic_end_angle = dynamic_start_angle + _angle_ranges[cd_id] / 180.0 * M_PI;
101
102 dynamic_start_angle = atan2(std::sin(dynamic_start_angle),
103 std::cos(dynamic_start_angle)) /
104 M_PI * 180.0; // quick way to move to -M_PI to M_PI
105 dynamic_end_angle = atan2(std::sin(dynamic_end_angle),
106 std::cos(dynamic_end_angle)) /
107 M_PI * 180.0; // quick way to move to -M_PI to M_PI
108
109 // Locate the first mesh azimuthal angle that is greater than dynamic_start_angle
110 auto start_bound = std::upper_bound(_control_drums_azimuthal_meta[cd_id].begin(),
112 dynamic_start_angle);
113 // Locate the first mesh azimuthal angle that is greater than dynamic_end_angle
114 auto end_bound = std::upper_bound(_control_drums_azimuthal_meta[cd_id].begin(),
116 dynamic_end_angle);
117
118 // Locate the elements that contain the start/end angles.
119 // This part seems long; but it only solves one simple issue -> transition from M_PI to -M_PI
120
121 // The two azimuthal ends of the element that the start angle intercepts;
122 // and the two azimuthal ends of the element that the end angle intercepts;
123 Real start_low, start_high, end_low, end_high;
124 // This means that the dynamic_start_angle is greater than the lowest mesh azimuthal angle and
125 // lower than the greatest mesh azimuthal angle. Namely, dynamic_start_angle does not cause the
126 // "transition from M_PI to -M_PI" issue.
127 if (start_bound != _control_drums_azimuthal_meta[cd_id].begin() &&
128 start_bound != _control_drums_azimuthal_meta[cd_id].end())
129 {
130 start_low = *(start_bound - 1);
131 start_high = *start_bound;
132 }
133 // On the contrary, this means the dynamic_start_angle intercepts an element across the
134 // M_PI(-M_PI) azimuthal angle. Namely, start_high is actually lower than start_low because of
135 // this transition.
136 else
137 {
138 start_high = _control_drums_azimuthal_meta[cd_id].front();
139 start_low = _control_drums_azimuthal_meta[cd_id].back();
140 }
141
142 // This means that dynamic_end_angle is greater than the lowest mesh azimuthal angle and lower
143 // than the greatest mesh azimuthal angle. Namely, dynamic_end_angle does not cause the
144 // "transition from M_PI to -M_PI" issue.
145 if (end_bound != _control_drums_azimuthal_meta[cd_id].begin() &&
146 end_bound != _control_drums_azimuthal_meta[cd_id].end())
147 {
148 end_low = *(end_bound - 1);
149 end_high = *end_bound;
150 }
151 // On the contrary, this means the dynamic_end_angle intercepts an element across the
152 // M_PI(-M_PI) azimuthal angle. Namely, end_high is actually lower than end_low because of
153 // this transition.
154 else
155 {
156 end_high = _control_drums_azimuthal_meta[cd_id].front();
157 end_low = _control_drums_azimuthal_meta[cd_id].back();
158 }
159
160 // azimuthal_p is the azimuthal angle of the point whose functon value needs to be calculated.
161 Real azimuthal_p = atan2(p(1) - cd_pos(1), p(0) - cd_pos(0)) / M_PI * 180;
162
163 // If there is not periodical transition from M_PI to -M_PI, we always have:
164 // start_low < start_high < end_low < end_high
165 // In the presence of "transition from M_PI to -M_PI", the relation above is compromised.
166 // Based on how the relation is compromised, we can tell where the transition occurs.
167
168 // Most trivial scenario -> no transition from M_PI to -M_PI is involved (it happens to a pure
169 // reflector element)
170 if (end_high >= start_low)
171 {
172 // the point is located in an element that is purely absorber.
173 if (azimuthal_p >= start_high && azimuthal_p <= end_low)
174 return 100.0;
175 // the point is located in an element that is purely reflector.
176 else if (azimuthal_p <= start_low || azimuthal_p >= end_high)
177 return 0.0;
178 // the point is located in a mixed element intercepted by dynamic_start_angle so that volume
179 // fraction is calculated.
180 else if (azimuthal_p < start_high && azimuthal_p > start_low)
181 {
182 Real start_interval = start_high - start_low;
183 Real stab_interval = start_high - dynamic_start_angle;
184 return stab_interval / start_interval * 100.0;
185 }
186 // the point is located in a mixed element intercepted by dynamic_end_angle so that volume
187 // fraction is calculated.
188 else
189 {
190 Real end_interval = end_high - end_low;
191 Real endab_interval = dynamic_end_angle - end_low;
192 return endab_interval / end_interval * 100.0;
193 }
194 }
195 // The element intercepted by dynamic_end_angle has the "transition from M_PI to -M_PI" issue.
196 // This is still relatively simple because only one of the mixed element is affected.
197 else if (end_low >= end_high)
198 {
199 // (Not affected) the point is located in an element that is purely absorber.
200 if (azimuthal_p >= start_high && azimuthal_p <= end_low)
201 return 100.0;
202 // (Affected) the point is located in an element that is purely reflector.
203 else if (azimuthal_p <= start_low && azimuthal_p >= end_high)
204 return 0.0;
205 // (Not affected) the point is located in a mixed element intercepted by dynamic_start_angle so
206 // that volume fraction is calculated.
207 else if (azimuthal_p < start_high && azimuthal_p > start_low)
208 {
209 Real start_interval = start_high - start_low;
210 Real stab_interval = start_high - dynamic_start_angle;
211 return stab_interval / start_interval * 100.0;
212 }
213 // (Affected) the point is located in a mixed element intercepted by dynamic_end_angle so that
214 // volume fraction is calculated. As this element is also intercepted by azimuthal_angle = M_PI
215 // (-M_PI), an extra 2 * M_PI (360 degrees) shift is used to correct this transition effect.
216 else
217 {
218 Real end_interval = end_high - end_low + 360.0;
219 Real endab_interval = (dynamic_end_angle - end_low >= 0.0)
220 ? (dynamic_end_angle - end_low)
221 : (dynamic_end_angle - end_low + 360.0);
222 return endab_interval / end_interval * 100.0;
223 }
224 }
225 // The "transition from M_PI to -M_PI" happens to an pure absorber element
226 else if (start_high >= end_low)
227 {
228 // (Affected) the point is located in an element that is purely absorber.
229 if (azimuthal_p >= start_high || azimuthal_p <= end_low)
230 return 100.0;
231 // (Affected) the point is located in an element that is purely reflector.
232 else if (azimuthal_p >= end_high && azimuthal_p <= start_low)
233 return 0.0;
234 // (Not affected) the point is located in a mixed element intercepted by dynamic_start_angle so
235 // that volume fraction is calculated.
236 else if (azimuthal_p < start_high && azimuthal_p > start_low)
237 {
238 Real start_interval = start_high - start_low;
239 Real stab_interval = start_high - dynamic_start_angle;
240 return stab_interval / start_interval * 100.0;
241 }
242 // (Not affected) the point is located in a mixed element intercepted by dynamic_end_angle so
243 // that volume fraction is calculated.
244 else
245 {
246 Real end_interval = end_high - end_low;
247 Real endab_interval = dynamic_end_angle - end_low;
248 return endab_interval / end_interval * 100.0;
249 }
250 }
251 // The element intercepted by dynamic_start_angle has the "transition from M_PI to -M_PI" issue.
252 // This is still relatively simple because only one of the mixed element is affected.
253 else
254 {
255 // (Not affected) the point is located in an element that is purely absorber.
256 if (azimuthal_p >= start_high && azimuthal_p <= end_low)
257 return 100.0;
258 // (Affected) the point is located in an element that is purely reflector.
259 else if (azimuthal_p >= end_high && azimuthal_p <= start_low)
260 return 0.0;
261 // (Affected) the point is located in a mixed element intercepted by dynamic_start_angle so that
262 // volume fraction is calculated. As this element is also intercepted by azimuthal_angle = M_PI
263 // (-M_PI), an extra 2 * M_PI (360 degrees) shift is used to correct this transition effect.
264 else if (azimuthal_p > start_low || azimuthal_p < start_high)
265 {
266 Real start_interval = start_high - start_low + 360.0;
267 Real stab_interval = (start_high - dynamic_start_angle >= 0.0)
268 ? (start_high - dynamic_start_angle)
269 : (start_high - dynamic_start_angle + 360.0);
270 return stab_interval / start_interval * 100.0;
271 }
272 // (Not affected) the point is located in a mixed element intercepted by dynamic_end_angle so
273 // that volume fraction is calculated.
274 else
275 {
276 Real end_interval = end_high - end_low;
277 Real endab_interval = dynamic_end_angle - end_low;
278 return endab_interval / end_interval * 100.0;
279 }
280 }
281}
const Real p
registerMooseObject("ReactorApp", MultiControlDrumFunction)
static InputParameters validParams()
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void paramError(const std::string &param, Args... args) const
A function that returns an absorber fraction for multiple control drums application.
const std::vector< Real > _angular_speeds
Vector of angular speeds of control drums.
const std::vector< Point > & _control_drum_positions
MeshMetaData: positions of control drums.
const dof_id_type & _control_drum_id
ExtraElementID: control drum ExtraElementID.
virtual Real value(Real t, const Point &p) const override
const std::vector< Real > _start_angles
Vector of initial starting angles of control drums.
MultiControlDrumFunction(const InputParameters &parameters)
const Real _rotation_end_time
End time of control drums rotation.
const std::vector< std::vector< Real > > & _control_drums_azimuthal_meta
MeshMetaData: vector of azimuthal angles of all nodes of each control drum.
static InputParameters validParams()
const std::vector< Real > _angle_ranges
Vector of angular ranges of control drums.
const Real _rotation_start_time
Start time of control drums rotation.
const bool _use_control_drum_id
Whether extra element id user_control_drum_id is used.