Chi-Tech
input_parameters.cc
Go to the documentation of this file.
1#include "input_parameters.h"
2
3#include "chi_runtime.h"
4#include "chi_log.h"
5
6#include <sstream>
7#include <algorithm>
8#include <utility>
9
10#define ThrowInputError \
11 throw std::invalid_argument( \
12 (GetErrorOriginScope().empty() ? "" : GetErrorOriginScope() + "\n") + \
13 "Input error: " + ObjectType() + "\n" + err_stream.str())
14
15#define ExceptionParamNotPresent(param_name) \
16 throw std::logic_error(std::string(__PRETTY_FUNCTION__) + ": Parameter \"" + \
17 param_name + "\" not present in list of parameters.")
18
19namespace chi
20{
21
22const std::vector<std::string> InputParameters::system_ignored_param_names_ = {
23 "chi_obj_type"};
24
25// #################################################################
27{
28 for (const auto& param : other)
29 AddParameter(param);
30
31 // Copy maps
32 {
33 auto& other_map = other.parameter_class_tags_;
34 auto& this_map = parameter_class_tags_;
35 for (const auto& [param_name, tag] : other_map)
36 {
37 ChiLogicalErrorIf(this_map.count(param_name) != 0,
38 "Duplicate tags detected.");
39 this_map[param_name] = tag;
40 }
41 }
42 {
43 auto& other_map = other.parameter_doc_string_;
44 auto& this_map = parameter_doc_string_;
45 for (const auto& [param_name, tag] : other_map)
46 {
47 ChiLogicalErrorIf(this_map.count(param_name) != 0,
48 "Duplicate tags detected.");
49 this_map[param_name] = tag;
50 }
51 }
52 {
53 auto& other_map = other.deprecation_warning_tags_;
54 auto& this_map = deprecation_warning_tags_;
55 for (const auto& [param_name, tag] : other_map)
56 {
57 ChiLogicalErrorIf(this_map.count(param_name) != 0,
58 "Duplicate tags detected.");
59 this_map[param_name] = tag;
60 }
61 }
62 {
63 auto& other_map = other.deprecation_error_tags_;
64 auto& this_map = deprecation_error_tags_;
65 for (const auto& [param_name, tag] : other_map)
66 {
67 ChiLogicalErrorIf(this_map.count(param_name) != 0,
68 "Duplicate tags detected.");
69 this_map[param_name] = tag;
70 }
71 }
72 {
73 auto& other_map = other.renamed_error_tags_;
74 auto& this_map = renamed_error_tags_;
75 for (const auto& [param_name, tag] : other_map)
76 {
77 ChiLogicalErrorIf(this_map.count(param_name) != 0,
78 "Duplicate tags detected.");
79 this_map[param_name] = tag;
80 }
81 }
82 {
83 auto& other_map = other.type_mismatch_allowed_tags_;
84 auto& this_map = type_mismatch_allowed_tags_;
85 for (const auto& [param_name, tag] : other_map)
86 {
87 ChiLogicalErrorIf(this_map.count(param_name) != 0,
88 "Duplicate tags detected.");
89 this_map[param_name] = tag;
90 }
91 }
92 {
93 auto& other_map = other.parameter_link_;
94 auto& this_map = parameter_link_;
95 for (const auto& [param_name, tag] : other_map)
96 {
97 ChiLogicalErrorIf(this_map.count(param_name) != 0,
98 "Duplicate tags detected.");
99 this_map[param_name] = tag;
100 }
101 }
102 {
103 auto& other_map = other.constraint_tags_;
104 auto& this_map = constraint_tags_;
105 for (auto& [param_name, tag] : other_map)
106 {
107 ChiLogicalErrorIf(this_map.count(param_name) != 0,
108 "Duplicate tags detected.");
109 this_map[param_name] = std::move(tag);
110 }
111 }
112
113 return *this;
114}
115
116// #################################################################
117/**Sets the object type string for more descriptive error messages.*/
118void InputParameters::SetObjectType(const std::string& obj_type)
119{
120 class_name_ = obj_type;
121}
122
123// #################################################################
124/**Returns the object type string.*/
125std::string InputParameters::ObjectType() const { return class_name_; }
126
127// #################################################################
128/**Sets a link to the documentation of a different object.*/
129void InputParameters::LinkParameterToBlock(const std::string& param_name,
130 const std::string& block_name)
131{
132 ChiInvalidArgumentIf(not this->Has(param_name),
133 "Parameter \"" + param_name + "\" not present in block");
134 parameter_link_[param_name] = block_name;
135}
136
137// #################################################################
138/**Gets any linkage information of a parameter.*/
140 const std::string& param_name) const
141{
142 if (parameter_link_.count(param_name) > 0)
143 return parameter_link_.at(param_name);
144 return {};
145}
146
147// #################################################################
148/**Returns the parameter's doc string.*/
149std::string
150InputParameters::GetParameterDocString(const std::string& param_name)
151{
152 ChiInvalidArgumentIf(parameter_doc_string_.count(param_name) == 0,
153 "Invalid parameter \"" + param_name + "\".");
154 return parameter_doc_string_.at(param_name);
155}
156
157// #################################################################
158/**Determines if a parameter is ignored.*/
159bool InputParameters::IsParameterIgnored(const std::string& param_name)
160{
161 bool ignored = false;
162
163 {
164 auto& list = system_ignored_param_names_;
165 if (std::find(list.begin(), list.end(), param_name) != list.end())
166 ignored = true;
167 }
168
169 return ignored;
170}
171
172// #################################################################
173/**Specialization for block type parameters.*/
175 const ParameterBlock& block,
176 const std::string& doc_string)
177{
178 auto new_block = block;
179 new_block.SetBlockName(name);
180 AddParameter(new_block);
182 parameter_doc_string_[name] = doc_string;
183}
184
185// #################################################################
186/**Specialization for block type parameters.*/
188 const std::string& name,
189 const std::vector<ParameterBlock>& array,
190 const std::string& doc_string)
191{
192 ParameterBlock new_block(name);
193 new_block.ChangeToArray();
194 for (auto& block : array)
195 new_block.AddParameter(block);
196
197 AddParameter(new_block);
199 parameter_doc_string_[name] = doc_string;
200}
201
202// #################################################################
203/**Specialization for block type parameters.*/
205 const std::string& doc_string)
206{
207 ParameterBlock new_block(name);
208 AddParameter(new_block);
210 parameter_doc_string_[name] = doc_string;
211}
212
213// #################################################################
214/**Specialization for array type parameters.*/
216 const std::string& doc_string)
217{
218 ParameterBlock new_block(name);
219 new_block.ChangeToArray();
220 AddParameter(new_block);
222 parameter_doc_string_[name] = doc_string;
223}
224
225// #################################################################
226/**Returns an error string if the check has not passed. This method
227 * first checks whether all the required parameters are supplied. Then
228 * it checks that all the parameters supplied actually maps to valid parameters.
229 * */
231{
233 std::stringstream err_stream;
234
235 if (Chi::log.GetVerbosity() >= 2)
237 << "Number of parameters " << params.NumParameters();
238
239 // ================================== Check required parameters
240 // Loops over all input-parameters that have been
241 // classed as being required. Input-parameters that
242 // have any form of deprecation is ignored.
243 for (const auto& [param_index, tag] : parameter_class_tags_)
244 {
245 if (tag != InputParameterTag::REQUIRED) continue;
246
247 const auto& req_param = GetParam(param_index);
248 const auto& req_param_name = req_param.Name();
249
250 if (deprecation_warning_tags_.count(req_param_name) > 0 or
251 deprecation_error_tags_.count(req_param_name) > 0 or
252 renamed_error_tags_.count(req_param_name) > 0)
253 continue;
254
255 if (not params.Has(req_param_name))
256 err_stream << "Required param \"" << req_param_name
257 << "\" not supplied.\ndoc-string: "
258 << GetParameterDocString(req_param_name)
259 << "\nEnsure the parameter given is supplied or not nil";
260 }
261
262 if (not err_stream.str().empty()) ThrowInputError;
263
264 // ================================== Check unused parameters
265 // Loops over all candidate-parameters and
266 // checks whether they have an assignable
267 // input-parameter or if they have been renamed.
268 {
269 for (const auto& param : params.Parameters())
270 {
271 const auto& param_name = param.Name();
272 if (IsParameterIgnored(param_name)) continue;
273 if (not this->Has(param_name))
274 err_stream << "Invalid param \"" << param_name << "\" supplied.\n";
275 else if (renamed_error_tags_.count(param_name) > 0)
276 {
277 err_stream << "Invalid param \"" << param_name << "\" supplied. ";
278 err_stream << "The parameter has been renamed. ";
279 err_stream << renamed_error_tags_.at(param_name);
280 }
281 }
282
283 if (not err_stream.str().empty()) ThrowInputError;
284 }
285
286 // ================================== Check deprecation warnings
287 // Loops over all candidate-parameters and
288 // checks whether they have deprecation warnings.
289 {
290 const auto& dep_warns = deprecation_warning_tags_;
291 for (const auto& param : params.Parameters())
292 {
293 const auto& param_name = param.Name();
294
295 if (IsParameterIgnored(param_name)) continue;
296
297 if (this->Has(param_name) and (dep_warns.count(param_name) > 0))
299 << "Parameter \"" << param_name << "\" has been deprecated "
300 << "and will be removed soon.\n"
301 << dep_warns.at(param_name);
302 }
303 }
304
305 // ================================== Check deprecation errors
306 // Loops over all candidate-parameters and
307 // checks whether they have deprecation errors.
308 {
309 const auto& dep_errs = deprecation_error_tags_;
310 for (const auto& param : params.Parameters())
311 {
312 const auto& param_name = param.Name();
313
314 if (IsParameterIgnored(param_name)) continue;
315
316 if (this->Has(param_name) and (dep_errs.count(param_name) > 0))
317 {
319 << "Parameter \"" << param_name << "\" has been deprecated.\n"
320 << dep_errs.at(param_name);
321 Chi::Exit(EXIT_FAILURE);
322 }
323 }
324 }
325
326 // ================================== Now attempt to assign values
327 for (auto& param : params.Parameters())
328 {
329 const auto& param_name = param.Name();
330
331 if (IsParameterIgnored(param_name)) continue;
332
333 auto& input_param = GetParam(param_name);
334
335 // ====================== Check types match
336 if (param.Type() != input_param.Type())
337 {
338 if (type_mismatch_allowed_tags_.count(param_name) == 0)
339 {
340 err_stream << "Invalid parameter type \""
341 << ParameterBlockTypeName(param.Type())
342 << "\" for parameter \"" << param_name
343 << "\". Expecting type \""
344 << ParameterBlockTypeName(input_param.Type()) << "\".\n"
345 << "doc-string: " << GetParameterDocString(param_name);
346 continue;
347 } // if not mismatch allowed
348 } // if type mismatch
349
350 // ====================== Check constraint
351 if (constraint_tags_.count(input_param.Name()) != 0)
352 {
353 const auto& constraint = constraint_tags_.at(input_param.Name());
354 if (not constraint->IsAllowable(param.Value()))
355 {
356 err_stream << constraint->OutOfRangeString(input_param.Name(),
357 param.Value());
358 err_stream << "\n";
359 continue;
360 }
361 } // if constraint
362
363 if (Chi::log.GetVerbosity() >= 2)
364 Chi::log.Log0Verbose2() << "Setting parameter " << param_name;
365 input_param = param;
366 } // for input params
367
368 if (not err_stream.str().empty()) ThrowInputError;
369}
370
371// ##################################################################
372/**Marks a parameters as deprecated but will only produce a warning.*/
374 const std::string& param_name, const std::string& deprecation_message /*=""*/)
375{
376 if (Has(param_name))
377 deprecation_warning_tags_[param_name] = deprecation_message;
378 else
379 ExceptionParamNotPresent(param_name);
380}
381
382// ##################################################################
383/**Marks a parameters as deprecated and will produce an error if the parameter
384 * is specified.*/
386 const std::string& param_name, const std::string& deprecation_message /*=""*/)
387{
388 if (Has(param_name))
389 deprecation_error_tags_[param_name] = deprecation_message;
390 else
391 ExceptionParamNotPresent(param_name);
392}
393
394// ##################################################################
395/**Marks a parameters as renamed and will produce an error if the parameter
396 * is specified.*/
398 const std::string& param_name, const std::string& renaming_description)
399{
400 if (Has(param_name)) renamed_error_tags_[param_name] = renaming_description;
401 else
402 ExceptionParamNotPresent(param_name);
403}
404
405// ##################################################################
406/**Creates a range based constraint for a given parameter.*/
407void InputParameters::ConstrainParameterRange(const std::string& param_name,
408 AllowableRangePtr allowable_range)
409{
410 if (Has(param_name))
411 {
412 const auto& param_type = GetParam(param_name).Type();
414 param_type == ParameterBlockType::ARRAY,
415 std::string("Parameter \"") + param_name +
416 "\" is of type " +
417 ParameterBlockTypeName(param_type) +
418 " to which constraints cannot be applied");
419 constraint_tags_[param_name] = std::move(allowable_range);
420 }
421 else
422 ExceptionParamNotPresent(param_name);
423}
424
425// ##################################################################
426/**Useful for accepting varying datatypes or making a choice based
427 * upon the type of a parameter.*/
429 const std::string& param_name)
430{
431 ChiInvalidArgumentIf(not Has(param_name),
432 "Parameter \"" + param_name + "\" not present.");
433 type_mismatch_allowed_tags_[param_name] = true;
434}
435
436// ##################################################################
437/**Dumps the input parameters to stdout.*/
439{
440 Chi::log.Log() << "CLASS_NAME " << class_name_;
441
442 Chi::log.Log() << "DESCRIPTION_BEGIN";
443 std::cout << GetGeneralDescription() << "\n";
444 Chi::log.Log() << "DESCRIPTION_END\n";
445
446 Chi::log.Log() << "DOC_GROUP " << doc_group_;
447
448 const std::string sp2 = " ";
449 const std::string sp4 = " ";
450 const auto params = Parameters();
451 for (const auto& param : params)
452 {
453 const auto& param_name = param.Name();
454 Chi::log.Log() << sp2 << "PARAM_BEGIN " << param_name;
455
456 const auto type = param.Type();
457
458 Chi::log.Log() << sp4 << "TYPE " << ParameterBlockTypeName(type);
459
461 {
462 Chi::log.Log() << sp4 << "TAG OPTIONAL";
463 if (type != ParameterBlockType::BLOCK and
465 Chi::log.Log() << sp4 << "DEFAULT_VALUE " << param.Value().PrintStr();
466 else if (type == ParameterBlockType::ARRAY)
467 {
468 std::stringstream outstr;
469 outstr << sp4 << "DEFAULT_VALUE ";
470 for (size_t k = 0; k < param.NumParameters(); ++k)
471 {
472 const auto& sub_param = param.GetParam(k);
473 outstr << sub_param.Value().PrintStr() << ", ";
474 }
475 Chi::log.Log() << outstr.str();
476 }
477 }
478 else
479 Chi::log.Log() << sp4 << "TAG REQUIRED";
480
481 if (constraint_tags_.count(param_name) != 0)
482 Chi::log.Log() << sp4 << "CONSTRAINTS "
483 << constraint_tags_.at(param_name)->PrintRange();
484
485 if (parameter_doc_string_.count(param_name) != 0)
486 {
487 Chi::log.Log() << sp4 << "DOC_STRING_BEGIN";
488 std::cout << parameter_doc_string_.at(param_name) << "\n";
489 Chi::log.Log() << sp4 << "DOC_STRING_END";
490 }
491
492 const auto& linkage = GetParameterDocumentationLink(param_name);
493 if (not linkage.empty()) { Chi::log.Log() << sp4 << "LINKS " << linkage; }
494
495 Chi::log.Log() << sp2 << "PARAM_END";
496 }
497}
498
499} // namespace chi
#define ChiLogicalErrorIf(condition, message)
#define ChiInvalidArgumentIf(condition, message)
static void Exit(int error_code)
Definition: chi_runtime.cc:342
static chi::ChiLog & log
Definition: chi_runtime.h:81
LogStream Log0Warning()
Definition: chi_log.h:231
LogStream Log0Error()
Definition: chi_log.h:232
LogStream Log0Verbose2()
Definition: chi_log.h:235
LogStream Log(LOG_LVL level=LOG_0)
Definition: chi_log.cc:35
std::string GetParameterDocString(const std::string &param_name)
void AssignParameters(const ParameterBlock &params)
Assigns parameters with thorough type checks, deprecation checks, unused parameter checks.
ParameterBlock param_block_at_assignment_
std::map< std::string, InputParameterTag > parameter_class_tags_
std::map< std::string, std::string > deprecation_error_tags_
void SetParameterTypeMismatchAllowed(const std::string &param_name)
Sets a tag for the given parameter that will allow its type to be mismatched upon assignment.
std::map< std::string, std::string > deprecation_warning_tags_
void AddRequiredParameterArray(const std::string &name, const std::string &doc_string)
std::map< std::string, std::string > parameter_doc_string_
void AddParameter(ParameterBlock block)
void SetObjectType(const std::string &obj_type)
void MarkParamaterRenamed(const std::string &param_name, const std::string &renaming_description)
std::string GetParameterDocumentationLink(const std::string &param_name) const
void AddRequiredParameterBlock(const std::string &name, const std::string &doc_string)
InputParameters & operator+=(InputParameters other)
void ConstrainParameterRange(const std::string &param_name, AllowableRangePtr allowable_range)
std::map< std::string, AllowableRangePtr > constraint_tags_
static bool IsParameterIgnored(const std::string &param_name)
std::string ObjectType() const
std::map< std::string, std::string > renamed_error_tags_
std::map< std::string, bool > type_mismatch_allowed_tags_
void AddOptionalParameterBlock(const std::string &name, const ParameterBlock &block, const std::string &doc_string)
std::map< std::string, std::string > parameter_link_
std::unique_ptr< chi_data_types::AllowableRange > AllowableRangePtr
std::string GetGeneralDescription() const
void LinkParameterToBlock(const std::string &param_name, const std::string &block_name)
static const std::vector< std::string > system_ignored_param_names_
void AddOptionalParameterArray(const std::string &name, const std::vector< T > &array, const std::string &doc_string)
void MarkParamaterDeprecatedError(const std::string &param_name, const std::string &deprecation_message="")
void MarkParamaterDeprecatedWarning(const std::string &param_name, const std::string &deprecation_message="")
bool Has(const std::string &param_name) const
size_t NumParameters() const
void AddParameter(ParameterBlock block)
const std::vector< ParameterBlock > & Parameters() const
void SetBlockName(const std::string &name)
ParameterBlockType Type() const
ParameterBlock & GetParam(const std::string &param_name)
#define ThrowInputError
#define ExceptionParamNotPresent(param_name)
std::string ParameterBlockTypeName(ParameterBlockType type)