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 "PiecewiseTabularBase.h"
11 : #include "DelimitedFileReader.h"
12 : #include "JSONFileReader.h"
13 : #include "libmesh/int_range.h"
14 :
15 : InputParameters
16 74959 : PiecewiseTabularBase::validParams()
17 : {
18 74959 : InputParameters params = PiecewiseBase::validParams();
19 :
20 : // Parameters shared across all data input methods
21 74959 : MooseEnum axis("x=0 y=1 z=2");
22 74959 : params.addParam<MooseEnum>(
23 : "axis", axis, "The axis used (x, y, or z) if this is to be a function of position");
24 74959 : params.addParam<Real>("scale_factor", 1.0, "Scale factor to be applied to the output values");
25 74959 : params.declareControllable("scale_factor");
26 :
27 : // Data from input file parameters
28 74959 : params.addParam<std::vector<Real>>("xy_data",
29 : "All function data, supplied in abscissa, ordinate pairs");
30 74959 : params.addParam<std::vector<Real>>("x", "The abscissa values");
31 74959 : params.addParam<std::vector<Real>>("y", "The ordinate values");
32 :
33 : // Data from CSV file parameters
34 74959 : params.addParam<FileName>("data_file", "File holding CSV data");
35 74959 : params.addParam<unsigned int>("x_index_in_file", 0, "The abscissa index in the data file");
36 74959 : params.addParam<unsigned int>("y_index_in_file", 1, "The ordinate index in the data file");
37 74959 : params.addParam<std::string>(
38 : "x_title", "The title of the column/row containing the x data in the data file");
39 74959 : params.addParam<std::string>(
40 : "y_title", "The title of the column/row containing the y data in the data file");
41 224877 : params.addParam<bool>(
42 149918 : "xy_in_file_only", true, "If the data file only contains abscissa and ordinate data");
43 74959 : MooseEnum format("columns=0 rows=1", "rows");
44 74959 : params.addParam<MooseEnum>(
45 : "format", format, "Format of csv data file that is in either in columns or rows");
46 :
47 : // Data from JSON parameters
48 74959 : params.addParam<UserObjectName>("json_uo", "JSONFileReader holding the data");
49 74959 : params.addParam<std::vector<std::string>>(
50 : "x_keys", "Ordered vector of keys in the JSON tree to obtain the abscissa");
51 74959 : params.addParam<std::vector<std::string>>(
52 : "y_keys", "Ordered vector of keys in the JSON tree to obtain the ordinate");
53 :
54 74959 : params.addParamNamesToGroup("xy_data x y", "Data from input file");
55 74959 : params.addParamNamesToGroup(
56 : "data_file x_index_in_file y_index_in_file x_title y_title xy_in_file_only format",
57 : "Data from CSV file");
58 74959 : params.addParamNamesToGroup("json_uo x_keys y_keys", "Data from JSON");
59 149918 : return params;
60 74959 : }
61 :
62 1912 : PiecewiseTabularBase::PiecewiseTabularBase(const InputParameters & parameters)
63 : : PiecewiseBase(parameters),
64 1912 : _scale_factor(this->template getParam<Real>("scale_factor")),
65 1912 : _has_axis(isParamValid("axis")),
66 1912 : _raw_data_loaded(false)
67 : {
68 : // determine function argument
69 1912 : if (_has_axis)
70 220 : _axis = this->template getParam<MooseEnum>("axis");
71 :
72 : // Check all parameters for inconsistencies
73 3824 : if (isParamValid("data_file") + isParamValid("json_uo") +
74 1912 : (isParamValid("x") || isParamValid("y")) + isParamValid("xy_data") >
75 : 1)
76 8 : mooseError("Either 'data_file' or 'json_uo' or 'x' and 'y' or 'xy_data' must be specified "
77 : "exclusively.");
78 1904 : if ((isParamValid("x") + isParamValid("y")) % 2 != 0)
79 4 : mooseError(
80 : "Both 'x' and 'y' parameters or neither (for another input method) must be specified");
81 5461 : if (!isParamValid("data_file") &&
82 3561 : (isParamSetByUser("x_index_in_file") || isParamSetByUser("y_index_in_file") ||
83 3561 : isParamValid("x_title") || isParamValid("y_title") || isParamSetByUser("xy_in_file_only") ||
84 3557 : isParamSetByUser("format")))
85 4 : mooseError(
86 : "A parameter was passed for an option using data from a CSV file but the "
87 : "'data_file' parameter has not been set. This is not allowed. Please check the parameter "
88 : "groups in the documentation for the list of parameters for each data input method.");
89 1896 : if (!isParamValid("json_uo") && (isParamValid("x_keys") || isParamValid("y_keys")))
90 4 : mooseError("A parameter was passed for a JSON input option but the 'json_uo' parameter has not "
91 : "been set. This is not allowed. Please check the parameter groups in the "
92 : "documentation for the list of parameters for each data input method.");
93 :
94 : // load the data
95 1892 : if (isParamValid("data_file"))
96 239 : buildFromFile();
97 1653 : else if (isParamValid("x") && isParamValid("y"))
98 1576 : buildFromXandY();
99 77 : else if (isParamValid("xy_data"))
100 60 : buildFromXY();
101 17 : else if (!isParamValid("json_uo"))
102 4 : mooseError("Unknown X-Y data source. Are you missing a parameter? Did you misspell one?");
103 : // JSON data is not available at construction as we rely on a user object
104 1840 : }
105 :
106 : void
107 1790 : PiecewiseTabularBase::initialSetup()
108 : {
109 : // For JSON UO input, we need to wait for initialSetup to load the data
110 1790 : if (!isParamValid("json_uo"))
111 1777 : return;
112 : else
113 13 : buildFromJSON();
114 : }
115 :
116 : void
117 239 : PiecewiseTabularBase::buildFromFile()
118 : {
119 : // Input parameters
120 239 : const auto & data_file_name = this->template getParam<FileName>("data_file");
121 239 : const MooseEnum format = this->template getParam<MooseEnum>("format");
122 :
123 : // xy_in_file_only does not make sense here as it restricts the file to exactly
124 : // two cows/columns, which is not a likely scenario when looking up data by name.
125 : // A wrong 'format' parameter would be caught by the failing name resolution anyways.
126 239 : bool xy_only = this->template getParam<bool>("xy_in_file_only");
127 239 : if (isParamValid("x_title") || isParamValid("y_title"))
128 : {
129 26 : if (this->_pars.isParamSetByUser("xy_in_file_only") && xy_only)
130 0 : paramError(
131 : "xy_in_file_only",
132 : "When accessing data through 'x_title' or 'y_title' this parameter should not be used");
133 : else
134 26 : xy_only = false;
135 : }
136 :
137 : // Read the data from CSV file
138 239 : MooseUtils::DelimitedFileReader reader(data_file_name, &_communicator);
139 239 : reader.setFormatFlag(format.getEnum<MooseUtils::DelimitedFileReader::FormatFlag>());
140 239 : reader.setComment("#");
141 239 : reader.read();
142 :
143 235 : const auto & columns = reader.getNames();
144 235 : const auto & data = reader.getData();
145 :
146 235 : if (data.size() > 2 && xy_only)
147 32 : mooseError("In ",
148 16 : _name,
149 : ": Read more than two ",
150 : format,
151 : " of data from file '",
152 : data_file_name,
153 : "'. Did you mean to use \"format = ",
154 16 : format == "columns" ? "rows" : "columns",
155 : "\" or set \"xy_in_file_only\" to false?");
156 :
157 219 : unsigned int x_index = libMesh::invalid_uint;
158 219 : unsigned int y_index = libMesh::invalid_uint;
159 430 : const auto setIndex = [&](unsigned int & index, const std::string & prefix)
160 : {
161 430 : if (isParamValid(prefix + "_title"))
162 : {
163 52 : const auto name = this->template getParam<std::string>(prefix + "_title");
164 208 : for (const auto i : index_range(columns))
165 156 : if (columns[i] == name)
166 52 : index = i;
167 :
168 52 : if (index == libMesh::invalid_uint)
169 0 : paramError(prefix + "_title",
170 : "None of the ",
171 0 : format,
172 : " in the data file has the title '",
173 : name,
174 : "'.");
175 52 : }
176 : else
177 378 : index = this->template getParam<unsigned int>(prefix + "_index_in_file");
178 :
179 430 : if (index >= data.size())
180 32 : paramError(prefix + "_index_in_file",
181 : "Index out-of-range of the available data in '",
182 16 : data_file_name,
183 : "', which contains ",
184 : data.size(),
185 : " ",
186 : format,
187 : " of data.");
188 414 : };
189 :
190 219 : setIndex(x_index, "x");
191 211 : setIndex(y_index, "y");
192 :
193 203 : if (x_index == y_index)
194 4 : mooseError(
195 4 : "In ", _name, ": 'x_index_in_file' and 'y_index_in_file' are set to the same value.");
196 :
197 : // Update the input vectors to contained the desired data
198 199 : _raw_x = reader.getData(x_index);
199 199 : _raw_y = reader.getData(y_index);
200 :
201 : // Size mismatch error
202 199 : if (_raw_x.size() != _raw_y.size())
203 4 : mooseError("In ", _name, ": Lengths of x and y data do not match.");
204 :
205 195 : _raw_data_loaded = true;
206 195 : }
207 :
208 : void
209 13 : PiecewiseTabularBase::buildFromJSON()
210 : {
211 13 : auto & json_uo = getUserObject<JSONFileReader>("json_uo");
212 13 : if (!isParamValid("x_keys"))
213 0 : mooseError("Missing 'x_keys' parameters for loading data from JSON");
214 13 : if (!isParamValid("y_keys"))
215 0 : mooseError("Missing 'y_keys' parameters for loading data from JSON");
216 13 : json_uo.getVector<Real>(getParam<std::vector<std::string>>("x_keys"), _raw_x);
217 13 : json_uo.getVector<Real>(getParam<std::vector<std::string>>("y_keys"), _raw_y);
218 13 : _raw_data_loaded = true;
219 :
220 : // Size mismatch error
221 13 : if (_raw_x.size() != _raw_y.size())
222 0 : mooseError("Lengths of x (", _raw_x.size(), ") and y (", _raw_y.size(), ") data do not match.");
223 13 : }
224 :
225 : void
226 1576 : PiecewiseTabularBase::buildFromXandY()
227 : {
228 1576 : _raw_x = this->template getParam<std::vector<Real>>("x");
229 1576 : _raw_y = this->template getParam<std::vector<Real>>("y");
230 1576 : _raw_data_loaded = true;
231 1576 : }
232 :
233 : void
234 60 : PiecewiseTabularBase::buildFromXY()
235 : {
236 60 : const auto & xy = this->template getParam<std::vector<Real>>("xy_data");
237 60 : const auto xy_size = xy.size();
238 60 : if (xy_size % 2 != 0)
239 4 : mooseError("In ", _name, ": Length of data provided in 'xy_data' must be a multiple of 2.");
240 :
241 56 : const auto data_size = xy_size / 2;
242 56 : _raw_x.resize(data_size);
243 56 : _raw_y.resize(data_size);
244 256 : for (const auto i : make_range(data_size))
245 : {
246 200 : _raw_x[i] = xy[2 * i];
247 200 : _raw_y[i] = xy[2 * i + 1];
248 : }
249 56 : _raw_data_loaded = true;
250 56 : }
|