votca 2024.2-dev
Loading...
Searching...
No Matches
optionshandler.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18// Local VOTCA includes
22#include <algorithm>
23#include <stdexcept>
24#include <string>
25
26namespace votca {
27namespace tools {
28
29template <typename T>
30static bool IsValidCast(const tools::Property &prop) {
31 try {
32 prop.as<T>();
33 return true;
34 } catch (const std::runtime_error &e) {
35 return false;
36 }
37}
38
40
41 if (prop.hasAttribute("link")) {
42 Tokenizer tok(prop.getAttribute<std::string>("link"), " ,");
43 for (std::string path : tok) {
44 std::string relative_path = "subpackages/" + path;
45 std::string file_path = defaults_path_ + relative_path;
46 tools::Property package;
47 package.LoadFromXML(file_path);
48 const tools::Property &options = *(package.begin());
50 attr != options.lastAttribute(); ++attr) {
51 if (!prop.hasAttribute(attr->first)) {
52 prop.setAttribute(attr->first, attr->second);
53 }
54 }
55
56 for (const auto &child : options) {
57 prop.add(child);
58 }
59 }
60 }
61
62 for (tools::Property &child : prop) {
63 ResolveLinks(child);
64 }
65}
66
68 const std::string &calcname) const {
69 Property print = LoadDefaults(calcname);
70
71 CheckUserInput(user_input, print);
72
73 OverwriteDefaultsWithUserInput(user_input.get("options"),
74 print.get("options"));
75 RemoveOptional(print);
76 CheckRequired(print);
79 return print;
80}
81
83 const Property &defaults) const {
84 if (user_input.hasAttribute("unchecked")) {
85 return;
86 } else {
87 for (const auto &child : user_input) {
88 if (defaults.exists(child.name())) {
89 CheckUserInput(child, defaults.get(child.name()));
90 } else {
91 throw std::runtime_error("Votca has no option:" + child.path() + "." +
92 child.name());
93 }
94 }
95 }
96}
97
98void OptionsHandler::CheckRequired(const Property &options) const {
99 for (const auto &child : options) {
100 CheckRequired(child);
101 }
102 if (options.hasAttribute("default") &&
103 options.getAttribute<std::string>("default") == "REQUIRED" &&
104 !options.hasAttribute("injected")) {
105 throw std::runtime_error("Please specify an input for:" + options.path() +
106 "." + options.name());
107 }
108}
109
111 options.deleteChildren([](const Property &p) {
112 return p.hasAttribute("default") &&
113 p.getAttribute<std::string>("default") == "OPTIONAL" &&
114 !p.hasAttribute("injected");
115 });
116 for (auto &child : options) {
117 RemoveOptional(child);
118 }
119}
120
121Property OptionsHandler::CalculatorOptions(const std::string &calcname) const {
122 Property print = LoadDefaults(calcname);
124 CleanAttributes(print, {"link"});
125 return print;
126}
127
129 Property &options, const std::vector<std::string> &attributes) const {
130 for (const auto &attribute : attributes) {
131 if (options.hasAttribute(attribute)) {
132 options.deleteAttribute(attribute);
133 }
134 }
135 for (auto &child : options) {
136 CleanAttributes(child, attributes);
137 }
138}
139
140// load the xml description of the calculator (with defaults and test values)
141Property OptionsHandler::LoadDefaults(const std::string &calculatorname) const {
142 Property defaults_all;
143 std::string defaults_file_path =
144 defaults_path_ + "/" + calculatorname + ".xml";
145 defaults_all.LoadFromXML(defaults_file_path);
146 ResolveLinks(defaults_all);
147 return defaults_all;
148}
149
151 for (Property &prop : defaults) {
152 if (prop.HasChildren()) {
154 } else if (prop.hasAttribute("default") && !prop.hasAttribute("injected")) {
155 std::string value = prop.getAttribute<std::string>("default");
156 if (std::none_of(
158 [value](const std::string &keyword) { return value == keyword; }))
159 prop.value() = value;
160 ;
161 }
162 }
163}
164
166 Property &defaults) const {
167 // There are 3 distinct cases
168 // a) normal option that can be copied over
169 // b) a list attribute is discovered
170 // c) an unchecked attriute is found,then the whole set is simply copied
171 // over from the user_options, should be done afterwards, as an unchecked
172 // session is not checked and simply copied over
173
174 if (!defaults.hasAttribute("list")) {
175 defaults.value() = user_input.value();
176 defaults.setAttribute("injected", "true");
177 for (auto &child : defaults) {
178 if (user_input.exists(child.name())) {
179 OverwriteDefaultsWithUserInput(user_input.get(child.name()), child);
180 }
181 }
182 } else {
183 defaults.setAttribute("injected", "true");
184 std::map<std::string, Index> tags;
185 for (const auto &child : defaults) {
186 tags[child.name()]++;
187 }
188
189 for (const auto &tag : tags) {
190 if (tag.second > 1) {
191 throw std::runtime_error(
192 "Developers: Each distinct tag in list should only appear once.");
193 }
194 std::vector<const tools::Property *> inputs =
195 user_input.Select(tag.first);
196
197 // if the input has no elements with that tag, remove all children from
198 // default with that tag as well
199 if (inputs.empty()) {
200 defaults.deleteChildren(
201 [&](const Property &prop) { return prop.name() == tag.first; });
202 } else {
203 // copy the element from defaults if the user_input has the element more
204 // than once
205 Property copy = defaults.get(tag.first);
206 for (Index i = 1; i < Index(inputs.size()); i++) {
207 defaults.add(copy);
208 }
209 std::vector<Property *> newdefault_elements =
210 defaults.Select(tag.first);
211 // for each element in both lists do the overwrite again
212 for (Index i = 0; i < Index(inputs.size()); i++) {
213 OverwriteDefaultsWithUserInput(*inputs[i], *newdefault_elements[i]);
214 }
215 }
216 }
217 }
218
219 if (defaults.hasAttribute("unchecked")) {
220 for (const auto &child : user_input) {
221 defaults.add(child);
222 }
223 }
224}
225
226std::vector<std::string> OptionsHandler::GetPropertyChoices(const Property &p) {
227 if (p.hasAttribute("choices")) {
228 std::string att = p.getAttribute<std::string>("choices");
229 std::size_t start_bracket = att.find('[');
230 if (start_bracket != std::string::npos) {
231 std::size_t end_bracket = att.find(']');
232 att = att.substr(start_bracket + 1, end_bracket - start_bracket - 1);
233 }
234 return Tokenizer{att, " ,"}.ToVector();
235 } else {
236 return {};
237 }
238}
239
241
242 for (const Property &prop : p) {
243 if (prop.HasChildren()) {
245 } else {
246 std::vector<std::string> choices = GetPropertyChoices(prop);
247 if (choices.empty()) {
248 continue;
249 }
250 if (!IsValidOption(prop, choices)) {
251 std::ostringstream oss;
252 oss << "\nThe input value for \"" << prop.name() << "\"";
253 if (choices.size() == 1) {
254 oss << " should be a \"" << choices.front() << "\"";
255 } else {
256 oss << " should be one of the following values: ";
257 for (const std::string &c : choices) {
258 oss << "\"" << c << "\""
259 << " ";
260 }
261 }
262 oss << " But \"" << prop.value()
263 << "\" cannot be converted into one.\n";
264 throw std::runtime_error(oss.str());
265 }
266 }
267 }
268}
269
271 const Property &prop, const std::vector<std::string> &choices) const {
272
273 const std::string &head = choices.front();
274 std::ostringstream oss;
275 bool is_valid = true;
276 if (head == "bool") {
277 is_valid = IsValidCast<bool>(prop);
278 } else if (head == "float") {
279 is_valid = IsValidCast<double>(prop);
280 } else if (head == "float+") {
281 is_valid = IsValidCast<double>(prop) && (prop.as<double>() >= 0.0);
282 } else if (head == "int") {
283 is_valid = IsValidCast<Index>(prop);
284 } else if (head == "int+") {
285 is_valid = IsValidCast<Index>(prop) && (prop.as<Index>() >= 0);
286 } else {
287 std::string value = prop.as<std::string>();
288 std::string att = prop.getAttribute<std::string>("choices");
289 std::size_t start_bracket = att.find('[');
290 if (start_bracket == std::string::npos) {
291 // There is a single choice out of multiple default valid choices
292 auto it = std::find(std::cbegin(choices), std::cend(choices), value);
293 is_valid = (it != std::cend(choices));
294 } else {
295 // there are multiple valid choices
296 Tokenizer tok{value, " ,"};
297 for (const std::string &word : tok) {
298 auto it = std::find(std::cbegin(choices), std::cend(choices), word);
299 if (it == std::cend(choices)) {
300 is_valid = false;
301 break;
302 }
303 }
304 }
305 }
306 if (std::find(additional_choices_.begin(), additional_choices_.end(),
307 prop.as<std::string>()) != additional_choices_.end()) {
308 is_valid = true;
309 }
310 return is_valid;
311}
312
313} // namespace tools
314} // namespace votca
void InjectDefaultsAsValues(Property &options) const
bool IsValidOption(const Property &prop, const std::vector< std::string > &choices) const
std::vector< std::string > additional_choices_
void OverwriteDefaultsWithUserInput(const Property &p, Property &defaults) const
void RemoveOptional(Property &options) const
Removes tags which have no value and default="OPTIONAL".
Property ProcessUserInput(const Property &user_input, const std::string &calcname) const
Load the default options and merge them with the user input.
static std::vector< std::string > GetPropertyChoices(const Property &p)
Property CalculatorOptions(const std::string &calcname) const
Resolve links and return all the options of a calculator.
Property LoadDefaults(const std::string &calculatorname) const
Loads default options stored in defaults_path_.
void CheckUserInput(const Property &user_input, const Property &defaults) const
Checks if all keywords in user_input (apart from sections named "unchecked") have corresponding keys ...
void CheckRequired(const Property &options) const
Checks that all options with default="REQUIRED" are filled in.
void CleanAttributes(Property &options, const std::vector< std::string > &attributes) const
std::vector< std::string > reserved_keywords_
void ResolveLinks(tools::Property &prop) const
Resolves "link" attribute in the Property by filling in defaults. Already existing tags are not overw...
void RecursivelyCheckOptions(const Property &p) const
class to manage program options with xml serialization functionality
Definition property.h:55
Property & add(const std::string &key, const std::string &value)
add a new property to structure
Definition property.cc:108
void deleteChildren(cond condition)
deletes all children that fulfill a condition
Definition property.h:348
std::string & path()
full path of property (including parents)
Definition property.h:167
Property & get(const std::string &key)
get existing property
Definition property.cc:79
void deleteAttribute(const std::string &attribute)
deletes an attribute
Definition property.cc:206
std::string & value()
reference to value of property
Definition property.h:153
bool exists(const std::string &key) const
check whether property exists
Definition property.cc:122
T as() const
return value as type
Definition property.h:283
bool hasAttribute(const std::string &attribute) const
return true if an attribute exists
Definition property.cc:118
iterator begin()
iterator to first child property
Definition property.h:188
T getAttribute(const std::string &attribute) const
return attribute as type
Definition property.h:308
std::string & name()
name of property
Definition property.h:159
std::map< std::string, std::string >::const_iterator const_AttributeIterator
Definition property.h:234
AttributeIterator firstAttribute()
returns an iterator to the first attribute
Definition property.h:247
std::vector< Property * > Select(const std::string &filter)
select property based on a filter
Definition property.cc:185
void setAttribute(const std::string &attribute, const T &value)
set an attribute
Definition property.h:314
AttributeIterator lastAttribute()
returns an iterator to the last attribute
Definition property.h:252
void LoadFromXML(std::string filename)
Definition property.cc:238
break string into words
Definition tokenizer.h:72
std::vector< T > ToVector()
store all words in a vector of type T, does type conversion.
Definition tokenizer.h:109
static bool IsValidCast(const tools::Property &prop)
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26