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 : // MOOSE includes
11 : #include "MooseUtils.h"
12 : #include "MooseInit.h"
13 : #include "InputFileFormatter.h"
14 : #include "YAMLFormatter.h"
15 : #include "MooseTypes.h"
16 : #include "CommandLine.h"
17 : #include "SystemInfo.h"
18 : #include "Parser.h"
19 : #include "Units.h"
20 :
21 : #include "libmesh/parallel.h"
22 : #include "libmesh/fparser.hh"
23 :
24 : // Regular expression includes
25 : #include "pcrecpp.h"
26 :
27 : // C++ includes
28 : #include <string>
29 : #include <map>
30 : #include <fstream>
31 : #include <iomanip>
32 : #include <algorithm>
33 : #include <cstdlib>
34 : #include <filesystem>
35 :
36 : std::string
37 3909 : FuncParseEvaler::eval(hit::Field * n, const std::list<std::string> & args, hit::BraceExpander & exp)
38 : {
39 3909 : std::string func_text;
40 10208 : for (auto & s : args)
41 6299 : func_text += s;
42 3909 : auto n_errs = exp.errors.size();
43 :
44 3909 : FunctionParser fp;
45 3909 : fp.AddConstant("pi", libMesh::pi);
46 3909 : fp.AddConstant("e", std::exp(Real(1)));
47 3909 : std::vector<std::string> var_names;
48 3909 : auto ret = fp.ParseAndDeduceVariables(func_text, var_names);
49 3909 : if (ret != -1)
50 : {
51 0 : exp.errors.push_back(hit::errormsg(n, "fparse error: ", fp.ErrorMsg()));
52 0 : return n->val();
53 : }
54 :
55 3909 : std::string errors;
56 3909 : std::vector<double> var_vals;
57 9899 : for (auto & var : var_names)
58 : {
59 : // recursively check all parent scopes for the needed variables
60 5990 : hit::Node * curr = n;
61 15419 : while ((curr = curr->parent()))
62 : {
63 15418 : auto src = curr->find(var);
64 15418 : if (src && src != n && src->type() == hit::NodeType::Field)
65 : {
66 17967 : exp.used.push_back(hit::pathJoin({curr->fullpath(), var}));
67 5989 : var_vals.push_back(curr->param<double>(var));
68 5989 : break;
69 : }
70 : }
71 :
72 5990 : if (curr == nullptr)
73 1 : exp.errors.push_back(hit::errormsg(
74 : n, "\n no variable '", var, "' found for use in function parser expression"));
75 : }
76 :
77 3909 : if (exp.errors.size() != n_errs)
78 1 : return n->val();
79 :
80 3908 : std::stringstream ss;
81 3908 : ss << std::setprecision(17) << fp.Eval(var_vals.data());
82 :
83 : // change kind only (not val)
84 3908 : n->setVal(n->val(), hit::Field::Kind::Float);
85 3908 : return ss.str();
86 9898 : }
87 :
88 : std::string
89 72 : UnitsConversionEvaler::eval(hit::Field * n,
90 : const std::list<std::string> & args,
91 : hit::BraceExpander & exp)
92 : {
93 72 : std::vector<std::string> argv;
94 72 : argv.insert(argv.begin(), args.begin(), args.end());
95 :
96 : // no conversion, the expression currently only documents the units and passes through the value
97 72 : if (argv.size() == 2)
98 : {
99 48 : n->setVal(n->val(), hit::Field::Kind::Float);
100 48 : return argv[0];
101 : }
102 :
103 : // conversion
104 24 : if (argv.size() != 4 || (argv.size() >= 3 && argv[2] != "->"))
105 : {
106 0 : exp.errors.push_back(
107 0 : hit::errormsg(n,
108 : "units error: Expected 4 arguments ${units number from_unit -> to_unit} or "
109 : "2 arguments ${units number unit}"));
110 0 : return n->val();
111 : }
112 :
113 : // get and check units
114 24 : auto from_unit = MooseUnits(argv[1]);
115 24 : auto to_unit = MooseUnits(argv[3]);
116 24 : if (!from_unit.conformsTo(to_unit))
117 : {
118 0 : exp.errors.push_back(hit::errormsg(n,
119 : "units error: ",
120 0 : argv[1],
121 : " (",
122 : from_unit,
123 : ") does not convert to ",
124 0 : argv[3],
125 : " (",
126 : to_unit,
127 : ")"));
128 0 : return n->val();
129 : }
130 :
131 : // parse number
132 24 : Real num = MooseUtils::convert<Real>(argv[0]);
133 :
134 : // convert units
135 24 : std::stringstream ss;
136 24 : ss << std::setprecision(17) << to_unit.convert(num, from_unit);
137 :
138 : #ifndef NDEBUG
139 : mooseInfoRepeated(n->filename() + ':' + Moose::stringify(n->line()) + ':' +
140 : Moose::stringify(n->column()) + ": Unit conversion ",
141 : num,
142 : ' ',
143 : argv[1],
144 : " -> ",
145 : ss.str(),
146 : ' ',
147 : argv[3]);
148 : #endif
149 :
150 : // change kind only (not val)
151 24 : n->setVal(n->val(), hit::Field::Kind::Float);
152 24 : return ss.str();
153 72 : }
154 :
155 62800 : Parser::Parser(const std::vector<std::string> & input_filenames,
156 62800 : const std::optional<std::vector<std::string>> & input_text)
157 62800 : : _root(nullptr),
158 62800 : _input_filenames(input_filenames),
159 62800 : _input_text(input_text),
160 62800 : _app_type(std::string())
161 : {
162 62800 : }
163 :
164 11404 : Parser::Parser(const std::string & input_filename, const std::optional<std::string> & input_text)
165 34212 : : Parser(std::vector<std::string>{input_filename},
166 22822 : input_text ? std::optional<std::vector<std::string>>({*input_text})
167 11404 : : std::optional<std::vector<std::string>>())
168 : {
169 22815 : }
170 :
171 : void
172 4664334 : DupParamWalker ::walk(const std::string & fullpath, const std::string & /*nodepath*/, hit::Node * n)
173 : {
174 4664334 : std::string prefix = n->type() == hit::NodeType::Field ? "parameter" : "section";
175 :
176 4664334 : if (_have.count(fullpath) > 0)
177 : {
178 18 : auto existing = _have[fullpath];
179 18 : if (_duplicates.count(fullpath) == 0)
180 : {
181 18 : errors.push_back(
182 36 : hit::errormsg(existing, prefix, " '", fullpath, "' supplied multiple times"));
183 18 : _duplicates.insert(fullpath);
184 : }
185 18 : errors.push_back(hit::errormsg(n, prefix, " '", fullpath, "' supplied multiple times"));
186 : }
187 4664334 : _have[fullpath] = n;
188 4664334 : }
189 :
190 : void
191 2293544 : CompileParamWalker::walk(const std::string & fullpath,
192 : const std::string & /*nodepath*/,
193 : hit::Node * n)
194 : {
195 2293544 : if (n->type() == hit::NodeType::Field)
196 2293544 : _map[fullpath] = n;
197 2293544 : }
198 :
199 : void
200 1161 : OverrideParamWalker::walk(const std::string & fullpath,
201 : const std::string & /*nodepath*/,
202 : hit::Node * n)
203 : {
204 1161 : const auto it = _map.find(fullpath);
205 1161 : if (it != _map.end())
206 22 : warnings.push_back(hit::errormsg(n,
207 : " Parameter '",
208 : fullpath,
209 : "' overrides the same parameter in ",
210 22 : it->second->filename(),
211 : ":",
212 22 : it->second->line()));
213 1161 : }
214 :
215 : void
216 2180676 : BadActiveWalker ::walk(const std::string & /*fullpath*/,
217 : const std::string & /*nodepath*/,
218 : hit::Node * section)
219 : {
220 2180676 : auto actives = section->find("active");
221 2180676 : auto inactives = section->find("inactive");
222 :
223 26012 : if (actives && inactives && actives->type() == hit::NodeType::Field &&
224 2206688 : inactives->type() == hit::NodeType::Field && actives->parent() == inactives->parent())
225 : {
226 4 : errors.push_back(
227 8 : hit::errormsg(section, "'active' and 'inactive' parameters both provided in section"));
228 4 : return;
229 : }
230 :
231 : // ensures we don't recheck deeper nesting levels
232 2180672 : if (actives && actives->type() == hit::NodeType::Field && actives->parent() == section)
233 : {
234 25984 : auto vars = section->param<std::vector<std::string>>("active");
235 25984 : std::string msg = "";
236 67681 : for (auto & var : vars)
237 : {
238 41697 : if (!section->find(var))
239 9 : msg += var + ", ";
240 : }
241 25984 : if (msg.size() > 0)
242 : {
243 9 : msg = msg.substr(0, msg.size() - 2);
244 9 : errors.push_back(hit::errormsg(section,
245 : "variables listed as active (",
246 : msg,
247 : ") in section '",
248 18 : section->fullpath(),
249 : "' not found in input"));
250 : }
251 25984 : }
252 : // ensures we don't recheck deeper nesting levels
253 2180672 : if (inactives && inactives->type() == hit::NodeType::Field && inactives->parent() == section)
254 : {
255 2944 : auto vars = section->param<std::vector<std::string>>("inactive");
256 2944 : std::string msg = "";
257 6597 : for (auto & var : vars)
258 : {
259 3653 : if (!section->find(var))
260 8 : msg += var + ", ";
261 : }
262 2944 : if (msg.size() > 0)
263 : {
264 8 : msg = msg.substr(0, msg.size() - 2);
265 8 : errors.push_back(hit::errormsg(section,
266 : "variables listed as inactive (",
267 : msg,
268 : ") in section '",
269 16 : section->fullpath(),
270 : "' not found in input"));
271 : }
272 2944 : }
273 : }
274 :
275 : class FindAppWalker : public hit::Walker
276 : {
277 : public:
278 : void
279 2289983 : walk(const std::string & /*fullpath*/, const std::string & /*nodepath*/, hit::Node * n) override
280 : {
281 2289983 : if (n && n->type() == hit::NodeType::Field && n->fullpath() == "Application/type")
282 19 : _app_type = n->param<std::string>();
283 2289983 : }
284 62243 : const std::optional<std::string> & getApp() { return _app_type; };
285 :
286 : private:
287 : std::optional<std::string> _app_type;
288 : };
289 :
290 : const std::string &
291 38892 : Parser::getLastInputFileName() const
292 : {
293 38892 : if (_input_filenames.empty())
294 0 : mooseError("Parser::getLastInputFileName(): No inputs are set");
295 38892 : return _input_filenames.back();
296 : }
297 :
298 : void
299 62248 : Parser::parse()
300 : {
301 62248 : _root.reset();
302 :
303 62248 : if (_input_text && _input_text->size() != getInputFileNames().size())
304 0 : mooseError("Input text is not the same size as the input filenames");
305 :
306 62248 : if (getInputFileNames().size() > 1)
307 188 : mooseInfo("Merging inputs ", Moose::stringify(getInputFileNames()));
308 :
309 62248 : CompileParamWalker::ParamMap override_map;
310 62248 : CompileParamWalker cpw(override_map);
311 62248 : OverrideParamWalker opw(override_map);
312 :
313 62248 : std::string errmsg;
314 62248 : std::vector<std::string> dw_errmsg;
315 :
316 : // Whether or not to use real filepaths (from env)
317 : const std::string use_rel_paths_str =
318 62248 : std::getenv("MOOSE_RELATIVE_FILEPATHS") ? std::getenv("MOOSE_RELATIVE_FILEPATHS") : "false";
319 62248 : const auto use_real_paths = use_rel_paths_str == "0" || use_rel_paths_str == "false";
320 :
321 124687 : for (const auto i : index_range(getInputFileNames()))
322 : {
323 62463 : const auto & input_filename = getInputFileNames()[i];
324 : const auto corrected_filename =
325 62463 : use_real_paths ? MooseUtils::realpath(input_filename) : input_filename;
326 :
327 : // Parse the input text string if provided, otherwise read file from disk
328 62463 : std::string input;
329 62463 : std::string errmsg;
330 62463 : if (_input_text)
331 15 : input = (*_input_text)[i];
332 : else
333 : {
334 62448 : MooseUtils::checkFileReadable(corrected_filename, true);
335 62440 : std::ifstream f(corrected_filename);
336 62440 : input = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
337 62440 : }
338 :
339 : try
340 : {
341 : // provide stream to hit parse function to capture any syntax errors,
342 : // set parser root node, then throw those errors if any were captured
343 62455 : std::stringstream input_errors;
344 62455 : std::unique_ptr<hit::Node> root(hit::parse(corrected_filename, input, &input_errors));
345 62455 : hit::explode(root.get());
346 62455 : DupParamWalker dw;
347 62455 : root->walk(&dw, hit::NodeType::Field);
348 62455 : if (!_root)
349 62240 : _root = std::move(root);
350 : else
351 : {
352 215 : root->walk(&opw, hit::NodeType::Field);
353 215 : hit::merge(root.get(), _root.get());
354 : }
355 :
356 62455 : if (!input_errors.str().empty())
357 16 : throw hit::ParseError(input_errors.str());
358 :
359 62475 : for (auto & msg : dw.errors)
360 36 : errmsg += msg + "\n";
361 :
362 62439 : dw_errmsg.push_back(errmsg);
363 62439 : _root->walk(&cpw, hit::NodeType::Field);
364 62487 : }
365 16 : catch (hit::ParseError & err)
366 : {
367 16 : mooseError(err.what());
368 1 : }
369 62442 : }
370 :
371 : // warn about overridden parameters in multiple inputs
372 62224 : if (!opw.warnings.empty())
373 22 : mooseInfo(Moose::stringify(opw.warnings), "\n");
374 :
375 : // do as much error checking as early as possible so that errors are more useful instead
376 : // of surprising and disconnected from what caused them.
377 62224 : BadActiveWalker bw;
378 62224 : _root->walk(&bw, hit::NodeType::Section);
379 :
380 62224 : FindAppWalker fw;
381 62224 : _root->walk(&fw, hit::NodeType::Field);
382 62224 : if (fw.getApp())
383 19 : setAppType(*fw.getApp());
384 :
385 62229 : for (auto & msg : bw.errors)
386 5 : errmsg += msg + "\n";
387 :
388 : // Print parse errors related to bad active early
389 62224 : if (errmsg.size() > 0)
390 5 : mooseError(errmsg);
391 :
392 124644 : for (auto & msg : dw_errmsg)
393 62434 : if (msg.size() > 0)
394 9 : mooseError(msg);
395 62232 : }
|