https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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");
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"))
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
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");
219
221 mooseError("'direction_max' must be larger than 'direction_min'");
222 }
223
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
235Real
236LayeredBase::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
346Real
347LayeredBase::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
354void
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
367void
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
392void
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
401unsigned int
402LayeredBase::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
441void
443{
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
460void
461LayeredBase::setLayerValue(unsigned int layer, Real value)
462{
463 _layer_values[layer] = value;
464 _layer_has_value[layer] = true;
465}
466
467void
469{
470 if (_layer_bounding_blocks.size() == 0)
471 {
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 {
493 _direction_min = std::min(_direction_min, node(_direction));
494 _direction_max = std::max(_direction_max, node(_direction));
495 }
496 }
497
498 mesh.comm().min(_direction_min);
499 mesh.comm().max(_direction_max);
500 }
501}
InputParameters emptyInputParameters()
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
unsigned int THREAD_ID
Definition MooseTypes.h:237
void ErrorVector unsigned int
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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 isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
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.
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...
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 addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another,...
This base class computes volume integrals of a variable storing partial sums for the specified number...
Definition LayeredBase.h:37
virtual void threadJoin(const UserObject &y)
LayeredBase(const InputParameters &parameters)
Definition LayeredBase.C:85
SubProblem & _layered_base_subproblem
Subproblem for the child object.
bool _has_direction_max_min
whether the max/min coordinate in the direction is known from user input
unsigned int _num_layers
Number of layers to split the mesh into.
virtual Real getLayerValue(unsigned int layer) const
Get the value for a given layer.
std::string _layered_base_name
Name of this object.
bool _interval_based
Whether or not this object is based on equally spaced intervals or "bounds".
Real _direction_max
bool _using_displaced_mesh
true if this object operates on the displaced mesh, otherwise false
virtual unsigned int getLayer(const Point &p) const
Helper function to return the layer the point lies in.
void setLayerValue(unsigned int layer, Real value)
Set the value for a particular layer.
std::vector< SubdomainID > _layer_bounding_blocks
List of SubdomainIDs, if given.
std::vector< int > & _layer_has_value
Whether or not each layer has had any value summed into it.
void computeLayerCenters()
Compute the center points for each layer.
std::vector< Real > _layer_centers
center coordinates of each layer
unsigned int _sample_type
How to sample the values.
bool _cumulative
Whether the values are cumulative over the layers.
const bool _positive_cumulative_direction
Whether the cumulative values should be summed in the positive or negative direction.
virtual Real integralValue(const Point &p) const
Given a Point return the integral value associated with the layer that point falls in.
static InputParameters validParams()
Definition LayeredBase.C:22
const InputParameters & _layered_base_params
Params for this object.
unsigned int _direction
The component direction the layers are going in. We cache this for speed (so we're not always going t...
Real _direction_min
unsigned int _average_radius
How many layers both above and below the found layer will be used in the average.
std::vector< Real > _layer_bounds
The boundaries of the layers.
virtual void finalize()
virtual void initialize()
std::vector< Real > & _layer_values
Value of the integral for each layer.
void getBounds()
Compute bounds, restricted to blocks if given.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
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:1726
A class for creating restricted objects.
Definition Restartable.h:29
Generic class for solving transient nonlinear problems.
Definition SubProblem.h:79
virtual MooseMesh & mesh()=0
void max(const T &r, T &o, Request &req) const
Base class for user-specific data.
Definition UserObject.h:20
const Parallel::Communicator & comm() const
MeshBase & mesh
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)