Line data Source code
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 <numeric> // Provides accumulate()
11 :
12 : #include "FunctionalBasisInterface.h" // Provides _domain_options
13 : #include "FunctionSeries.h"
14 : #include "Cartesian.h"
15 : #include "CylindricalDuo.h"
16 :
17 : registerMooseObject("FunctionalExpansionToolsApp", FunctionSeries);
18 :
19 : InputParameters
20 341 : FunctionSeries::validParams()
21 : {
22 341 : InputParameters params = MutableCoefficientsFunctionInterface::validParams();
23 :
24 341 : params.addClassDescription("This function uses a convolution of functional series (functional "
25 : "expansion or FX) to create a 1D, 2D, or 3D function");
26 :
27 : // The available composite series types.
28 : // Cartesian: 1D, 2D, or 3D, depending on which of x, y, and z are present
29 : // CylindricalDuo: planar disc expansion and axial expansion
30 682 : MooseEnum series_types("Cartesian CylindricalDuo");
31 682 : MooseEnum single_series_types_1D("Legendre");
32 682 : MooseEnum single_series_types_2D("Zernike");
33 :
34 682 : params.addRequiredParam<MooseEnum>(
35 : "series_type", series_types, "The type of function series to construct.");
36 :
37 : /*
38 : * This needs to use `unsigned int` instead of `std::size_t` because otherwise MOOSE errors at
39 : * runtime
40 : */
41 682 : params.addRequiredParam<std::vector<unsigned int>>("orders",
42 : "The order of each series. These must be "
43 : "defined as \"x y z\" for Cartesian, and \"z "
44 : "disc\" for CylindricalDuo.");
45 :
46 682 : params.addParam<std::vector<Real>>("physical_bounds",
47 : "The physical bounds of the function series. These must be "
48 : "defined as \"x_min x_max y_min y_max z_min z_max\" for "
49 : "Cartesian, and \"axial_min axial_max disc_center1 "
50 : "disc_center2 radius\" for CylindricalDuo");
51 :
52 682 : params.addParam<MooseEnum>("x", single_series_types_1D, "The series to use for the x-direction.");
53 682 : params.addParam<MooseEnum>("y", single_series_types_1D, "The series to use for the y-direction.");
54 682 : params.addParam<MooseEnum>("z", single_series_types_1D, "The series to use for the z-direction.");
55 :
56 682 : params.addParam<MooseEnum>("disc",
57 : single_series_types_2D,
58 : "The series to use for the disc. Its direction is determined by "
59 : "orthogonality to the declared direction of the axis.");
60 :
61 341 : std::string normalization_types = "orthonormal sqrt_mu standard";
62 682 : MooseEnum expansion_type(normalization_types, "standard");
63 682 : MooseEnum generation_type(normalization_types, "orthonormal");
64 682 : params.addParam<MooseEnum>("expansion_type",
65 : expansion_type,
66 : "The normalization used for expansion of the basis functions");
67 682 : params.addParam<MooseEnum>(
68 : "generation_type",
69 : generation_type,
70 : "The normalization used for generation of the basis function coefficients");
71 341 : return params;
72 682 : }
73 :
74 204 : FunctionSeries::FunctionSeries(const InputParameters & parameters)
75 : : MutableCoefficientsFunctionInterface(this, parameters),
76 204 : _orders(convertOrders(getParam<std::vector<unsigned int>>("orders"))),
77 612 : _physical_bounds(getParam<std::vector<Real>>("physical_bounds")),
78 408 : _series_type_name(getParam<MooseEnum>("series_type")),
79 408 : _x(getParam<MooseEnum>("x")),
80 408 : _y(getParam<MooseEnum>("y")),
81 408 : _z(getParam<MooseEnum>("z")),
82 408 : _disc(getParam<MooseEnum>("disc")),
83 408 : _expansion_type(getParam<MooseEnum>("expansion_type")),
84 612 : _generation_type(getParam<MooseEnum>("generation_type"))
85 : {
86 : std::vector<MooseEnum> domains;
87 : std::vector<MooseEnum> types;
88 :
89 204 : if (_series_type_name == "Cartesian")
90 : {
91 : /*
92 : * For Cartesian series, at least one of 'x', 'y', and 'z' must be specified.
93 : *
94 : * The individual series are always stored in x, y, z order (independent of the order in which
95 : * they appear in the input file). Hence, the 'orders' and 'physical_bounds' vectors must always
96 : * be specified in x, y, z order.
97 : */
98 396 : if (isParamValid("x"))
99 : {
100 124 : domains.push_back(FunctionalBasisInterface::_domain_options = "x");
101 124 : types.push_back(_x);
102 : }
103 396 : if (isParamValid("y"))
104 : {
105 72 : domains.push_back(FunctionalBasisInterface::_domain_options = "y");
106 72 : types.push_back(_y);
107 : }
108 396 : if (isParamValid("z"))
109 : {
110 0 : domains.push_back(FunctionalBasisInterface::_domain_options = "z");
111 0 : types.push_back(_z);
112 : }
113 198 : if (types.size() == 0)
114 2 : mooseError("Must specify one of 'x', 'y', or 'z' for 'Cartesian' series!");
115 390 : _series_type = std::make_unique<Cartesian>(
116 196 : domains, _orders, types, name(), _expansion_type, _generation_type);
117 : }
118 6 : else if (_series_type_name == "CylindricalDuo")
119 : {
120 : /*
121 : * CylindricalDuo represents a disc-axial expansion, where the disc is described by a single
122 : * series, such as Zernike (as opposed to a series individually representing r and a second
123 : * series independently representing theta. For CylindricalDuo series, the series are always
124 : * stored in the axial, planar order, independent of which order the series appear in the input
125 : * file. Therefore, the _orders and _physical_bounds vectors must always appear in axial, planar
126 : * order. The first entry in _domains is interpreted as the axial direction, and the following
127 : * two as the planar.
128 : */
129 12 : if (isParamValid("x"))
130 : {
131 20 : domains = {FunctionalBasisInterface::_domain_options = "x",
132 8 : FunctionalBasisInterface::_domain_options = "y",
133 24 : FunctionalBasisInterface::_domain_options = "z"};
134 4 : types.push_back(_x);
135 : }
136 12 : if (isParamValid("y"))
137 : {
138 10 : domains = {FunctionalBasisInterface::_domain_options = "y",
139 4 : FunctionalBasisInterface::_domain_options = "x",
140 12 : FunctionalBasisInterface::_domain_options = "z"};
141 2 : types.push_back(_y);
142 : }
143 12 : if (isParamValid("z"))
144 : {
145 0 : domains = {FunctionalBasisInterface::_domain_options = "z",
146 0 : FunctionalBasisInterface::_domain_options = "x",
147 0 : FunctionalBasisInterface::_domain_options = "y"};
148 0 : types.push_back(_z);
149 : }
150 :
151 6 : if (types.size() == 0)
152 2 : mooseError("Must specify one of 'x', 'y', or 'z' for 'CylindricalDuo' series!");
153 :
154 4 : if (types.size() > 1)
155 2 : mooseError("Cannot specify more than one of 'x', 'y', or 'z' for 'CylindricalDuo' series!");
156 :
157 2 : types.push_back(_disc);
158 2 : _series_type = std::make_unique<CylindricalDuo>(
159 2 : domains, _orders, types, name(), _expansion_type, _generation_type);
160 : }
161 : else
162 0 : mooseError("Unknown functional series type \"", _series_type_name, "\"");
163 :
164 : // Set the physical bounds of each of the single series if defined
165 388 : if (isParamValid("physical_bounds"))
166 194 : _series_type->setPhysicalBounds(_physical_bounds);
167 :
168 : // Resize the coefficient array as needed
169 192 : enforceSize(false), resize(getNumberOfTerms(), 0.0), enforceSize(true);
170 192 : setCharacteristics(_orders);
171 210 : }
172 :
173 : FunctionSeries &
174 327 : FunctionSeries::checkAndConvertFunction(const Function & function,
175 : const std::string & typeName,
176 : const std::string & objectName)
177 : {
178 327 : const FunctionSeries * test = dynamic_cast<const FunctionSeries *>(&function);
179 327 : if (!test)
180 8 : ::mooseError("In ",
181 : typeName,
182 : "-type object \"",
183 : objectName,
184 : "\": the named Function \"",
185 : function.name(),
186 : "\" must be a FunctionSeries-type object.");
187 :
188 319 : return *const_cast<FunctionSeries *>(test);
189 : }
190 :
191 : Real
192 164 : FunctionSeries::getStandardizedFunctionVolume() const
193 : {
194 164 : return _series_type->getStandardizedFunctionVolume();
195 : }
196 :
197 : std::size_t
198 1450 : FunctionSeries::getNumberOfTerms() const
199 : {
200 1450 : return _series_type->getNumberOfTerms();
201 : }
202 :
203 : const std::vector<size_t> &
204 164 : FunctionSeries::getOrders() const
205 : {
206 164 : return _orders;
207 : }
208 :
209 : /*
210 : * getAllGeneration() is defined in the FunctionalBasisInterface, which calls the pure virtual
211 : * evaluateGeneration() method of the CompositeSeriesBasisInterface class, which then calls the
212 : * getAllGeneration() method of each of the single series.
213 : */
214 : const std::vector<Real> &
215 187576 : FunctionSeries::getGeneration()
216 : {
217 187576 : return _series_type->getAllGeneration();
218 : }
219 :
220 : /*
221 : * getAllExpansion() is defined in the FunctionalBasisInterface, which calls the pure virtual
222 : * evaluateExpansion() method of the CompositeSeriesBasisInterface class, which then calls the
223 : * getAllExpansion() method of each of the single series.
224 : */
225 : const std::vector<Real> &
226 106902 : FunctionSeries::getExpansion()
227 : {
228 106902 : return _series_type->getAllExpansion();
229 : }
230 :
231 : /*
232 : * isInPhysicalBounds() is a pure virtual method of the FunctionalBasisInterface that is defined in
233 : * the CompositeSeriesBasisInterface class because it is agnostic to the underlying types of the
234 : * single series.
235 : */
236 : bool
237 204986 : FunctionSeries::isInPhysicalBounds(const Point & point) const
238 : {
239 204986 : return _series_type->isInPhysicalBounds(point);
240 : }
241 :
242 : void
243 294478 : FunctionSeries::setLocation(const Point & point)
244 : {
245 294478 : _series_type->setLocation(point);
246 294478 : }
247 :
248 : Real
249 109766 : FunctionSeries::evaluateValue(Real, const Point & point)
250 : {
251 : // Check that the point is within the physical bounds of the series
252 109766 : if (!isInPhysicalBounds(point))
253 : return 0.0;
254 :
255 : // Set the location at which to evaluate the series
256 106902 : setLocation(point);
257 :
258 106902 : return expand();
259 : }
260 :
261 : Real
262 106902 : FunctionSeries::expand()
263 : {
264 106902 : return expand(_coefficients);
265 : }
266 :
267 : Real
268 106902 : FunctionSeries::expand(const std::vector<Real> & coefficients)
269 : {
270 : // Evaluate all of the terms in the series
271 106902 : const std::vector<Real> & terms = getExpansion();
272 :
273 106902 : return std::inner_product(terms.begin(), terms.end(), coefficients.begin(), 0.0);
274 : }
275 :
276 : std::ostream &
277 930 : operator<<(std::ostream & stream, const FunctionSeries & me)
278 : {
279 : stream << "\n\n"
280 : << "FunctionSeries: " << me.name() << "\n"
281 2790 : << " Terms: " << me.getNumberOfTerms() << "\n";
282 930 : me._series_type->formatCoefficients(stream, me._coefficients);
283 930 : stream << "\n\n" << std::flush;
284 :
285 930 : return stream;
286 : }
287 :
288 : std::vector<std::size_t>
289 204 : FunctionSeries::convertOrders(const std::vector<unsigned int> & orders)
290 : {
291 204 : return std::vector<std::size_t>(orders.begin(), orders.end());
292 : }
|