https://mooseframework.inl.gov
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 
136 void
138 {
139 }
140 
141 void
143 {
145 }
146 
147 void
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 
167 void
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  {
192  if (_number_of_nodes[li] != _number_of_nodes[0])
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";
198  mooseError(
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 
208 void
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 
265 Real
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 
284 void
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 }
const Real _cross_section_maximum_radius
Tolerance to disambiguate cross section locations in different components within the same block...
virtual const NumericVector< Number > *const & currentSolution() const=0
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
const bool _have_symmetry_plane
Whether a symmetry plane has been defined by the user.
auto norm() const -> decltype(std::norm(Real()))
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
const bool _require_equal_node_counts
Whether to require the number of nodes at each axial location to be equal.
std::vector< unsigned int > _number_of_nodes
Number of nodes for computing output (local and global).
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
unsigned int number() const
void mooseInfo(Args &&... args) const
const Point _direction
Axis direction of the structural component.
MeshBase & mesh
const Parallel::Communicator & _communicator
void automaticallyLocatePositions()
Automatically identify all axial positions of nodes within the component and store their unique value...
virtual const std::string & name() const
void addRequiredParam(const std::string &name, const std::string &doc_string)
AverageSectionValueSampler(const InputParameters &parameters)
std::vector< SubdomainID > _block_ids
std::vector< unsigned int > _var_numbers
Indices of the variables in their systems.
const Point _reference_point
Starting or reference point of the structural component to locate nodes on the cross section...
static InputParameters validParams()
RealVectorValue _symmetry_plane
Vector normal to a symmetry plane, optionally defined if the section has a symmetry plane...
static InputParameters validParams()
const T & getParam(const std::string &name) const
registerMooseObject("SolidMechanicsApp", AverageSectionValueSampler)
std::vector< Real > _positions
Axial positions along the component at which average values are computed.
Real axialPosition(const Node &node) const
Determine axial distance of the point from the component&#39;s reference point.
void paramError(const std::string &param, Args... args) const
unsigned int number() const
auto norm(const T &a) -> decltype(std::abs(a))
VectorPostprocessorValue & declareVector(const std::string &vector_name)
virtual bool hasVariable(const std::string &var_name) const
const bool _automatically_locate_positions
Whether to automatically locate positions along section for averaging field values.
bool isNodal() const override
bool isParamSetByUser(const std::string &name) const
const std::shared_ptr< MooseMesh > & _mesh
Reference to the mesh.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
MooseVariableFE< T > & getFieldVariable(THREAD_ID tid, const std::string &var_name)
std::vector< VariableName > _variables
Variables to output.
const Real _tolerance
Tolerance to identify nodes on the user-prescribed cross section.
std::vector< VectorPostprocessorValue * > _output_vector
Vector of outputs, where each entry is the vector of average values for single variable at the select...
IntRange< T > make_range(T beg, T end)
virtual void initialSetup() override
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
const InputParameters & parameters() const
bool _need_mesh_initializations
Whether node locations need to be identified and nodes at positions need to be counted.
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
processor_id_type processor_id() const
auto index_range(const T &sizable)