git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@16053 f3b2605a-c512-4ea7-a41b...
[lammps.git] / tools / i-pi / ipi / utils / inputvalue.py
blobd1bbc631fdf47a3245c66f4f84f2878e6981b3fb
1 """Contains the classes that are used to write to and read from restart files.
3 Copyright (C) 2013, Joshua More and Michele Ceriotti
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http.//www.gnu.org/licenses/>.
19 The classes defined in this module define the base functions which parse the
20 data in the restart files. Each restart object defined has a fields and an
21 attributes dictionary, which are filled with the tags and attributes that
22 are allowed to be present, along with their default values and data type.
24 These are then filled with the data from the xml file when the program
25 is initialised, and are filled by the values calculated in the program which
26 are then output to the checkpoint file when a restart file is required.
28 Also deals with checking for user input errors, of the form of misspelt tags,
29 bad data types, and failure to input required fields.
31 Classes:
32 Input: Base input class.
33 InputAttribute: Input class for attribute data.
34 InputValue: Input class for scalar objects.
35 InputArray: Input class for arrays.
36 input_default: Class used to create mutable objects dynamically.
37 """
39 __all__ = ['Input', 'InputValue', 'InputAttribute', 'InputArray', 'input_default']
41 import numpy as np
42 from copy import copy
43 from ipi.utils.io.io_xml import *
44 from ipi.utils.units import unit_to_internal, unit_to_user
48 class input_default(object):
49 """Contains information required to dynamically create objects
51 Used so that we can define mutable default input values to various tags
52 without the usual trouble with having a class object that is also mutable,
53 namely that all members of that class share the same mutable object, so that
54 changing it for one instance of that class changes it for all others. It
55 does this by not holding the mutable default value, but instead the
56 information to create it, so that each instance of an input class can
57 have a separate instance of the default value.
59 Attributes:
60 type: Either a class type or function call from which to create the
61 default object.
62 args: A tuple giving positional arguments to be passed to the function.
63 kwargs: A dictionary giving key word arguments to be passed to the
64 function.
65 """
67 def __init__(self, factory, args = None, kwargs = None):
68 """Initialises input_default.
70 Args:
71 type: The class or function to be used to create the default object.
72 args: A tuple giving the arguments to be used to initialise
73 the default value.
74 kwargs: A dictionary giving the key word arguments to be used
75 to initialise the default value.
76 """
78 if args is None:
79 args = ()
80 if kwargs is None:
81 kwargs = {}
82 # a default will be generated by factory(*args, **kwargs)
83 # *args unpacks the tuple, and is used for positional arguments
84 # **kwargs unpacks the dictionary, and is used for keyword arguments
85 self.factory = factory
86 self.args = args
87 self.kwargs = kwargs
90 class Input(object):
91 """Base class for input handling.
93 Has the generic methods for dealing with the xml input file. Parses the input
94 data, outputs the output data, and deals with storing and returning the
95 data obtained during the simulation for the restart files.
97 Attributes:
98 fields: A dictionary holding the possible tags contained within the
99 tags for this restart object, which are then turned into the objects
100 held by the object given by this restart object. The dictionary is
101 of the form:
102 {"tag name": ( Input_object,
103 {"default": default value,
104 "dtype": data type,
105 "options": list of available options,
106 "help": help string,
107 "dimension": dimensionality of data}), ... }.
108 dynamic: A dictionary holding the possible tags contained within the
109 tags for this restart object, which are then turned into the objects
110 held by the object given by this restart object. These are used for
111 tags that can be specified more than once.
112 The dictionary is of the form:
113 {"tag name": ( Input_object,
114 {"default": default value,
115 "dtype": data type,
116 "options": list of available options,
117 "help": help string,
118 "dimension": dimensionality of data}), ... }.
119 attribs: A dictionary holding the attribute data for the tag for this
120 restart object. The dictionary is of the form:
121 {"attribute name": ( Input_object,
122 {"default": default value,
123 "dtype": data type,
124 "options": list of available options,
125 "help": help string,
126 "dimension": dimensionality of data}), ... }.
127 extra: A list of tuples ( "name", Input_object ) that may be used to
128 extend the capabilities of the class, i.e. to hold several instances of
129 a field with the same name, or to hold variable numbers of elements.
130 default_help: The default help string.
131 _help: The help string of the object. Defaults to default_help.
132 _default: Optional default value.
133 _optional: A bool giving whether the field is a required field.
134 _explicit: A bool giving whether the field has been specified by the user.
135 _text: All text written between the tags of the object.
136 _label: A label to be used to identify the class in the latex user manual.
137 _defwrite: The string which would be output if the class has its default
138 value.
141 fields = {}
142 attribs = {}
143 dynamic = {}
145 default_help = "Generic input value"
146 default_label = "" #used as a way to reference a particular class using
147 #hyperlinks
149 def __init__(self, help=None, default=None):
150 """Initialises Input.
152 Automatically adds all the fields and attribs names to the input object's
153 dictionary, then initialises all the appropriate input objects
154 as the corresponding values.
156 Args:
157 help: A help string.
158 default: A default value.
161 # list of extended (dynamic) fields
162 self.extra = []
164 if help is None:
165 self._help = self.default_help
166 else:
167 self._help = help
169 if isinstance(default,input_default):
170 #creates default dynamically if a suitable template is defined.
171 self._default = default.factory(*default.args, **default.kwargs)
172 else:
173 self._default = default
175 self._optional = not (self._default is None)
177 self._label = self.default_label
179 #For each tag name in the fields and attribs dictionaries,
180 #creates and object of the type given, expanding the dictionary to give
181 #the arguments of the __init__() function, then adds it to the input
182 #object's dictionary.
183 for f, v in self.fields.iteritems():
184 self.__dict__[f] = v[0](**v[1])
186 for a, v in self.attribs.iteritems():
187 self.__dict__[a] = v[0](**v[1])
189 self.set_default()
191 self._text = ""
193 # stores what we would write out if the default was set
194 self._defwrite = ""
195 if not self._default is None:
196 self._defwrite = self.write(name="%%NAME%%")
198 def set_default(self):
199 """Sets the default value of the object."""
201 if not self._default is None:
202 self.store(self._default)
203 elif not hasattr(self, 'value'):
204 self.value = None #Makes sure we don't get exceptions when we
205 #look for self.value
207 self._explicit = False #Since the value was not set by the user
209 def store(self, value=None):
210 """Dummy function for storing data."""
212 self._explicit = True
213 pass
215 def fetch(self):
216 """Dummy function to retrieve data."""
218 self.check()
219 pass
221 def check(self):
222 """Base function to check for input errors.
224 Raises:
225 ValueError: Raised if the user does not specify a required field.
228 if not (self._explicit or self._optional):
229 raise ValueError("Uninitialized Input value of type " + type(self).__name__)
231 def extend(self, name, xml):
232 """ Dynamically add elements to the 'extra' list.
234 Picks from one of the templates in the self.dynamic dictionary, then
235 parses.
237 Args:
238 name: The tag name of the dynamically stored tag.
239 xml: The xml_node object used to parse the data stored in the tags.
242 newfield = self.dynamic[name][0](**self.dynamic[name][1])
243 newfield.parse(xml)
244 self.extra.append((name,newfield))
246 def write(self, name="", indent="", text="\n"):
247 """Writes data in xml file format.
249 Writes the tag, attributes, data and closing tag appropriate to the
250 particular fields and attribs data. Writes in a recursive manner, so
251 that objects contained in the fields dictionary have their write function
252 called, so that their tags are written between the start and end tags
253 of this object, as is required for the xml format.
255 This also adds an indent to the lower levels of the xml heirarchy,
256 so that it is easy to see which tags contain other tags.
258 Args:
259 name: An optional string giving the tag name. Defaults to "".
260 indent: An optional string giving the string to be added to the start
261 of the line, so usually a number of tabs. Defaults to "".
262 text: Additional text to be output between the tags.
264 Returns:
265 A string giving all the data contained in the fields and attribs
266 dictionaries, in the appropriate xml format.
269 rstr = indent + "<" + name;
270 for a in self.attribs:
271 # only write out attributes that are not defaults
272 # have a very simple way to check whether they actually add something:
273 # we compare with the string that would be output if the argument was set
274 # to its default
275 defstr = self.__dict__[a]._defwrite.replace("%%NAME%%",a)
276 outstr = self.__dict__[a].write(name=a)
277 if outstr != defstr:
278 rstr += " " + outstr
279 rstr += ">"
280 rstr += text
281 for f in self.fields:
282 #only write out fields that are not defaults
284 defstr = self.__dict__[f]._defwrite.replace("%%NAME%%",f)
285 if defstr != self.__dict__[f].write(f): # here we must compute the write string twice not to be confused by indents.
286 rstr += self.__dict__[f].write(f, " " + indent)
288 for (f,v) in self.extra:
289 # also write out extended (dynamic) fields if present
290 rstr += v.write(f, " " + indent)
292 if text.find('\n') >= 0:
293 rstr += indent + "</" + name + ">\n"
294 else:
295 rstr += "</" + name + ">\n"
296 return rstr
298 def parse(self, xml=None, text=""):
299 """Parses an xml file.
301 Uses the xml_node class defined in io_xml to read all the information
302 contained within the root tags, and uses it to give values for the attribs
303 and fields data recursively. It does this by giving all the data between
304 the appropriate field tag to the appropriate field restart object as a
305 string, and the appropriate attribute data to the appropriate attribs
306 restart object as a string. These data are then parsed by these objects
307 until all the information is read, or an input error is found.
309 Args:
310 xml: An xml_node object containing all the data for the parent
311 tag.
312 text: The data held between the start and end tags.
314 Raises:
315 NameError: Raised if one of the tags in the xml input file is
316 incorrect.
317 ValueError: Raised if the user does not specify a required field.
320 # before starting, sets everything to its default -- if a default is set!
321 for a in self.attribs:
322 self.__dict__[a].set_default()
323 for f in self.fields:
324 self.__dict__[f].set_default()
326 self.extra = []
327 self._explicit = True
328 if xml is None:
329 self._text = text
330 else:
331 for a, v in xml.attribs.iteritems():
332 if a in self.attribs:
333 self.__dict__[a].parse(text=v)
334 elif a == "_text":
335 pass
336 else:
337 raise NameError("Attribute name '" + a + "' is not a recognized property of '" + xml.name + "' objects")
339 for (f, v) in xml.fields: #reads all field and dynamic data.
340 if f in self.fields:
341 self.__dict__[f].parse(xml=v)
342 elif f == "_text":
343 self._text = v
344 elif f in self.dynamic:
345 self.extend(f, v)
346 else:
347 raise NameError("Tag name '" + f + "' is not a recognized property of '" + xml.name + "' objects")
349 #checks for missing arguments.
350 for a in self.attribs:
351 va = self.__dict__[a]
352 if not (va._explicit or va._optional):
353 raise ValueError("Attribute name '" + a + "' is mandatory and was not found in the input for the property " + xml.name)
354 for f in self.fields:
355 vf = self.__dict__[f]
356 if not (vf._explicit or vf._optional):
357 raise ValueError("Field name '" + f + "' is mandatory and was not found in the input for the property " + xml.name)
359 def detail_str(self):
360 """Prints out the supplementary information about a particular input class.
362 Used to print out the dimensions, default value, possible options and data
363 type of an input value to the LaTeX helf file.
366 xstr = ""
367 if hasattr(self, '_dimension') and self._dimension != "undefined": #gives dimension
368 xstr += "dimension: " + self._dimension + "; "
370 if self._default != None and issubclass(self.__class__, InputAttribute):
371 #We only print out the default if it has a well defined value.
372 #For classes such as InputCell, self._default is not the value,
373 #instead it is an object that is stored to give the default value in
374 #self.value. For this reason we print out self.value at this stage,
375 #and not self._default
376 xstr += "default: " + self.pprint(self.value) + "; "
378 if issubclass(self.__class__, InputAttribute):
379 #if possible, prints out the type of data that is being used
380 xstr += "data type: " + self.type_print(self.type) + "; "
382 if hasattr(self, "_valid"):
383 if self._valid is not None:
384 xstr += "options: " #prints out valid options, if
385 for option in self._valid: #required.
386 xstr += "`" + str(option) + "', "
387 xstr = xstr.rstrip(", ")
388 xstr += "; "
389 return xstr
391 def help_latex(self, name="", level=0, stop_level=None, standalone=True):
392 """Function to generate a LaTeX formatted help file.
394 Args:
395 name: Name of the tag that has to be written out.
396 level: Current level of the hierarchy being considered.
397 stop_level: The depth to which information will be given. If not given,
398 will give all information.
399 standalone: A boolean giving whether the latex file produced will be a
400 stand-alone document, or will be intended as a section of a larger
401 document with cross-references between the different sections.
403 Returns:
404 A LaTeX formatted string.
407 #stops when we've printed out the prerequisite number of levels
408 if (not stop_level is None and level > stop_level):
409 return ""
411 rstr = ""
412 if level == 0:
413 if standalone:
414 #assumes that it is a stand-alone document, so must have
415 #document options.
416 rstr += r"\documentclass[12pt,fleqn]{report}"
417 rstr += r"""
418 \usepackage{etoolbox}
419 \usepackage{suffix}
421 \newcommand{\ipiitem}[3]{%
422 \setul{1pt}{.4pt}\ifblank{#1}{}{\ifstrequal{#1}{\underline{\smash{}}}{}{
423 {\noindent\textbf{#1}:\rule{0.0pt}{1.05\baselineskip}\quad}}}% uses a strut to add a bit of vertical space
424 {#2}\parskip=0pt\par
425 \ifblank{#3}{}%
426 { {\hfill\raggedleft\textit{\small #3}\par} }
429 \makeatletter
430 \newenvironment{ipifield}[4]{%
431 \ifblank{#1}{}{\vspace{0.5em}}
432 \noindent\parskip=0pt\begin{tabular}[t]{|p{1.0\linewidth}}
433 %cell without border
434 \multicolumn{1}{@{}p{1.0\linewidth}}{
435 \ipiitem{\underline{\smash{#1}}}{#2}{}
436 \ifblank{#4}{ %
437 \ifblank{#3}{}{{\hfill\raggedleft\textit{\small #3}}\par}}{} } \vspace{-1em}\\ %
438 % cell with border
439 \ifblank{#4}{} %
440 { \ifblank{#3}{}{\vspace{-1em}{\hfill\raggedleft\textit{\small #3}}\par} %
441 {#4}\vspace{-1em}\\\hline } % negative vspace to undo the line break
442 \end{tabular}
443 \parskip=0pt\list{}{\listparindent 1.5em%
444 \leftmargin \listparindent
445 \rightmargin 0pt
446 \parsep 0pt
447 \itemsep 0pt
448 \topsep 0pt
450 \item\relax
452 {\endlist}
453 \makeatother
455 rstr += "\n\\begin{document}\n"
456 if self._label != "" and not standalone:
457 #assumes that it is part of a cross-referenced document, so only
458 #starts a new section.
459 rstr += "\\section{" + self._label + "}\n"
460 rstr += "\\label{" + self._label + "}\n"
462 rstr += "\\begin{ipifield}{}%\n"
463 else:
464 if self._label != "" and not standalone:
465 rstr += "\\begin{ipifield}{\hyperref["+self._label+"]{"+name+"}}%\n"
466 else:
467 rstr += "\\begin{ipifield}{"+name+"}%\n"
469 rstr += "{"+self._help+"}%\n"
471 rstr += "{"+self.detail_str()+"}%\n"
473 rstr += "{"
474 # Prints out the attributes
475 if len(self.attribs) != 0:
476 #don't print out units if not necessary
477 if len(self.attribs) == 1 and (("units" in self.attribs) and self._dimension == "undefined"):
478 pass
479 else:
480 for a in self.attribs:
481 #don't print out units if not necessary
482 if not (a == "units" and self._dimension == "undefined"):
483 rstr += "\\ipiitem{" + a + "}%\n{" + self.__dict__[a]._help + "}%\n{"+self.__dict__[a].detail_str()+"}%\n" #!!MUST ADD OTHER STUFF
484 rstr+="}\n"
486 #As above, for the fields. Only prints out if we have not reached the
487 #user-specified limit.
488 if len(self.fields) != 0 and level != stop_level:
489 for f in self.fields:
490 rstr += self.__dict__[f].help_latex(name=f, level=level+1, stop_level=stop_level, standalone=standalone)
492 if len(self.dynamic) != 0 and level != stop_level:
493 for f, v in self.dynamic.iteritems():
494 dummy_obj = v[0](**v[1])
495 rstr += dummy_obj.help_latex(name=f, level=level+1, stop_level=stop_level, standalone=standalone)
497 rstr += "\\end{ipifield}\n"
498 if level == 0 and standalone:
499 #ends the created document if it is not part of a larger document
500 rstr += "\\end{document}"
502 #Some escape characters are necessary for the proper latex formatting
503 rstr = rstr.replace('_', '\\_')
504 rstr = rstr.replace('\\\\_', '\\_')
505 rstr = rstr.replace('...', '\\ldots ')
506 rstr = rstr.replace('<', '$<$')
507 rstr = rstr.replace('>', '$>$')
509 return rstr
511 def pprint(self, default, indent="", latex = True):
512 """Function to convert arrays and other objects to human-readable strings.
514 Args:
515 default: The object that needs to be converted to a string.
516 indent: The indent at the beginning of a line.
517 latex: A boolean giving whether the string will be latex-format.
519 Returns:
520 A formatted string.
523 if type(default) is np.ndarray:
524 if default.shape == (0,):
525 return " [ ] " #proper treatment of empty arrays.
526 else:
527 #indents new lines for multi-D arrays properly
528 rstr = "\n" + indent + " "
529 rstr += str(default).replace("\n", "\n" + indent + " ")
530 if not latex:
531 rstr += "\n" + indent + " "
533 return rstr
534 elif type(default) == str:
535 if latex:
536 return "`" + default + "'" #indicates that it is a string
537 else:
538 return " " + default + " "
539 elif default == []:
540 return " [ ] "
541 elif default == {}:
542 if latex:
543 return " \\{ \\} " #again, escape characters needed for latex
544 else: #formatting
545 return " { } "
546 else:
547 #in most cases standard formatting will do
548 return " " + str(default) + " "
550 def type_print(self, dtype):
551 """Function to convert a data types to human-readable strings.
553 Args:
554 dtype: A data type.
557 if dtype == bool:
558 return "boolean"
559 elif dtype == float or dtype == np.float64:
560 return "float"
561 elif dtype == int or dtype == np.uint64 or dtype == np.int64:
562 return "integer"
563 elif dtype == dict:
564 return "dictionary"
565 elif dtype == str:
566 return "string"
567 elif dtype == tuple:
568 return "tuple"
569 else:
570 raise TypeError("Unrecognized data type " + str(dtype))
572 def help_xml(self, name="", indent="", level=0, stop_level=None):
573 """Function to generate an xml formatted help file.
575 Args:
576 name: A string giving the name of the root node.
577 indent: The indent at the beginning of a line.
578 level: Current level of the hierarchy being considered.
579 stop_level: The depth to which information will be given. If not given,
580 all information will be given
582 Returns:
583 An xml formatted string.
586 #stops when we've printed out the prerequisite number of levels
587 if (not stop_level is None and level > stop_level):
588 return ""
590 #these are booleans which tell us whether there are any attributes
591 #and fields to print out
592 show_attribs = (len(self.attribs) != 0)
593 show_fields = (not (len(self.fields) == 0 and len(self.dynamic) == 0)) and level != stop_level
595 rstr = ""
596 rstr = indent + "<" + name; #prints tag name
597 for a in self.attribs:
598 if not (a == "units" and self._dimension == "undefined"):
599 #don't print out units if not necessary
600 rstr += " " + a + "=''" #prints attribute names
601 rstr += ">\n"
603 #prints help string
604 rstr += indent + " <help> " + self._help + " </help>\n"
605 if show_attribs:
606 for a in self.attribs:
607 if not (a == "units" and self._dimension == "undefined"):
608 #information about tags is found in tags beginning with the name
609 #of the attribute
610 rstr += indent + " <" + a + "_help> " + self.__dict__[a]._help + " </" + a + "_help>\n"
612 #prints dimensionality of the object
613 if hasattr(self, '_dimension') and self._dimension != "undefined":
614 rstr += indent + " <dimension> " + self._dimension + " </dimension>\n"
616 if self._default != None and issubclass(self.__class__, InputAttribute):
617 #We only print out the default if it has a well defined value.
618 #For classes such as InputCell, self._default is not the value,
619 #instead it is an object that is stored, putting the default value in
620 #self.value. For this reason we print out self.value at this stage,
621 #and not self._default
622 rstr += indent + " <default>" + self.pprint(self.value, indent=indent, latex=False) + "</default>\n"
623 if show_attribs:
624 for a in self.attribs:
625 if not (a == "units" and self._dimension == "undefined"):
626 if self.__dict__[a]._default is not None:
627 rstr += indent + " <" + a + "_default>" + self.pprint(self.__dict__[a]._default, indent=indent, latex=False) + "</" + a + "_default>\n"
629 #prints out valid options, if required.
630 if hasattr(self, "_valid"):
631 if self._valid is not None:
632 rstr += indent + " <options> " + str(self._valid) + " </options>\n"
633 if show_attribs:
634 for a in self.attribs:
635 if not (a == "units" and self._dimension == "undefined"):
636 if hasattr(self.__dict__[a], "_valid"):
637 if self.__dict__[a]._valid is not None:
638 rstr += indent + " <" + a + "_options> " + str(self.__dict__[a]._valid) + " </" + a + "_options>\n"
640 #if possible, prints out the type of data that is being used
641 if issubclass(self.__class__, InputAttribute):
642 rstr += indent + " <dtype> " + self.type_print(self.type) + " </dtype>\n"
643 if show_attribs:
644 for a in self.attribs:
645 if not (a == "units" and self._dimension == "undefined"):
646 rstr += indent + " <" + a + "_dtype> " + self.type_print(self.__dict__[a].type) + " </" + a + "_dtype>\n"
648 #repeats the above instructions for any fields or dynamic tags.
649 #these will only be printed if their level in the hierarchy is not above
650 #the user specified limit.
651 if show_fields:
652 for f in self.fields:
653 rstr += self.__dict__[f].help_xml(f, " " + indent, level+1, stop_level)
654 for f, v in self.dynamic.iteritems():
655 #we must create the object manually, as dynamic objects are
656 #not automatically added to the input object's dictionary
657 dummy_obj = v[0](**v[1])
658 rstr += dummy_obj.help_xml(f, " " + indent, level+1, stop_level)
660 rstr += indent + "</" + name + ">\n"
661 return rstr
664 class InputAttribute(Input):
665 """Class for handling attribute data.
667 Has the methods for dealing with attribute data of the form:
668 <tag_name attrib='data'> ..., where data is just a value. Takes the data and
669 converts it to the required data_type, so that it can be used in the
670 simulation.
672 Attributes:
673 type: Data type of the data.
674 value: Value of data. Also specifies data type if type is None.
675 _valid: An optional list of valid options.
678 def __init__(self, help=None, default=None, dtype=None, options=None):
679 """Initialises InputAttribute.
681 Args:
682 help: A help string.
683 default: A default value.
684 dtype: An optional data type. Defaults to None.
685 options: An optional list of valid options.
688 if not dtype is None:
689 self.type = dtype
690 else:
691 raise TypeError("You must provide dtype")
693 super(InputAttribute,self).__init__(help, default)
695 if options is not None:
696 self._valid = options
697 if not default is None and not self._default in self._valid:
698 #This makes sure that the programmer has set the default value
699 #so that it is a valid value.
700 raise ValueError("Default value '" + str(self._default) + "' not in option list " + str(self._valid)+ "\n" + self._help)
701 else:
702 self._valid = None
704 def parse(self, text=""):
705 """Reads the data for a single attribute value from an xml file.
707 Args:
708 text: The data held between the start and end tags.
711 super(InputAttribute, self).parse(text=text)
713 self.value = read_type(self.type, self._text)
715 def store(self, value):
716 """Stores the input data.
718 Args:
719 value: The raw data to be stored.
721 super(InputAttribute,self).store(value)
722 self.value = value
724 def fetch(self):
725 """Returns the stored data."""
727 super(InputAttribute,self).fetch()
728 return self.value
730 def check(self):
731 """Function to check for input errors.
733 Raises:
734 ValueError: Raised if the value chosen is not one of the valid options.
737 super(InputAttribute,self).check()
738 if not (self._valid is None or self.value in self._valid):
739 #This checks that the user has set the value to a valid value.
740 raise ValueError(str(self.value) + " is not a valid option (" + str(self._valid) + ")")
742 def write(self, name=""):
743 """Writes data in xml file format.
745 Writes the attribute data in the appropriate format.
747 Args:
748 name: An optional string giving the attribute name. Defaults to "".
750 Returns:
751 A string giving the stored value in the appropriate format.
754 return name + "='" + write_type(self.type, self.value) + "'"
757 class InputValue(InputAttribute):
758 """Scalar class for input handling.
760 Has the methods for dealing with simple data tags of the form:
761 <tag_name> data </tag_name>, where data is just a value. Takes the data and
762 converts it to the required data_type, so that it can be used in the
763 simulation.
765 Attributes:
766 units: The units that the input data is given in.
767 _dimension: The dimensionality of the data.
770 default_dimension = "undefined"
771 default_units = ""
773 attribs= { "units" : ( InputAttribute, { "dtype" : str, "help" : "The units the input data is given in.", "default" : default_units } ) }
775 def __init__(self, help=None, default=None, dtype=None, options=None, dimension=None):
776 """Initialises InputValue.
778 Args:
779 help: A help string.
780 dimension: The dimensionality of the value.
781 default: A default value.
782 dtype: An optional data type. Defaults to None.
783 options: An optional list of valid options.
786 # a note on units handling:
787 # 1) units are only processed at parse/fetch time:
788 # internally EVERYTHING is in internal units
789 # 2) if one adds an explicit "units" attribute to a derived class,
790 # the internal units handling will be just ignored
791 if dimension is None:
792 self._dimension = self.default_dimension
793 else:
794 self._dimension = dimension
796 super(InputValue,self).__init__(help, default, dtype, options)
798 def store(self, value, units=""):
799 """Converts the data to the appropriate data type and units and stores it.
801 Args:
802 value: The raw data to be stored.
803 units: Optional string giving the units that the data should be stored
807 super(InputValue,self).store(value)
809 if units != "":
810 self.units.store(units) #User can define in the code the units to be
811 #printed
813 self.value = value
814 if self._dimension != "undefined":
815 self.value *= unit_to_user(self._dimension, units, 1.0)
817 def fetch(self):
818 """Returns the stored data in the user defined units."""
820 super(InputValue,self).fetch()
822 rval = self.value
823 if self._dimension != "undefined":
824 rval *= unit_to_internal(self._dimension, self.units.fetch(), 1.0)
825 return rval
827 def write(self, name="", indent=""):
828 """Writes data in xml file format.
830 Writes the data in the appropriate format between appropriate tags.
832 Args:
833 name: An optional string giving the tag name. Defaults to "".
834 indent: An optional string giving the string to be added to the start
835 of the line, so usually a number of tabs. Defaults to "".
837 Returns:
838 A string giving the stored value in the appropriate xml format.
841 return Input.write(self, name=name, indent=indent, text=write_type(self.type, self.value))
843 def parse(self, xml=None, text=""):
844 """Reads the data for a single value from an xml file.
846 Args:
847 xml: An xml_node object containing the all the data for the parent
848 tag.
849 text: The data held between the start and end tags.
852 Input.parse(self, xml=xml, text=text)
853 self.value = read_type(self.type, self._text)
856 ELPERLINE = 5
857 class InputArray(InputValue):
858 """Array class for input handling.
860 Has the methods for dealing with simple data tags of the form:
861 <tag_name shape="(shape)"> data </tag_name>, where data is an array
862 of the form [data[0], data[1], ... , data[length]].
864 Takes the data and converts it to the required data type,
865 so that it can be used in the simulation. Also holds the shape of the array,
866 so that we can use a simple 1D list of data to specify a multi-dimensional
867 array.
869 Attributes:
870 shape: The shape of the array.
873 attribs = copy(InputValue.attribs)
874 attribs["shape"] = (InputAttribute, {"dtype": tuple, "help": "The shape of the array.", "default": (0,)})
876 def __init__(self, help=None, default=None, dtype=None, dimension=None):
877 """Initialises InputArray.
879 Args:
880 help: A help string.
881 dimension: The dimensionality of the value.
882 default: A default value.
883 dtype: An optional data type. Defaults to None.
886 super(InputArray,self).__init__(help, default, dtype, dimension=dimension)
888 def store(self, value, units=""):
889 """Converts the data to the appropriate data type, shape and units and
890 stores it.
892 Args:
893 value: The raw data to be stored.
894 units: Optional string giving the units that the data should be stored
898 super(InputArray,self).store(value=np.array(value, dtype=self.type).flatten().copy(), units=units)
899 self.shape.store(value.shape)
901 #if the shape is not specified, assume the array is linear.
902 if self.shape.fetch() == (0,):
903 self.shape.store((len(self.value),))
905 def fetch(self):
906 """Returns the stored data in the user defined units."""
908 value = super(InputArray,self).fetch()
910 #if the shape is not specified, assume the array is linear.
911 if self.shape.fetch() == (0,):
912 value = np.resize(self.value,0).copy()
913 else:
914 value = self.value.reshape(self.shape.fetch()).copy()
916 return value
918 def write(self, name="", indent=""):
919 """Writes data in xml file format.
921 Writes the data in the appropriate format between appropriate tags. Note
922 that only ELPERLINE values are printed on each line if there are more
923 than this in the array. If the values are floats, or another data type
924 with a fixed width of data output, then they are aligned in columns.
926 Args:
927 name: An optional string giving the tag name. Defaults to "".
928 indent: An optional string giving the string to be added to the start
929 of the line, so usually a number of tabs. Defaults to "".
931 Returns:
932 A string giving the stored value in the appropriate xml format.
935 rstr = ""
936 if (len(self.value) > ELPERLINE):
937 rstr += "\n" + indent + " [ "
938 else:
939 rstr += " [ " #inlines the array if it is small enough
941 for i, v in enumerate(self.value):
942 if (len(self.value) > ELPERLINE and i > 0 and i%ELPERLINE == 0):
943 rstr += "\n" + indent + " "
944 rstr += write_type(self.type, v) + ", "
946 rstr = rstr.rstrip(", ") #get rid of trailing commas
947 if (len(self.value) > ELPERLINE):
948 rstr += " ]\n"
949 else:
950 rstr += " ] "
952 return Input.write(self, name=name, indent=indent, text=rstr)
954 def parse(self, xml=None, text=""):
955 """Reads the data for an array from an xml file.
957 Args:
958 xml: An xml_node object containing the all the data for the parent
959 tag.
960 text: The data held between the start and end tags.
963 Input.parse(self, xml=xml, text=text)
964 self.value = read_array(self.type, self._text)
966 #if the shape is not specified, assume the array is linear.
967 if self.shape.fetch() == (0,):
968 self.shape.store((len(self.value),))