1"""Contains the classes that are used to write to and read from restart files.
2
3Copyright (C) 2013, Joshua More and Michele Ceriotti
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program. If not, see <http.//www.gnu.org/licenses/>.
17
18
19The classes defined in this module define the base functions which parse the
20data in the restart files. Each restart object defined has a fields and an
21attributes dictionary, which are filled with the tags and attributes that
22are allowed to be present, along with their default values and data type.
23
24These are then filled with the data from the xml file when the program
25is initialized, and are filled by the values calculated in the program which
26are then output to the checkpoint file when a restart file is required.
27
28Also deals with checking for user input errors, of the form of misspelt tags,
29bad data types, and failure to input required fields.
30
31Classes:
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"""
38
39__all__ = ['Input', 'InputValue', 'InputAttribute', 'InputArray', 'input_default']
40
41import numpy as np
42from copy import copy
43from ipi.utils.io.io_xml import *
44from ipi.utils.units import unit_to_internal, unit_to_user
45
46
47
48class input_default(object):
49   """Contains information required to dynamically create objects
50
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.
58
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   """
66
67   def __init__(self, factory, args = None, kwargs = None):
68      """Initializes input_default.
69
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 initialize
73            the default value.
74         kwargs: A dictionary giving the key word arguments to be used
75            to initialize the default value.
76      """
77
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
88
89
90class Input(object):
91   """Base class for input handling.
92
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.
96
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.
139   """
140
141   fields = {}
142   attribs = {}
143   dynamic = {}
144
145   default_help = "Generic input value"
146   default_label = "" #used as a way to reference a particular class using
147                      #hyperlinks
148
149   def __init__(self, help=None, default=None):
150      """Initializes Input.
151
152      Automatically adds all the fields and attribs names to the input object's
153      dictionary, then initializes all the appropriate input objects
154      as the corresponding values.
155
156      Args:
157         help: A help string.
158         default: A default value.
159      """
160
161      # list of extended (dynamic) fields
162      self.extra = []
163
164      if help is None:
165         self._help = self.default_help
166      else:
167         self._help = help
168
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
174
175      self._optional = not (self._default is None)
176
177      self._label = self.default_label
178
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])
185
186      for a, v in self.attribs.iteritems():
187         self.__dict__[a] = v[0](**v[1])
188
189      self.set_default()
190
191      self._text = ""
192
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%%")
197
198   def set_default(self):
199      """Sets the default value of the object."""
200
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
206
207      self._explicit = False #Since the value was not set by the user
208
209   def store(self, value=None):
210      """Dummy function for storing data."""
211
212      self._explicit = True
213      pass
214
215   def fetch(self):
216      """Dummy function to retrieve data."""
217
218      self.check()
219      pass
220
221   def check(self):
222      """Base function to check for input errors.
223
224      Raises:
225         ValueError: Raised if the user does not specify a required field.
226      """
227
228      if not (self._explicit or self._optional):
229         raise ValueError("Uninitialized Input value of type " + type(self).__name__)
230
231   def extend(self, name,  xml):
232      """ Dynamically add elements to the 'extra' list.
233
234      Picks from one of the templates in the self.dynamic dictionary, then
235      parses.
236
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.
240      """
241
242      newfield = self.dynamic[name][0](**self.dynamic[name][1])
243      newfield.parse(xml)
244      self.extra.append((name,newfield))
245
246   def write(self, name="", indent="", text="\n"):
247      """Writes data in xml file format.
248
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.
254
255      This also adds an indent to the lower levels of the xml hierarchy,
256      so that it is easy to see which tags contain other tags.
257
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.
263
264      Returns:
265         A string giving all the data contained in the fields and attribs
266         dictionaries, in the appropriate xml format.
267      """
268
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
283
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)
287
288      for (f,v) in self.extra:
289         # also write out extended (dynamic) fields if present
290         rstr += v.write(f, "   " + indent)
291
292      if text.find('\n') >= 0:
293         rstr += indent + "</" + name + ">\n"
294      else:
295         rstr += "</" + name + ">\n"
296      return rstr
297
298   def parse(self, xml=None, text=""):
299      """Parses an xml file.
300
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.
308
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.
313
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.
318      """
319
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()
325
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")
338
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")
348
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)
358
359   def detail_str(self):
360      """Prints out the supplementary information about a particular input class.
361
362      Used to print out the dimensions, default value, possible options and data
363      type of an input value to the LaTeX helf file.
364      """
365
366      xstr = ""
367      if hasattr(self, '_dimension') and self._dimension != "undefined": #gives dimension
368         xstr += "dimension: " + self._dimension + "; "
369
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) + "; "
377
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) + "; "
381
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
390
391   def help_latex(self, name="", level=0, stop_level=None, standalone=True):
392      """Function to generate a LaTeX formatted help file.
393
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.
402
403      Returns:
404         A LaTeX formatted string.
405      """
406
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 ""
410
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}
420
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} }
427}
428
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
449                        }%
450                \item\relax
451                }
452               {\endlist}
453\makeatother
454"""
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"
461
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"
468
469      rstr += "{"+self._help+"}%\n"
470
471      rstr += "{"+self.detail_str()+"}%\n"
472
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"
485
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)
491
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)
496
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}"
501
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('>', '$>$')
508
509      return rstr
510
511   def pprint(self, default, indent="", latex = True):
512      """Function to convert arrays and other objects to human-readable strings.
513
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.
518
519      Returns:
520         A formatted string.
521      """
522
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 + "   "
532
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) + " "
549
550   def type_print(self, dtype):
551      """Function to convert a data types to human-readable strings.
552
553      Args:
554         dtype: A data type.
555      """
556
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))
571
572   def help_xml(self, name="", indent="", level=0, stop_level=None):
573      """Function to generate an xml formatted help file.
574
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
581
582      Returns:
583         An xml formatted string.
584      """
585
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 ""
589
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
594
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"
602
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"
611
612      #prints dimensionality of the object
613      if hasattr(self, '_dimension') and self._dimension != "undefined":
614         rstr += indent + "   <dimension> " + self._dimension + " </dimension>\n"
615
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"
628
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"
639
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"
647
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)
659
660      rstr += indent + "</" + name + ">\n"
661      return rstr
662
663
664class InputAttribute(Input):
665   """Class for handling attribute data.
666
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.
671
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.
676   """
677
678   def __init__(self,  help=None, default=None, dtype=None, options=None):
679      """Initializes InputAttribute.
680
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.
686      """
687
688      if not dtype is None:
689         self.type = dtype
690      else:
691         raise TypeError("You must provide dtype")
692
693      super(InputAttribute,self).__init__(help, default)
694
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
703
704   def parse(self, text=""):
705      """Reads the data for a single attribute value from an xml file.
706
707      Args:
708         text: The data held between the start and end tags.
709      """
710
711      super(InputAttribute, self).parse(text=text)
712
713      self.value = read_type(self.type, self._text)
714
715   def store(self, value):
716      """Stores the input data.
717
718      Args:
719         value: The raw data to be stored.
720      """
721      super(InputAttribute,self).store(value)
722      self.value = value
723
724   def fetch(self):
725      """Returns the stored data."""
726
727      super(InputAttribute,self).fetch()
728      return self.value
729
730   def check(self):
731      """Function to check for input errors.
732
733      Raises:
734         ValueError: Raised if the value chosen is not one of the valid options.
735      """
736
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) + ")")
741
742   def write(self, name=""):
743      """Writes data in xml file format.
744
745      Writes the attribute data in the appropriate format.
746
747      Args:
748         name: An optional string giving the attribute name. Defaults to "".
749
750      Returns:
751         A string giving the stored value in the appropriate format.
752      """
753
754      return name + "='" + write_type(self.type, self.value) + "'"
755
756
757class InputValue(InputAttribute):
758   """Scalar class for input handling.
759
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.
764
765   Attributes:
766      units: The units that the input data is given in.
767      _dimension: The dimensionality of the data.
768   """
769
770   default_dimension = "undefined"
771   default_units = ""
772
773   attribs= { "units" : ( InputAttribute, { "dtype" : str, "help" : "The units the input data is given in.", "default" : default_units } ) }
774
775   def __init__(self,  help=None, default=None, dtype=None, options=None, dimension=None):
776      """Initializes InputValue.
777
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.
784      """
785
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
795
796      super(InputValue,self).__init__(help, default, dtype, options)
797
798   def store(self, value, units=""):
799      """Converts the data to the appropriate data type and units and stores it.
800
801      Args:
802         value: The raw data to be stored.
803         units: Optional string giving the units that the data should be stored
804            in.
805      """
806
807      super(InputValue,self).store(value)
808
809      if units != "":
810         self.units.store(units) #User can define in the code the units to be
811                                 #printed
812
813      self.value = value
814      if self._dimension != "undefined":
815         self.value *= unit_to_user(self._dimension, units, 1.0)
816
817   def fetch(self):
818      """Returns the stored data in the user defined units."""
819
820      super(InputValue,self).fetch()
821
822      rval = self.value
823      if self._dimension != "undefined":
824         rval *= unit_to_internal(self._dimension, self.units.fetch(), 1.0)
825      return rval
826
827   def write(self, name="", indent=""):
828      """Writes data in xml file format.
829
830      Writes the data in the appropriate format between appropriate tags.
831
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 "".
836
837      Returns:
838         A string giving the stored value in the appropriate xml format.
839      """
840
841      return Input.write(self, name=name, indent=indent, text=write_type(self.type, self.value))
842
843   def parse(self, xml=None, text=""):
844      """Reads the data for a single value from an xml file.
845
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.
850      """
851
852      Input.parse(self, xml=xml, text=text)
853      self.value = read_type(self.type, self._text)
854
855
856ELPERLINE = 5
857class InputArray(InputValue):
858   """Array class for input handling.
859
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]].
863
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.
868
869   Attributes:
870      shape: The shape of the array.
871   """
872
873   attribs = copy(InputValue.attribs)
874   attribs["shape"] = (InputAttribute,  {"dtype": tuple,  "help": "The shape of the array.", "default": (0,)})
875
876   def __init__(self,  help=None, default=None, dtype=None, dimension=None):
877      """Initializes InputArray.
878
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.
884      """
885
886      super(InputArray,self).__init__(help, default, dtype, dimension=dimension)
887
888   def store(self, value, units=""):
889      """Converts the data to the appropriate data type, shape and units and
890      stores it.
891
892      Args:
893         value: The raw data to be stored.
894         units: Optional string giving the units that the data should be stored
895            in.
896      """
897
898      super(InputArray,self).store(value=np.array(value, dtype=self.type).flatten().copy(), units=units)
899      self.shape.store(value.shape)
900
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),))
904
905   def fetch(self):
906      """Returns the stored data in the user defined units."""
907
908      value = super(InputArray,self).fetch()
909
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()
915
916      return value
917
918   def write(self, name="", indent=""):
919      """Writes data in xml file format.
920
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.
925
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 "".
930
931      Returns:
932         A string giving the stored value in the appropriate xml format.
933      """
934
935      rstr = ""
936      if (len(self.value) > ELPERLINE):
937         rstr += "\n" + indent + " [ "
938      else:
939         rstr += " [ " #inlines the array if it is small enough
940
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) + ", "
945
946      rstr = rstr.rstrip(", ") #get rid of trailing commas
947      if (len(self.value) > ELPERLINE):
948         rstr += " ]\n"
949      else:
950         rstr += " ] "
951
952      return Input.write(self, name=name, indent=indent, text=rstr)
953
954   def parse(self, xml=None, text=""):
955      """Reads the data for an array from an xml file.
956
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.
961      """
962
963      Input.parse(self, xml=xml, text=text)
964      self.value = read_array(self.type, self._text)
965
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),))
969