3 Represents an OpenMPCD configuration.
5 Configurations are assumed to be in the libconfig++ format.
8 def __init__(self, source = None, canonicalize = True):
13 If `None`, constructs an empty configuration.
14 If the parameter is a string that corresponds to a file, it
15 is loaded as the configuration file.
16 If the string corresponds to a directory, it is assumed that
17 this directory contains a file `config.txt`, which is loaded.
18 Otherwise, it is assumed that the parameter contains a string
19 representation of a configuration.
20 @param[in] canonicalize
21 Set to true to canonicalize the input configuration, i.e.
22 transform it such that deprecated configuration settings are
23 translated into their new equivalents.
29 self.
_config = pylibconfig2.Config()
31 if source
is not None:
32 if os.path.isfile(source):
34 elif os.path.isdir(source):
35 self.
_config.read_file(source +
"/config.txt")
37 self.
_config.read_string(source)
45 Creates a group of the given name, if it does not exist, and returns it.
47 This will create groups recursively, if necessary.
54 if not isinstance(ret, pylibconfig2.ConfGroup):
59 components = name.split(
".")
61 for component
in components:
64 if component
not in tmp:
65 current.set(component, pylibconfig2.ConfGroup())
66 current = current.get(component)
68 if not isinstance(current, pylibconfig2.ConfGroup):
76 Returns whether this configuration is equivalent to `rhs`, i.e. if all
77 settings in this configuration exist and have the same values in `rhs`
80 This method does not discriminate what `libconfig` calls arrays and
84 Throws if `rhs` is not of type `Configuration`.
86 Throws if `pathTranslators` is not a list of elements that
87 `callable()` returns `True` for.
90 The instance to compare to.
91 @param[in] pathTranslators
92 Any configuration value string (on both this instance and
93 `rhs`) will be successively replaced by the results of
94 calling the functions in `pathTranslators` on that string.
97 def applyTanslators(s, translators):
98 for translator
in translators:
102 if not isinstance(rhs, Configuration):
104 if not isinstance(pathTranslators, list):
106 for translator
in pathTranslators:
107 if not callable(translator):
111 listTypes = (pylibconfig2.ConfList, pylibconfig2.ConfArray)
112 if isinstance(self.
_config, listTypes):
113 if not isinstance(rhs._config, listTypes):
115 if len(self.
_config) != len(rhs._config):
118 for i
in range(0, len(self.
_config)):
119 key =
"[" + str(i) +
"]"
122 if isinstance(self[key], self.__class__):
123 if not isinstance(rhs[key], self.__class__):
125 if not self[key].
isEquivalent(rhs[key], pathTranslators):
131 if type(lhsValue) != type(rhsValue):
134 if isinstance(lhsValue, str):
135 lhsString = applyTanslators(lhsValue, pathTranslators)
136 rhsString = applyTanslators(rhsValue, pathTranslators)
137 if lhsString != rhsString:
140 if lhsValue != rhsValue:
145 if isinstance(rhs._config, listTypes):
148 for key
in self.
_config.__dict__:
152 if isinstance(self[key], Configuration):
153 if not isinstance(rhs[key], Configuration):
155 if not self[key].
isEquivalent(rhs[key], pathTranslators):
157 elif isinstance(self[key], str):
158 if not isinstance(rhs[key], str):
160 lhsString = applyTanslators(self[key], pathTranslators)
161 rhsString = applyTanslators(rhs[key], pathTranslators)
162 if lhsString != rhsString:
165 if self[key] != rhs[key]:
168 for key
in rhs._config.__dict__:
177 Alias for `__getitem__`, which makes the interface more compatible to
178 `pylibconfig2.Config`.
186 Returns this configuration as a dictionary, with each key being a
187 setting name (including the names of the parent setting groups, lists,
188 etc.), and the corresponding dictionary value being the setting's value.
190 Empty groups, lists, and arrays will have `None` as their value.
195 if isinstance(self.
_config, pylibconfig2.ConfArray):
198 ret[
"[" + str(key) +
"]"] = value
202 for key
in self.
_config.__dict__:
203 if isinstance(self[key], Configuration):
205 for subkey, subvalue
in subdict.items():
206 ret[key +
"." + subkey] = subvalue
207 if len(subdict) == 0:
212 if isinstance(self.
_config, pylibconfig2.ConfList):
213 for i
in range(0, len(self.
_config)):
214 key =
"[" + str(i) +
"]"
215 if isinstance(self[key], Configuration):
217 for subkey, subvalue
in subdict.items():
218 ret[key +
"." + subkey] = subvalue
219 if len(subdict) == 0:
229 Returns a dictionary, the keys of which are exactly the setting names
230 that appear in either exactly one of `self` and `rhs`, or occur in both,
231 but with different values.
233 Each key's value is a list of two lists; the first corresponding to
234 `self`, the second to `rhs`. These lists are empty if the setting is not
235 set in the corresponding configuration, and otherwise contain the value
236 that is set in the corresponding configuration.
238 Setting names and values are understood as in `getAsFlatDictionary`.
241 The right-hand-side instance of this class.
244 if not isinstance(rhs, self.__class__):
248 rhsDict = rhs.getAsFlatDictionary()
252 if myKey
not in rhsDict:
253 ret[myKey] = [[myDict[myKey]], []]
254 elif myDict[myKey] != rhsDict[myKey]:
255 ret[myKey] = [[myDict[myKey]], [rhsDict[myKey]]]
257 for rhsKey
in rhsDict:
258 if rhsKey
not in myDict:
259 ret[rhsKey] = [[], [rhsDict[rhsKey]]]
267 Saves the configuration settings in a file with the given `path`.
269 The first line contains the setting names, ordered lexicographically and
270 separated by tab characters;
271 the second line contains the corresponding values, with string values
272 enclosed in curly braces, empty groups being represented by the string
273 (not enclosed in curly braces) `None`,
274 and with boolean values being represented by the strings `true` and
277 This format is supposed to be readable by the `LaTeX` package
281 Throws if any argument has an invalid type.
284 The file path to save the output to, as a string.
285 @param[in] additionalParameters
286 A dictionary containing, the keys of which are strings that
287 describe addition parameters to write to the file, while the
288 repsective values describe the parameter values.
291 if not isinstance(path, str):
294 if not isinstance(additionalParameters, dict):
296 for key
in additionalParameters:
297 if not isinstance(key, str):
302 for key, value
in additionalParameters.items():
303 assert key
not in flatDict
304 flatDict[key] = value
308 for key
in sorted(flatDict):
315 value = flatDict[key]
316 if isinstance(value, str):
317 valueLine +=
"{" + value +
"}"
320 elif isinstance(value, bool):
326 valueLine += str(value)
328 with open(path,
"w")
as f:
329 f.write(headerLine +
"\n" + valueLine)
334 Returns the value of the given key.
337 Throws if `key` is not of type `str`.
339 Throws if `key` is malformed or does not exist.
347 if not isinstance(key, str):
350 listTypes = (pylibconfig2.ConfList, pylibconfig2.ConfArray)
351 pylibconfigBaseTypes = listTypes + (pylibconfig2.ConfGroup,)
353 if isinstance(self.
_config, pylibconfig2.Config):
355 elif isinstance(self.
_config, pylibconfig2.ConfGroup):
356 components = key.split(
".")
358 for i, component
in enumerate(components):
359 if component
not in ret.keys():
361 ret = ret.get(component)
362 if isinstance(ret, listTypes):
365 if i + 1 == len(components):
368 newKey =
".".join(components[i + 1:])
370 elif isinstance(self.
_config, listTypes):
371 components = key.split(
".", 1)
372 numeral = components[0]
373 if numeral[0] !=
"[" or numeral[-1] !=
"]":
375 number = int(numeral[1:-1])
376 if str(number) != numeral[1:-1]:
381 if isinstance(ret, pylibconfigBaseTypes):
385 if len(components) == 1:
388 return tmp[
".".join(components[1:])]
397 if isinstance(ret, pylibconfigBaseTypes):
407 Sets the given configuration key to the given value.
409 Groups will be created as needed, if they do not exist already.
412 Throws if `key` is not of type `str`.
414 Throws if `key` is malformed.
417 The configuration key.
419 The configuration value.
422 if not isinstance(key, str):
427 components = key.split(
".")
429 for component
in components:
430 if len(component) == 0:
433 if isinstance(value, Configuration):
434 value = value._config
436 if len(components) == 1:
440 for component
in components[:-1]:
441 if not component
in group:
442 if component[0] ==
"[":
443 if not component[-1] ==
"]":
445 raise Exception(
"not implemented")
446 group.createGroup(component)
447 group = group[component]
450 if isinstance(group._config, pylibconfig2.ConfArray):
451 component = components[-1]
452 if component[0] !=
"[" or component[-1] !=
"]":
454 number = int(component[1:-1])
455 group._config[number] = value
457 group._config.set(components[-1], value)
462 Deletes the given setting recursively, if it exists.
465 Throws if `key` is not an instance of `str`.
468 The setting to delete, as a string.
471 if not isinstance(key, str):
477 components = key.split(
".")
479 if len(components) == 1:
483 parent = self[
".".join(components[:-1])]
484 del parent[components[-1]]
489 Returns whether the given configuration `key` has been set.
492 Throws if `key` is not an instance of `str`.
495 The setting to look up, as a string.
500 if not isinstance(key, str):
503 if isinstance(self.
_config, pylibconfig2.Config):
505 return self.
_config.lookup(key)
is not None
506 except pylibconfig2.ParseException:
509 if isinstance(self.
_config, pylibconfig2.ConfGroup):
510 components = key.split(
".")
512 for component
in components:
513 if component
not in ret.keys():
515 ret = ret.get(component)
519 listTypes = (pylibconfig2.ConfList, pylibconfig2.ConfArray)
520 if isinstance(self.
_config, listTypes):
521 components = key.split(
".", 1)
522 numeral = components[0]
523 if numeral[0] !=
"[" or numeral[-1] !=
"]":
525 number = int(numeral[1:-1])
526 if str(number) != numeral[1:-1]:
540 Returns a `str` representation of this instance.
542 This representation can be used to construct another instance of this
543 class that is equivalent to this instance.
549 def _canonicalize(self):
551 Transforms this instance's configuration data such that deprecated
552 settings are translated into their replacements.
555 if "boundaryConditions.type" in self:
556 if self[
"boundaryConditions.type"] ==
"Lees-Edwards":
557 shearRate = self[
"boundaryConditions.shearRate"]
558 del self[
"boundaryConditions.type"]
559 del self[
"boundaryConditions.shearRate"]
560 self[
"boundaryConditions.LeesEdwards.shearRate"] = shearRate
562 raise RuntimeError(
"Unknown boundary condition")
564 if "instrumentation.cellSubdivision" in self:
565 assert "instrumentation.flowProfile" not in self
566 assert "instrumentation.densityProfile" not in self
568 cellSubdivision = self[
"instrumentation.cellSubdivision"]
569 del self[
"instrumentation.cellSubdivision"]
571 self[
"instrumentation.flowProfile.cellSubdivision"] = \
573 self[
"instrumentation.densityProfile.cellSubdivision"] = \