-
Notifications
You must be signed in to change notification settings - Fork 152
Expand file tree
/
Copy pathInputSection.cpp
More file actions
358 lines (323 loc) · 12.3 KB
/
Copy pathInputSection.cpp
File metadata and controls
358 lines (323 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
//////////////////////////////////////////////////////////////////////////////////////
// This file is distributed under the University of Illinois/NCSA Open Source License.
// See LICENSE file in top directory for details.
//
// Copyright (c) 2023 QMCPACK developers.
//
// File developed by: Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory
// Peter W. Doak, doakpw@ornl.gov, Oak Ridge National Laboratory
//
//////////////////////////////////////////////////////////////////////////////////////
#include "InputSection.h"
#include "Message/UniformCommunicateError.h"
#include "ModernStringUtils.hpp"
#include "Utilities/string_utils.h"
namespace qmcplusplus
{
template<typename T>
const T& anyCastOrThrow(const std::string& section_name, const std::string& name, const std::any& value)
{
if (const auto* typed_value = std::any_cast<T>(&value))
return *typed_value;
throw UniformCommunicateError("std::any_cast failed in setFromValue for name:" + name + " in " + section_name);
}
[[noreturn]] std::any InputSection::assignAnyEnum(const std::string& tag) const
{
throw UniformCommunicateError("derived class must provide assignAnyEnum method if enum parameters are used");
}
[[noreturn]] void InputSection::setFromStreamCustom(const std::string& ename,
const std::string& name,
std::istringstream& svalue)
{
throw UniformCommunicateError("derived class must provide handleCustom method if custom parameters are used");
}
void InputSection::readAttributes(xmlNodePtr cur,
const std::string& element_name,
const std::vector<std::string>& do_not_consume)
{
xmlAttrPtr att = cur->properties;
while (att != NULL)
{
// unsafe att->name is an xmlChar, xmlChar is a UTF-8 byte
std::string name{lowerCase(castXMLCharToChar(att->name))};
// issue here is that we don't want to consume the name of the parameter as that has a special status in a parameter tag.
// This is due to the <parameter name="parameter_name> == <parameter_name> tag equivalence :(
std::string qualified_name{((element_name.size() > 0 && element_name != "parameter") ? (element_name + "::") : "") +
name};
// Since parameters don't get a qualified name this will still prevent consumption of any of the do_not_consume attributes from them
if (std::any_of(do_not_consume.begin(), do_not_consume.end(), [&name](auto& dnc) { return name == dnc; }))
{
att = att->next;
continue;
}
if (!isAttribute(qualified_name))
{
std::stringstream error;
error << "InputSection::readXML name " << name << " is not an attribute of " << section_name << "\n";
throw UniformCommunicateError(error.str());
}
std::istringstream stream(castXMLCharToChar(att->children->content));
if (isCustom(name))
setFromStreamCustom(element_name, qualified_name, stream);
else
setFromStream(qualified_name, stream);
att = att->next;
}
}
void InputSection::handleDelegate(const std::string& ename, const xmlNodePtr element)
{
assert(delegate_factories_.find(ename) != delegate_factories_.end());
std::string value_key;
std::any value = delegate_factories_[ename](element, value_key);
assignValue(value_key, value);
}
void InputSection::readXML(xmlNodePtr cur)
{
assert(cur != nullptr);
// For historical reasons that actual "type" of the element/input section is expressed in a very inconsistent way.
// It could be coded via the element name i.e. the tag, or at minimum a method, type, or name attribute.
std::string section_ename{lowerCase(castXMLCharToChar(cur->name))};
std::string section_method(lowerCase(getXMLAttributeValue(cur, "method")));
std::string section_type(lowerCase(getXMLAttributeValue(cur, "type")));
std::string section_name_actual(lowerCase(getXMLAttributeValue(cur, "name")));
// at anyrate one of these must match the section_name.
std::string lcase_section_name{lowerCase(section_name)};
auto checkSectionName = [§ion_name = section_name,
§ion_name_alternates = section_name_alternates](auto& possible_sname) {
std::string lcase_section_name{lowerCase(section_name)};
if (possible_sname == lcase_section_name)
return true;
if (section_name_alternates.size() > 0)
return std::any_of(section_name_alternates.begin(), section_name_alternates.end(),
[&possible_sname](auto& name_alternate) {
std::string lcase_alternate{lowerCase(name_alternate)};
return possible_sname == lcase_alternate;
});
return false;
};
if (!(checkSectionName(section_ename) || checkSectionName(section_method) || checkSectionName(section_type) ||
checkSectionName(section_name_actual)))
{
std::stringstream error;
error << "Input is invalid \"" << lcase_section_name << "\" does not match a defined input node!";
throw UniformCommunicateError(error.str());
}
// these attributes don't get an element name passed to them because by convention we save and define them unqualified.
readAttributes(cur, "", {"type"});
// read parameters
xmlNodePtr element = cur->xmlChildrenNode;
while (element != NULL)
{
// ename is the "name" of the XML element i.e. <ename [attributes]>
std::string ename{lowerCase(castXMLCharToChar(element->name))};
// value of the elements attribute = name
std::string name(lowerCase(getXMLAttributeValue(element, "name")));
// we need both ename and name to figure out how to handle an element because of the <parameter name="actual_parameter"> pattern.
// If the name attribute isn't there it should equal the element name.
if (name.size() < 1)
name = ename;
if (isDelegate(ename))
{
handleDelegate(ename, element);
}
else if (isCustom(ename))
{
std::istringstream stream(XMLNodeString{element});
setFromStreamCustom(ename, name, stream);
}
else if (ename == "parameter" || isParameter(ename))
{
if (ename == "parameter")
ename = name;
else
// We do this because the semantics of parameters are such that name can not have a unique value because
// if it did have one that the equivalence of <parameter_name> and <parameter name="parameter_name"> would
// be broken. Input code being ported could depend on this an an invariant.
name = ename;
if (!isParameter(ename))
{
std::stringstream error;
error << "InputSection::readXML name " << name << " is not a parameter of " << section_name << "\n";
throw UniformCommunicateError(error.str());
}
std::istringstream stream(XMLNodeString{element});
setFromStream(name, stream);
readAttributes(element, name, {"name"});
}
else if (ename != "text")
{
std::stringstream error;
error << "InputSection::readXML node name " << ename << " is not handled by InputSection subtype " << section_name
<< "\n";
throw UniformCommunicateError(error.str());
}
// else can't be an error case because this is how the whitespace text nodes
element = element->next;
}
// assign default values for optional variables
setDefaults();
// check input validity
checkValid();
//report();
}
void InputSection::init(const std::unordered_map<std::string, std::any>& init_values)
{
// assign inputted values
for (auto& [name, value] : init_values)
setFromValue(name, value);
// assign default values for optional variables
setDefaults();
// check input validity
checkValid();
//report();
}
void InputSection::registerDelegate(const std::string& tag,
std::function<std::any(xmlNodePtr cur, std::string& value_key)> factory)
{
delegate_factories_[tag] = factory;
}
void InputSection::setDefaults()
{
for (auto& [name, default_value] : default_values)
if (!has(name))
setFromValue(name, default_value);
}
void InputSection::setFromStream(const std::string& name, std::istringstream& svalue)
{
if (isString(name) || isEnumString(name))
{
std::string value;
svalue >> value;
assignValue(name, value);
}
else if (isMultiString(name))
{
std::vector<std::string> string_values;
for (std::string value; svalue >> value;)
string_values.push_back(value);
assignValue(name, string_values);
}
else if (isMultiReal(name))
{
std::vector<Real> real_values;
for (Real value; svalue >> value;)
real_values.push_back(static_cast<Real>(value));
assignValue(name, real_values);
}
else if (isBool(name))
{
std::string sval;
svalue >> sval;
bool value = sval == "yes" || sval == "true" || sval == "1";
assignValue(name, value);
}
else if (isInteger(name))
{
int value;
svalue >> value;
assignValue(name, value);
}
else if (isReal(name))
{
Real value;
svalue >> value;
assignValue(name, Real(value));
}
else if (isPosition(name))
{
Position value;
svalue >> value;
assignValue(name, value);
}
else
{
std::stringstream error;
error << "InputSection::set_from_stream name " << name << " in " << section_name << " does not have a type\n";
throw UniformCommunicateError(error.str());
}
}
template<typename T>
void InputSection::assignValue(const std::string& name, const T& value)
{
if (has(name) && !isMultiple(name))
throw UniformCommunicateError("Input is invalid " + section_name + " contains " + name +
" node with duplicate name!");
if (!isMultiple(name))
values_[name] = value;
else
{
if (has(name))
std::any_cast<std::vector<T>&>(values_[name]).push_back(value);
else
values_[name] = std::vector<T>{value};
}
}
void InputSection::setFromValue(const std::string& name, const std::any& value)
{
if (isString(name) || isEnumString(name))
assignValue(name, anyCastOrThrow<std::string>(section_name, name, value));
else if (isMultiString(name))
assignValue(name, anyCastOrThrow<std::vector<std::string>>(section_name, name, value));
else if (isMultiReal(name))
assignValue(name, anyCastOrThrow<std::vector<Real>>(section_name, name, value));
else if (isBool(name))
assignValue(name, anyCastOrThrow<bool>(section_name, name, value));
else if (isInteger(name))
assignValue(name, anyCastOrThrow<int>(section_name, name, value));
else if (isReal(name))
assignValue(name, anyCastOrThrow<Real>(section_name, name, value));
else if (isPosition(name))
assignValue(name, anyCastOrThrow<Position>(section_name, name, value));
else
{
std::stringstream error;
error << "InputSection::set_from_value name " << name << " in " << section_name << " does not have a type\n";
throw UniformCommunicateError(error.str());
}
}
void InputSection::checkValid()
{
// check that all required inputs are present
for (auto& name : required)
if (!has(name))
{
std::stringstream error;
error << "InputSection::check_valid required variable " << name << " in " << section_name
<< " has not been assigned\n";
throw UniformCommunicateError(error.str());
}
checkParticularValidity();
};
void InputSection::report(std::ostream& out) const
{
out << "\n" << section_name;
for (auto& [name, value] : values_)
{
out << "\n " << name << " = ";
if (isString(name))
out << std::any_cast<std::string>(value);
else if (isBool(name))
out << std::any_cast<bool>(value);
else if (isInteger(name))
out << std::any_cast<int>(value);
else if (isReal(name))
out << std::any_cast<Real>(value);
}
out << "\n\n";
}
void InputSection::report() const { report(app_log()); }
std::any InputSection::lookupAnyEnum(const std::string& enum_name,
const std::string& enum_value,
const std::unordered_map<std::string, std::any>& enum_map)
{
std::string enum_value_str(lowerCase(enum_name + "-" + enum_value));
try
{
return enum_map.at(enum_value_str);
}
catch (std::out_of_range& oor_exc)
{
std::throw_with_nested(UniformCommunicateError("bad_enum_tag_value: " + enum_value_str));
}
}
} // namespace qmcplusplus