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 659 : FunctionSeries::validParams()
21 : {
22 659 : InputParameters params = MutableCoefficientsFunctionInterface::validParams();
23 :
24 659 : 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 1318 : MooseEnum series_types("Cartesian CylindricalDuo");
31 1318 : MooseEnum single_series_types_1D("Legendre");
32 1318 : MooseEnum single_series_types_2D("Zernike");
33 :
34 1318 : 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 1318 : 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 1318 : 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 1318 : params.addParam<MooseEnum>("x", single_series_types_1D, "The series to use for the x-direction.");
53 1318 : params.addParam<MooseEnum>("y", single_series_types_1D, "The series to use for the y-direction.");
54 1318 : params.addParam<MooseEnum>("z", single_series_types_1D, "The series to use for the z-direction.");
55 :
56 1318 : 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 659 : std::string normalization_types = "orthonormal sqrt_mu standard";
62 1318 : MooseEnum expansion_type(normalization_types, "standard");
63 1318 : MooseEnum generation_type(normalization_types, "orthonormal");
64 1318 : params.addParam<MooseEnum>("expansion_type",
65 : expansion_type,
66 : "The normalization used for expansion of the basis functions");
67 1318 : params.addParam<MooseEnum>(
68 : "generation_type",
69 : generation_type,
70 : "The normalization used for generation of the basis function coefficients");
71 659 : return params;
72 1318 : }
73 :
74 392 : FunctionSeries::FunctionSeries(const InputParameters & parameters)
75 : : MutableCoefficientsFunctionInterface(this, parameters),
76 392 : _orders(convertOrders(getParam<std::vector<unsigned int>>("orders"))),
77 1176 : _physical_bounds(getParam<std::vector<Real>>("physical_bounds")),
78 784 : _series_type_name(getParam<MooseEnum>("series_type")),
79 784 : _x(getParam<MooseEnum>("x")),
80 784 : _y(getParam<MooseEnum>("y")),
81 784 : _z(getParam<MooseEnum>("z")),
82 784 : _disc(getParam<MooseEnum>("disc")),
83 784 : _expansion_type(getParam<MooseEnum>("expansion_type")),
84 1176 : _generation_type(getParam<MooseEnum>("generation_type"))
85 : {
86 : std::vector<MooseEnum> domains;
87 : std::vector<MooseEnum> types;
88 :
89 392 : 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 772 : if (isParamValid("x"))
99 : {
100 232 : domains.push_back(FunctionalBasisInterface::_domain_options = "x");
101 232 : types.push_back(_x);
102 : }
103 772 : if (isParamValid("y"))
104 : {
105 152 : domains.push_back(FunctionalBasisInterface::_domain_options = "y");
106 152 : types.push_back(_y);
107 : }
108 772 : if (isParamValid("z"))
109 : {
110 0 : domains.push_back(FunctionalBasisInterface::_domain_options = "z");
111 0 : types.push_back(_z);
112 : }
113 386 : if (types.size() == 0)
114 2 : mooseError("Must specify one of 'x', 'y', or 'z' for 'Cartesian' series!");
115 766 : _series_type = std::make_unique<Cartesian>(
116 384 : 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 764 : if (isParamValid("physical_bounds"))
166 382 : _series_type->setPhysicalBounds(_physical_bounds);
167 :
168 : // Resize the coefficient array as needed
169 380 : enforceSize(false), resize(getNumberOfTerms(), 0.0), enforceSize(true);
170 380 : setCharacteristics(_orders);
171 398 : }
172 :
173 : FunctionSeries &
174 629 : FunctionSeries::checkAndConvertFunction(const Function & function,
175 : const std::string & typeName,
176 : const std::string & objectName)
177 : {
178 629 : const FunctionSeries * test = dynamic_cast<const FunctionSeries *>(&function);
179 629 : 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 621 : return *const_cast<FunctionSeries *>(test);
189 : }
190 :
191 : Real
192 320 : FunctionSeries::getStandardizedFunctionVolume() const
193 : {
194 320 : return _series_type->getStandardizedFunctionVolume();
195 : }
196 :
197 : std::size_t
198 2646 : FunctionSeries::getNumberOfTerms() const
199 : {
200 2646 : return _series_type->getNumberOfTerms();
201 : }
202 :
203 : const std::vector<size_t> &
204 320 : FunctionSeries::getOrders() const
205 : {
206 320 : 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 281008 : FunctionSeries::getGeneration()
216 : {
217 281008 : 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 160128 : FunctionSeries::getExpansion()
227 : {
228 160128 : 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 307070 : FunctionSeries::isInPhysicalBounds(const Point & point) const
238 : {
239 307070 : return _series_type->isInPhysicalBounds(point);
240 : }
241 :
242 : void
243 441136 : FunctionSeries::setLocation(const Point & point)
244 : {
245 441136 : _series_type->setLocation(point);
246 441136 : }
247 :
248 : Real
249 164420 : FunctionSeries::evaluateValue(Real, const Point & point)
250 : {
251 : // Check that the point is within the physical bounds of the series
252 164420 : if (!isInPhysicalBounds(point))
253 : return 0.0;
254 :
255 : // Set the location at which to evaluate the series
256 160128 : setLocation(point);
257 :
258 160128 : return expand();
259 : }
260 :
261 : Real
262 160128 : FunctionSeries::expand()
263 : {
264 160128 : return expand(_coefficients);
265 : }
266 :
267 : Real
268 160128 : FunctionSeries::expand(const std::vector<Real> & coefficients)
269 : {
270 : // Evaluate all of the terms in the series
271 160128 : const std::vector<Real> & terms = getExpansion();
272 :
273 160128 : return std::inner_product(terms.begin(), terms.end(), coefficients.begin(), 0.0);
274 : }
275 :
276 : std::ostream &
277 1626 : operator<<(std::ostream & stream, const FunctionSeries & me)
278 : {
279 : stream << "\n\n"
280 : << "FunctionSeries: " << me.name() << "\n"
281 4878 : << " Terms: " << me.getNumberOfTerms() << "\n";
282 1626 : me._series_type->formatCoefficients(stream, me._coefficients);
283 1626 : stream << "\n\n" << std::flush;
284 :
285 1626 : return stream;
286 : }
287 :
288 : std::vector<std::size_t>
289 392 : FunctionSeries::convertOrders(const std::vector<unsigned int> & orders)
290 : {
291 392 : return std::vector<std::size_t>(orders.begin(), orders.end());
292 : }
|