https://mooseframework.inl.gov
LayeredBase.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 
10 #include "LayeredBase.h"
11 
12 // MOOSE includes
13 #include "MooseEnum.h"
14 #include "MooseMesh.h"
15 #include "SubProblem.h"
16 #include "UserObject.h"
17 
18 #include "libmesh/mesh_tools.h"
19 #include "libmesh/point.h"
20 
23 {
25  MooseEnum directions("x y z");
26 
27  params.addRequiredParam<MooseEnum>("direction", directions, "The direction of the layers.");
28  params.addParam<unsigned int>("num_layers", "The number of layers.");
29  params.addParam<std::vector<Real>>("bounds",
30  "The 'bounding' positions of the layers i.e.: '0, "
31  "1.2, 3.7, 4.2' will mean 3 layers between those "
32  "positions.");
33  params.addRangeCheckedParam<unsigned int>("bound_uniform_splits",
34  "bound_uniform_splits > 0",
35  "The number of times the bins specified in 'bounds' "
36  "should be split uniformly.");
37  params.addParam<std::vector<unsigned int>>(
38  "bound_splits", "Number of uniform splits of all layers in 'bounds' (default to all ones)");
39 
40  MooseEnum sample_options("direct interpolate average", "direct");
41  params.addParam<MooseEnum>("sample_type",
42  sample_options,
43  "How to sample the layers. 'direct' means get the value of the layer "
44  "the point falls in directly (or average if that layer has no value). "
45  " 'interpolate' does a linear interpolation between the two closest "
46  "layers. 'average' averages the two closest layers.");
47 
48  params.addParam<unsigned int>("average_radius",
49  1,
50  "When using 'average' sampling this is how "
51  "the number of values both above and below "
52  "the layer that will be averaged.");
53 
54  params.addParam<bool>(
55  "cumulative",
56  false,
57  "When true the value in each layer is the sum of the values up to and including that layer");
58  params.addParam<bool>(
59  "positive_cumulative_direction",
60  true,
61  "When 'cumulative' is true, whether the direction for summing the cumulative value "
62  "is the positive direction or negative direction");
63 
64  params.addParam<std::vector<SubdomainName>>(
65  "block", "The list of block ids (SubdomainID) that this object will be applied");
66 
67  params.addParam<std::vector<SubdomainName>>("layer_bounding_block",
68  "List of block ids (SubdomainID) that are used to "
69  "determine the upper and lower geometric bounds for "
70  "all layers. If this is not specified, the ids "
71  "specified in 'block' are used for this purpose.");
72 
73  params.addParam<Real>("direction_min",
74  "Minimum coordinate along 'direction' that bounds the layers");
75  params.addParam<Real>("direction_max",
76  "Maximum coordinate along 'direction' that bounds the layers");
77  params.addParamNamesToGroup(
78  "direction num_layers bounds direction_min direction_max bound_uniform_splits bound_splits",
79  "Layers extent and definition");
80  params.addParamNamesToGroup("sample_type average_radius cumulative positive_cumulative_direction",
81  "Value sampling / aggregating");
82  return params;
83 }
84 
86  : Restartable(parameters.getCheckedPointerParam<SubProblem *>("_subproblem")->getMooseApp(),
87  parameters.getObjectName() + "_layered_base",
88  "LayeredBase",
89  parameters.get<THREAD_ID>("_tid")),
90  _layered_base_name(parameters.getObjectName()),
91  _layered_base_params(parameters),
92  _direction_enum(parameters.get<MooseEnum>("direction")),
93  _direction(_direction_enum),
94  _sample_type(parameters.get<MooseEnum>("sample_type")),
95  _average_radius(parameters.get<unsigned int>("average_radius")),
96  _using_displaced_mesh(_layered_base_params.get<bool>("use_displaced_mesh")),
97  _layer_values(declareRestartableData<std::vector<Real>>("layer_values")),
98  _layer_has_value(declareRestartableData<std::vector<int>>("layer_has_value")),
99  _cumulative(parameters.get<bool>("cumulative")),
100  _positive_cumulative_direction(parameters.get<bool>("positive_cumulative_direction")),
101  _layered_base_subproblem(*parameters.getCheckedPointerParam<SubProblem *>("_subproblem")),
102  _layer_bounding_blocks(),
103  _has_direction_max_min(false)
104 {
105  if (_layered_base_params.isParamValid("num_layers") &&
107  mooseError("'bounds' and 'num_layers' cannot both be set");
108 
109  if (!_cumulative && parameters.isParamSetByUser("positive_cumulative_direction"))
110  mooseWarning(
111  "The 'positive_cumulative_direction' parameter is unused when 'cumulative' is false");
112 
113  if (_layered_base_params.isParamValid("num_layers"))
114  {
115  _num_layers = _layered_base_params.get<unsigned int>("num_layers");
116  _interval_based = true;
117  }
118  else if (_layered_base_params.isParamValid("bounds"))
119  {
120  _interval_based = false;
121 
122  _layer_bounds = _layered_base_params.get<std::vector<Real>>("bounds");
123 
124  if (_layer_bounds.size() < 2)
125  mooseError("At least two boundaries must be provided in 'bounds' to form layers!");
126 
127  // Make sure the bounds are sorted - we're going to depend on this
128  std::sort(_layer_bounds.begin(), _layer_bounds.end());
129 
130  if (_layered_base_params.isParamValid("bound_splits") &&
131  _layered_base_params.isParamValid("bound_uniform_splits"))
132  mooseError("Parameters 'bound_splits' and 'bound_uniform_splits' cannot be both supplied");
133 
134  // If requested, we uniformly split the layers.
135  if (_layered_base_params.isParamValid("bound_uniform_splits"))
136  {
137  const auto splits = _layered_base_params.get<unsigned int>("bound_uniform_splits");
138  for (unsigned int s = 0; s < splits; ++s)
139  {
140  std::vector<Real> new_bnds;
141  new_bnds.reserve(2 * (_layer_bounds.size() - 1) + 1);
142  for (unsigned int i = 0; i < _layer_bounds.size() - 1; ++i)
143  {
144  new_bnds.emplace_back(_layer_bounds[i]);
145  new_bnds.emplace_back(0.5 * (_layer_bounds[i] + _layer_bounds[i + 1]));
146  }
147  new_bnds.emplace_back(_layer_bounds.back());
148  _layer_bounds = new_bnds;
149  }
150  }
151 
152  if (_layered_base_params.isParamValid("bound_splits"))
153  {
154  // expand layers with subdivisions
155  const auto nsubs = _layered_base_params.get<std::vector<unsigned int>>("bound_splits");
156  if (nsubs.size() != _layer_bounds.size() - 1)
157  mooseError("'bound_splits' size must be equal to the size of 'bounds' minus one");
158 
159  std::vector<Real> new_layers;
160  new_layers.reserve(std::accumulate(nsubs.begin(), nsubs.end(), (unsigned int)0));
161  new_layers.push_back(_layer_bounds[0]);
162  for (const auto i : index_range(nsubs))
163  {
164  Real dz = (_layer_bounds[i + 1] - _layer_bounds[i]) / nsubs[i];
165  for (const auto j : make_range(nsubs[i]))
166  {
167  libmesh_ignore(j);
168  new_layers.push_back(new_layers.back() + dz);
169  }
170  }
171  _layer_bounds = new_layers;
172  }
173 
174  _num_layers = _layer_bounds.size() - 1; // Layers are only in-between the bounds
175  _direction_min = _layer_bounds.front();
176  _direction_max = _layer_bounds.back();
177  _has_direction_max_min = true;
178  }
179  else
180  mooseError("One of 'bounds' or 'num_layers' must be specified");
181 
182  if (!_interval_based && _sample_type == 1)
183  mooseError("'sample_type = interpolate' not supported with 'bounds'");
184 
185  bool has_layer_bounding_block = _layered_base_params.isParamValid("layer_bounding_block");
186  bool has_block = _layered_base_params.isParamValid("block");
187  bool has_direction_min = _layered_base_params.isParamValid("direction_min");
188  bool has_direction_max = _layered_base_params.isParamValid("direction_max");
189 
190  if (_has_direction_max_min && has_direction_min)
191  mooseWarning("'direction_min' is unused when providing 'bounds'");
192 
193  if (_has_direction_max_min && has_direction_max)
194  mooseWarning("'direction_max' is unused when providing 'bounds'");
195 
196  // can only specify one of layer_bounding_block or the pair direction_max/min
197  if (has_layer_bounding_block && (has_direction_min || has_direction_max))
198  mooseError("Only one of 'layer_bounding_block' and the pair 'direction_max' and "
199  "'direction_min' can be provided");
200 
201  // if either one of direction_min or direction_max is specified, must provide the other one
202  if (has_direction_min != has_direction_max)
203  mooseError("If providing the layer max/min directions, both 'direction_max' and "
204  "'direction_min' must be specified.");
205 
206  if (has_layer_bounding_block)
208  _layered_base_params.get<std::vector<SubdomainName>>("layer_bounding_block"));
209  else if (has_block)
211  _layered_base_params.get<std::vector<SubdomainName>>("block"));
212 
213  // specifying the direction max/min overrides anything set with the 'block'
214  if (has_direction_min && has_direction_max)
215  {
216  _direction_min = parameters.get<Real>("direction_min");
217  _direction_max = parameters.get<Real>("direction_max");
218  _has_direction_max_min = true;
219 
221  mooseError("'direction_max' must be larger than 'direction_min'");
222  }
223 
224  _layer_values.resize(_num_layers);
226 
227  // if we haven't already figured out the max/min in specified direction
228  // (either with the 'bounds' or explicit specification from the user), do so
230  getBounds();
231 
233 }
234 
235 Real
236 LayeredBase::integralValue(const Point & p) const
237 {
238  unsigned int layer = getLayer(p);
239 
240  int higher_layer = -1;
241  int lower_layer = -1;
242 
243  for (unsigned int i = layer; i < _layer_values.size(); i++)
244  {
245  if (_layer_has_value[i])
246  {
247  higher_layer = i;
248  break;
249  }
250  }
251 
252  for (int i = layer - 1; i >= 0; i--)
253  {
254  if (_layer_has_value[i])
255  {
256  lower_layer = i;
257  break;
258  }
259  }
260 
261  if (higher_layer == -1 && lower_layer == -1)
262  return 0; // TODO: We could error here but there are startup dependency problems
263 
264  switch (_sample_type)
265  {
266  case 0: // direct
267  {
268  if (higher_layer == -1) // Didn't find a higher layer
269  return _layer_values[lower_layer];
270 
271  if (unsigned(higher_layer) == layer) // constant in a layer
272  return _layer_values[higher_layer];
273 
274  if (lower_layer == -1) // Didn't find a lower layer
275  return _layer_values[higher_layer];
276 
277  return (_layer_values[higher_layer] + _layer_values[lower_layer]) / 2;
278  }
279  case 1: // interpolate
280  {
281  if (higher_layer == -1) // Didn't find a higher layer
282  return _layer_values[lower_layer];
283 
284  Real layer_length = (_direction_max - _direction_min) / _num_layers;
285  Real lower_coor = _direction_min;
286  Real lower_value = 0;
287  if (lower_layer != -1)
288  {
289  lower_coor += (lower_layer + 1) * layer_length;
290  lower_value = _layer_values[lower_layer];
291  }
292 
293  // Interpolate between the two points
294  Real higher_value = _layer_values[higher_layer];
295 
296  // Linear interpolation
297  return lower_value +
298  (higher_value - lower_value) * (p(_direction) - lower_coor) / layer_length;
299  }
300  case 2: // average
301  {
302  Real total = 0;
303  unsigned int num_values = 0;
304 
305  if (higher_layer != -1)
306  {
307  for (const auto i : make_range(_average_radius))
308  {
309  int current_layer = higher_layer + i;
310 
311  if ((size_t)current_layer >= _layer_values.size())
312  break;
313 
314  if (_layer_has_value[current_layer])
315  {
316  total += _layer_values[current_layer];
317  num_values += 1;
318  }
319  }
320  }
321 
322  if (lower_layer != -1)
323  {
324  for (const auto i : make_range(_average_radius))
325  {
326  int current_layer = lower_layer - i;
327 
328  if (current_layer < 0)
329  break;
330 
331  if (_layer_has_value[current_layer])
332  {
333  total += _layer_values[current_layer];
334  num_values += 1;
335  }
336  }
337  }
338 
339  return total / num_values;
340  }
341  default:
342  mooseError("Unknown sample type!");
343  }
344 }
345 
346 Real
347 LayeredBase::getLayerValue(unsigned int layer) const
348 {
349  if (layer >= _layer_values.size())
350  mooseError("Layer '", layer, "' not found in '", _layered_base_name, "'.");
351  return _layer_values[layer];
352 }
353 
354 void
356 {
358  getBounds();
359 
360  for (const auto i : index_range(_layer_values))
361  {
362  _layer_values[i] = 0.0;
363  _layer_has_value[i] = false;
364  }
365 }
366 
367 void
369 {
372 
373  if (_cumulative)
374  {
375  Real value = 0;
376 
378  for (const auto i : make_range(_num_layers))
379  {
380  value += getLayerValue(i);
381  setLayerValue(i, value);
382  }
383  else
384  for (int i = _num_layers - 1; i >= 0; i--)
385  {
386  value += getLayerValue(i);
387  setLayerValue(i, value);
388  }
389  }
390 }
391 
392 void
394 {
395  const auto & lb = dynamic_cast<const LayeredBase &>(y);
396  for (const auto i : index_range(_layer_values))
397  if (lb.layerHasValue(i))
398  setLayerValue(i, getLayerValue(i) + lb._layer_values[i]);
399 }
400 
401 unsigned int
402 LayeredBase::getLayer(const Point & p) const
403 {
404  Real direction_x = p(_direction);
405 
406  if (direction_x < _direction_min)
407  return 0;
408 
409  if (_interval_based)
410  {
411  unsigned int layer =
412  std::floor(((direction_x - _direction_min) / (_direction_max - _direction_min)) *
413  static_cast<Real>(_num_layers));
414 
415  if (layer >= _num_layers)
416  layer = _num_layers - 1;
417 
418  return layer;
419  }
420  else // Figure out what layer we are in from the bounds
421  {
422  // This finds the first entry in the vector that is larger than what we're looking for
423  std::vector<Real>::const_iterator one_higher =
424  std::upper_bound(_layer_bounds.begin(), _layer_bounds.end(), direction_x);
425 
426  if (one_higher == _layer_bounds.end())
427  {
428  return static_cast<unsigned int>(
429  _layer_bounds.size() -
430  2); // Just return the last layer. -2 because layers are "in-between" bounds
431  }
432  else if (one_higher == _layer_bounds.begin())
433  return 0; // Return the first layer
434  else
435  // The -1 is because the interval that we fall in is just _before_ the number that is bigger
436  // (which is what we found
437  return static_cast<unsigned int>(std::distance(_layer_bounds.begin(), one_higher - 1));
438  }
439 }
440 
441 void
443 {
444  _layer_centers.resize(_num_layers);
445 
446  if (_interval_based)
447  {
449 
450  for (const auto i : make_range(_num_layers))
451  _layer_centers[i] = (i + 0.5) * dx;
452  }
453  else
454  {
455  for (const auto i : make_range(_num_layers))
456  _layer_centers[i] = 0.5 * (_layer_bounds[i + 1] + _layer_bounds[i]);
457  }
458 }
459 
460 void
461 LayeredBase::setLayerValue(unsigned int layer, Real value)
462 {
463  _layer_values[layer] = value;
464  _layer_has_value[layer] = true;
465 }
466 
467 void
469 {
470  if (_layer_bounding_blocks.size() == 0)
471  {
472  BoundingBox bounding_box = MeshTools::create_bounding_box(_layered_base_subproblem.mesh());
473  _direction_min = bounding_box.min()(_direction);
474  _direction_max = bounding_box.max()(_direction);
475  }
476  else
477  {
478  _direction_min = std::numeric_limits<Real>::infinity();
479  _direction_max = -std::numeric_limits<Real>::infinity();
480 
482 
483  for (auto & elem_ptr : *mesh.getActiveLocalElementRange())
484  {
485  auto subdomain_id = elem_ptr->subdomain_id();
486 
487  if (std::find(_layer_bounding_blocks.begin(), _layer_bounding_blocks.end(), subdomain_id) ==
489  continue;
490 
491  for (auto & node : elem_ptr->node_ref_range())
492  {
495  }
496  }
497 
498  mesh.comm().min(_direction_min);
499  mesh.comm().max(_direction_max);
500  }
501 }
virtual MooseMesh & mesh()=0
std::vector< Real > & _layer_values
Value of the integral for each layer.
Definition: LayeredBase.h:139
bool _using_displaced_mesh
true if this object operates on the displaced mesh, otherwise false
Definition: LayeredBase.h:130
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
A class for creating restricted objects.
Definition: Restartable.h:28
std::string _layered_base_name
Name of this object.
Definition: LayeredBase.h:103
SubProblem & _layered_base_subproblem
Subproblem for the child object.
Definition: LayeredBase.h:152
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
Combine two vector parameters into a single vector of pairs.
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition: MooseError.h:345
bool _interval_based
Whether or not this object is based on equally spaced intervals or "bounds".
Definition: LayeredBase.h:115
bool _cumulative
Whether the values are cumulative over the layers.
Definition: LayeredBase.h:145
MeshBase & mesh
void getBounds()
Compute bounds, restricted to blocks if given.
Definition: LayeredBase.C:468
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
const Parallel::Communicator & comm() const
std::vector< int > & _layer_has_value
Whether or not each layer has had any value summed into it.
Definition: LayeredBase.h:142
static InputParameters validParams()
Definition: LayeredBase.C:22
const InputParameters & _layered_base_params
Params for this object.
Definition: LayeredBase.h:106
std::vector< Real > _layer_centers
center coordinates of each layer
Definition: LayeredBase.h:133
std::vector< SubdomainID > getSubdomainIDs(const std::vector< SubdomainName > &subdomain_names) const
Get the associated subdomainIDs for the subdomain names that are passed in.
Definition: MooseMesh.C:1759
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
InputParameters emptyInputParameters()
auto max(const L &left, const R &right)
virtual void threadJoin(const UserObject &y)
Definition: LayeredBase.C:393
unsigned int _direction
The component direction the layers are going in. We cache this for speed (so we&#39;re not always going t...
Definition: LayeredBase.h:112
std::vector< SubdomainID > _layer_bounding_blocks
List of SubdomainIDs, if given.
Definition: LayeredBase.h:155
void libmesh_ignore(const Args &...)
unsigned int _average_radius
How many layers both above and below the found layer will be used in the average. ...
Definition: LayeredBase.h:127
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
virtual Real getLayerValue(unsigned int layer) const
Get the value for a given layer.
Definition: LayeredBase.C:347
void computeLayerCenters()
Compute the center points for each layer.
Definition: LayeredBase.C:442
void setLayerValue(unsigned int layer, Real value)
Set the value for a particular layer.
Definition: LayeredBase.C:461
virtual Real integralValue(const Point &p) const
Given a Point return the integral value associated with the layer that point falls in...
Definition: LayeredBase.C:236
const bool _positive_cumulative_direction
Whether the cumulative values should be summed in the positive or negative direction.
Definition: LayeredBase.h:148
virtual unsigned int getLayer(const Point &p) const
Helper function to return the layer the point lies in.
Definition: LayeredBase.C:402
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition: MooseMesh.h:93
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
virtual void initialize()
Definition: LayeredBase.C:355
unsigned int _num_layers
Number of layers to split the mesh into.
Definition: LayeredBase.h:118
virtual void finalize()
Definition: LayeredBase.C:368
std::vector< Real > _layer_bounds
The boundaries of the layers.
Definition: LayeredBase.h:121
bool isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Generic class for solving transient nonlinear problems.
Definition: SubProblem.h:78
void max(const T &r, T &o, Request &req) const
This base class computes volume integrals of a variable storing partial sums for the specified number...
Definition: LayeredBase.h:36
IntRange< T > make_range(T beg, T end)
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
Real _direction_min
Definition: LayeredBase.h:135
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
unsigned int _sample_type
How to sample the values.
Definition: LayeredBase.h:124
LayeredBase(const InputParameters &parameters)
Definition: LayeredBase.C:85
Real _direction_max
Definition: LayeredBase.h:136
auto min(const L &left, const R &right)
void ErrorVector unsigned int
auto index_range(const T &sizable)
Base class for user-specific data.
Definition: UserObject.h:19
const Elem & get(const ElemType type_in)
unsigned int THREAD_ID
Definition: MooseTypes.h:237
bool _has_direction_max_min
whether the max/min coordinate in the direction is known from user input
Definition: LayeredBase.h:158
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another, i.e.