https://mooseframework.inl.gov
Loading...
Searching...
No Matches
AverageSectionValueSampler.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#include "MooseMesh.h"
12#include "SystemBase.h"
13#include "Conversion.h"
14#include <limits>
15
17
20{
22 params.addClassDescription("Compute the section's variable average in three-dimensions "
23 "given a user-defined definition of the cross section.");
24 params.addParam<std::vector<SubdomainName>>(
25 "block",
26 "The list of blocks in which to search for cross sectional nodes to compute the variable "
27 "average.");
28 params.addRequiredParam<Point>("axis_direction", "Direction of the structural component's axis");
29 params.addRequiredParam<Point>("reference_point",
30 "Structural component reference starting point from which the "
31 "input parameter 'lengths' applies.");
32 params.addParam<Real>("cross_section_maximum_radius",
33 std::numeric_limits<Real>::max(),
34 "Radial distance with respect to the body axis within which nodes are "
35 "considered to belong to this "
36 "structural component. Used to disambiguate multiple components that share "
37 "the same mesh block.");
38 params.addRequiredParam<std::vector<VariableName>>(
39 "variables", "Variables for the cross section output. These variables must be nodal.");
40 params.addDeprecatedParam<std::vector<Real>>(
41 "lengths",
42 {},
43 "Positions along axis from reference_point at which to compute average values.",
44 "Use the 'positions' parameter instead");
45 params.addParam<std::vector<Real>>(
46 "positions",
47 {},
48 "Positions along axis from reference_point at which to compute average values.");
49 params.addParam<RealVectorValue>(
50 "symmetry_plane",
51 RealVectorValue(0, 0, 0),
52 "Vector normal to a symmetry plane, used if the section has a symmetry plane through it. "
53 "Causes the variables to be treated as components of a vector and zeros out the compomnent "
54 "in the direction normal to symmetry_plane. Three variables must be defined in 'variables' "
55 "to use this option.");
56 params.addParam<Real>("tolerance",
57 1.0e-6,
58 "Maximum axial distance of nodes from the specified axial lengths to "
59 "consider them in the cross-section average");
60 params.addParam<bool>(
61 "require_equal_node_counts",
62 true,
63 "Whether to require the number of nodes at each axial location to be equal");
64 return params;
65}
66
68 : GeneralVectorPostprocessor(parameters),
69 _mesh(_app.actionWarehouse().mesh()),
70 _variables(getParam<std::vector<VariableName>>("variables")),
71 _direction(getParam<Point>("axis_direction")),
72 _reference_point(getParam<Point>("reference_point")),
73 _positions(isParamSetByUser("lengths") ? getParam<std::vector<Real>>("lengths")
74 : getParam<std::vector<Real>>("positions")),
75 _have_symmetry_plane(isParamSetByUser("symmetry_plane")),
76 _symmetry_plane(getParam<RealVectorValue>("symmetry_plane")),
77 _automatically_locate_positions(_positions.size() == 0),
78 _tolerance(getParam<Real>("tolerance")),
79 _number_of_nodes(_positions.size()),
80 _cross_section_maximum_radius(getParam<Real>("cross_section_maximum_radius")),
81 _require_equal_node_counts(getParam<bool>("require_equal_node_counts")),
82 _need_mesh_initializations(true)
83{
84 if (parameters.isParamSetByUser("lengths") && parameters.isParamSetByUser("positions"))
85 paramError("lengths", "The 'lengths' and 'positions' parameters cannot both be set.");
86
87 if (parameters.isParamSetByUser("lengths") && _positions.size() == 0)
88 paramError("lengths", "If 'lengths' is specified, at least one value must be provided");
89
90 if (parameters.isParamSetByUser("positions") && _positions.size() == 0)
91 paramError("positions", "If 'positions' is specified, at least one value must be provided");
92
93 if (_mesh->dimension() != 3)
94 mooseError("The AverageSectionValueSampler postprocessor can only be used with three "
95 "dimensional meshes.");
96
97 if (!MooseUtils::absoluteFuzzyEqual(_direction.norm_sq(), 1.0))
98 paramError("axis_direction", "Axis direction must be a unit vector.");
99
100 _output_vector.resize(_variables.size() + 2);
101 for (const auto j : make_range(_variables.size()))
103 const auto pos_idx = _variables.size();
104 _output_vector[pos_idx] = &declareVector("axial_position");
105 _output_vector[pos_idx + 1] = &declareVector("node_count");
106
107 for (const auto j : make_range(_variables.size()))
108 {
109 if (!_sys.hasVariable(_variables[j]))
110 paramError("variables",
111 "One or more of the specified variables do not exist in this system.");
112 const MooseVariable & variable = _sys.getFieldVariable<Real>(_tid, _variables[j]);
113 if (!variable.isNodal())
114 paramError("variables",
115 "One or more of the specified variables are not defined at the nodes.");
116 _var_numbers.push_back(variable.number());
117 }
118
120 {
121 const auto symm_plane_norm = _symmetry_plane.norm();
122 if (MooseUtils::absoluteFuzzyEqual(symm_plane_norm, 0.0))
123 mooseError("Vector defined in 'symmetry_plane' cannot have a norm of zero");
125 if (_variables.size() != 3)
126 paramError("variables",
127 "If 'symmetry_plane' is prescribed, three variables must be provided (for the x, "
128 "y, z vector components, in that order)");
129 mooseInfo("In AverageSectionValueSampler " + name() +
130 ": \nTreating the variables as vector components (" + _variables[0] + ", " +
131 _variables[1] + ", " + _variables[2] +
132 ") and zeroing out the component normal to the prescribed 'symmetry_plane'\n");
133 }
134}
135
136void
140
141void
146
147void
149{
151 {
152 // Option 1: locate positions first, then check counts
156 }
157
158 for (const auto j : make_range(_variables.size() + 2))
159 {
160 _output_vector[j]->clear();
161 _output_vector[j]->resize(_positions.size());
162 }
163 _number_of_nodes.clear();
164 _number_of_nodes.resize(_positions.size());
165}
166
167void
169{
170 for (const auto j : make_range(_variables.size()))
172
174
175 for (const auto li : index_range(_positions))
176 if (_number_of_nodes[li] < 1)
177 mooseError("No nodes were found in AverageSectionValueSampler postprocessor.");
178
179 for (const auto li : index_range(_positions))
180 for (const auto j : make_range(_variables.size()))
181 (*_output_vector[j])[li] /= _number_of_nodes[li];
182
183 const auto pos_idx = _variables.size();
184 (*_output_vector[pos_idx]) = _positions;
185 std::vector<Real> num_nodes_real(_number_of_nodes.begin(), _number_of_nodes.end());
186 (*_output_vector[pos_idx + 1]) = num_nodes_real;
187
189 {
190 for (const auto li : index_range(_number_of_nodes))
191 {
193 {
194 std::ostringstream pos_out;
195 for (const auto li2 : index_range(_number_of_nodes))
196 pos_out << std::setw(10) << _positions[li2] << std::setw(10) << _number_of_nodes[li2]
197 << "\n";
199 "Node counts do not match for all axial positions. If this behavior "
200 "is desired to accommodate nonuniform meshes, set "
201 "'require_equal_node_counts=false'\n Axial Node\n Position Count\n" +
202 pos_out.str());
203 }
204 }
205 }
206}
207
208void
210{
211 _block_ids = _mesh->getSubdomainIDs(getParam<std::vector<SubdomainName>>("block"));
212
213 auto * active_nodes = _mesh->getActiveSemiLocalNodeRange();
214
215 std::vector<std::vector<Real>> output_vector_partial(_variables.size(),
216 std::vector<Real>(_positions.size()));
217
218 const NumericVector<Number> & _sol = *_sys.currentSolution();
219
220 for (const auto & node : *active_nodes)
221 {
222 const std::set<SubdomainID> & node_blk_ids = _mesh->getNodeBlockIds(*node);
223
224 for (const auto li : index_range(_positions))
225 {
226 for (const auto i : _block_ids)
227 {
228 if (node_blk_ids.find(i) != node_blk_ids.end())
229 {
230 // Check if node is within tolerance of user-prescribed plane
231 if (std::abs(axialPosition(*node) - _positions[li]) > _tolerance)
232 continue;
233 if ((*node).processor_id() != processor_id())
234 continue;
235
236 // Retrieve nodal variables
237 std::vector<Real> this_node_vars(_var_numbers.size());
238 for (const auto j : make_range(_var_numbers.size()))
239 this_node_vars[j] = _sol(node->dof_number(_sys.number(), _var_numbers[j], 0));
240
242 {
243 RealVectorValue this_node_vec_var(
244 this_node_vars[0], this_node_vars[1], this_node_vars[2]);
245 this_node_vec_var -= _symmetry_plane * this_node_vec_var * _symmetry_plane;
246 this_node_vars[0] = this_node_vec_var(0);
247 this_node_vars[1] = this_node_vec_var(1);
248 this_node_vars[2] = this_node_vec_var(2);
249 }
250
251 for (const auto j : make_range(_var_numbers.size()))
252 output_vector_partial[j][li] += this_node_vars[j];
253
254 _number_of_nodes[li]++;
255 break;
256 }
257 }
258 }
259 }
260
261 for (const auto j : make_range(_variables.size()))
262 *_output_vector[j] = output_vector_partial[j];
263}
264
265Real
267{
268 // Compute node location w.r.t. structural component length
269 const Point relative_distance{
270 node(0) - _reference_point(0), node(1) - _reference_point(1), node(2) - _reference_point(2)};
271
272 const Real axial_position = _direction * relative_distance;
273 const Real in_plane_distance =
274 (relative_distance - relative_distance * _direction * _direction).norm();
275
276 // If the in-plane distance is greater than the specified cross-section radius, the point is not
277 // in this component
278 if (in_plane_distance > _cross_section_maximum_radius)
279 return std::numeric_limits<Real>::max();
280
281 return axial_position;
282}
283
284void
286{
287 _positions.clear();
288
289 // Data structure used to collect the locations with nodes on each processor,
290 // and gather in parallel.
291 std::vector<Real> pos_vec;
292
293 // Data structure used to store parallel-gathered locations
294 std::set<Real> pos_set;
295
296 _block_ids = _mesh->getSubdomainIDs(getParam<std::vector<SubdomainName>>("block"));
297 auto * nodes = _mesh->getLocalNodeRange();
298
299 for (const auto node : *nodes)
300 {
301 const std::set<SubdomainID> & node_blk_ids = _mesh->getNodeBlockIds(*node);
302
303 for (const auto i : _block_ids)
304 {
305 if (node_blk_ids.find(i) != node_blk_ids.end())
306 {
307 bool found_match = false;
308 Real axial_position = axialPosition(*node);
309 if (axial_position == std::numeric_limits<Real>::max()) // Node not in this component
310 continue;
311 for (auto & pos : pos_vec)
312 {
313 const Real dist_to_plane = std::abs(axial_position - pos);
314 if (dist_to_plane <= _tolerance)
315 {
316 found_match = true;
317 break;
318 }
319 }
320 if (!found_match)
321 pos_vec.emplace_back(axial_position);
322 }
323 }
324 }
325
326 _communicator.allgather(pos_vec);
327
328 for (const auto & posv : pos_vec)
329 {
330 bool found_match = false;
331 for (auto & poss : pos_set)
332 {
333 if (std::abs(posv - poss) < _tolerance)
334 {
335 found_match = true;
336 break;
337 }
338 }
339 if (!found_match)
340 pos_set.insert(posv);
341 }
342 for (const auto & poss : pos_set)
343 _positions.emplace_back(poss);
344}
registerMooseObject("SolidMechanicsApp", AverageSectionValueSampler)
void automaticallyLocatePositions()
Automatically identify all axial positions of nodes within the component and store their unique value...
const Real _cross_section_maximum_radius
Tolerance to disambiguate cross section locations in different components within the same block.
std::vector< unsigned int > _var_numbers
Indices of the variables in their systems.
const std::shared_ptr< MooseMesh > & _mesh
Reference to the mesh.
AverageSectionValueSampler(const InputParameters &parameters)
const Point _reference_point
Starting or reference point of the structural component to locate nodes on the cross section.
const Point _direction
Axis direction of the structural component.
const Real _tolerance
Tolerance to identify nodes on the user-prescribed cross section.
std::vector< SubdomainID > _block_ids
RealVectorValue _symmetry_plane
Vector normal to a symmetry plane, optionally defined if the section has a symmetry plane.
Real axialPosition(const Node &node) const
Determine axial distance of the point from the component's reference point.
std::vector< VectorPostprocessorValue * > _output_vector
Vector of outputs, where each entry is the vector of average values for single variable at the select...
const bool _have_symmetry_plane
Whether a symmetry plane has been defined by the user.
std::vector< Real > _positions
Axial positions along the component at which average values are computed.
std::vector< unsigned int > _number_of_nodes
Number of nodes for computing output (local and global).
std::vector< VariableName > _variables
Variables to output.
const bool _require_equal_node_counts
Whether to require the number of nodes at each axial location to be equal.
static InputParameters validParams()
const bool _automatically_locate_positions
Whether to automatically locate positions along section for averaging field values.
bool _need_mesh_initializations
Whether node locations need to be identified and nodes at positions need to be counted.
static InputParameters validParams()
bool isParamSetByUser(const std::string &name) const
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
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)
const InputParameters & parameters() const
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
const T & getParam(const std::string &name) const
void mooseInfo(Args &&... args) const
unsigned int number() const
bool isNodal() const override
virtual const NumericVector< Number > *const & currentSolution() const=0
MooseVariableFE< T > & getFieldVariable(THREAD_ID tid, const std::string &var_name)
unsigned int number() const
virtual bool hasVariable(const std::string &var_name) const
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
VectorPostprocessorValue & declareVector(const std::string &vector_name)
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
MeshBase & mesh