https://mooseframework.inl.gov
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 {
18  params.addClassDescription(
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),
45  ElementIDInterface(this),
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 
74 Real
75 MultiControlDrumFunction::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
81  : t - _rotation_start_time);
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(),
111  _control_drums_azimuthal_meta[cd_id].end(),
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(),
115  _control_drums_azimuthal_meta[cd_id].end(),
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 std::vector< Real > _angle_ranges
Vector of angular ranges of control drums.
A function that returns an absorber fraction for multiple control drums application.
static InputParameters validParams()
const dof_id_type & _control_drum_id
ExtraElementID: control drum ExtraElementID.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
const std::vector< std::vector< Real > > & _control_drums_azimuthal_meta
MeshMetaData: vector of azimuthal angles of all nodes of each control drum.
virtual Real value(Real t, const Point &p) const override
registerMooseObject("ReactorApp", MultiControlDrumFunction)
MultiControlDrumFunction(const InputParameters &parameters)
const Real _rotation_start_time
Start time of control drums rotation.
void addRequiredParam(const std::string &name, const std::string &doc_string)
const std::vector< Real > _start_angles
Vector of initial starting angles of control drums.
const std::vector< Real > _angular_speeds
Vector of angular speeds of control drums.
void paramError(const std::string &param, Args... args) const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Real _rotation_end_time
End time of control drums rotation.
void addClassDescription(const std::string &doc_string)
const std::vector< Point > & _control_drum_positions
MeshMetaData: positions of control drums.
const bool _use_control_drum_id
Whether extra element id user_control_drum_id is used.
static InputParameters validParams()
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)