Skip to content

input data

Paul Talbot edited this page Jun 11, 2024 · 6 revisions

InputData is a library for describing data and checking and reading it.

Generally it is used by creating subclasses of InputData.ParameterInput

Example:

class DistributionClass(InputData.ParameterInput):
   """
     Description of class
   """

After they are created they need to be initialized by calling the class method createClass.

Example:

DistributionClass.createClass("DistributionClass",
                              contentType=InputTypes.StringType)

Alternatively, InputData.parameterInputFactory can be used to both subclass ParameterInput and to call createClass.

Example:

DistributionClass = InputData.parameterInputFactory("DistributionClass",
                                            contentType=InputTypes.StringType)

After that the subclasses of ParameterInput can have subnodes and parameters added.

Parameters are added with the addParam that takes the name and the type.

Example:

DistributionClass.addParam("distribution_name", InputTypes.StringType)

The content type and the parameter type are subclasses of InputTypes.InputType.

There are several predefined types including InputTypes.StringType and InputTypes.IntegerType and InputTypes.FloatType.

There is an enum type maker that can be used to create types that only allow from a list of strings.

Example:

YesOrNoType = InputTypes.makeEnumType("YesOrNo","YesOrNoType",["yes","no"])

To create a subnode, first create it, and then add it to the parent node with addSub

Example:

metricInput = InputData.parameterInputFactory("Metric", contentType=InputTypes.StringType)
metricInput.addParam("class", InputTypes.StringType, True)
metricInput.addParam("type", InputTypes.StringType, True)
inputSpecification.addSub(metricInput)

Note that addSub can take a quantity:

inputSpecification.addSub(metricInput, InputData.Quantity.one)

where the possibilities are Quantity.zero_to_one, Quantity.zero_to_infinity (default), Quantity.one, Quantity.one_to_infinity.

Where to put code:

The standard method for defining these is to add a class method called getInputSpecification

Example:

class Something(SomeBase):
  @classmethod
  def getInputSpecification(cls):
    """
      Method to get a reference to a class that specifies the input data for
      class cls.
      @ In, cls, the class for which we are retrieving the specification
      @ Out, inputSpecification, InputData.ParameterInput, class to use for
        specifying input of cls.
    """
    inputSpecification = InputData.parameterInputFactory(cls.__name__, ordered=False, baseNode=InputData.RavenBase)
    inputSpecification.addParam("name", InputTypes.StringType, True)
    return inputSpecification

For classes where they should inherit from the base class, they can get the parent input specification, and use that as a start.

Example:

  @classmethod
  def getInputSpecification(cls):
    inputSpecification = super(ComparisonStatistics, cls).getInputSpecification()
    inputSpecification.addParam("something", InputTypes.IntegerType)
    return inputSpecification

Reading data from XML files

First, create a class instance. Then use the parseNode function on the XML node. After that, the children can be checked with the subparts variable, or parameters can be checked with the parameterValues variable. The main data will be stored in the value variable. There are also functions getName() that gets the subnode's name and findFirst that finds the first subnode with the given name.

Example XML:

<NDCartesianSpline>
   <dataFilename type="PDF">2DgaussianCartesianPDF.txt</dataFilename>
</NDCartesianSpline>

Example Reading:

paramInput = NDCartesianSpline.getInputSpecification()()
paramInput.parseNode(xmlNode)
dataFilename = paramInput.findFirst('dataFilename')
if dataFilename != None:
  self.dataFilename = os.path.join(self.workingDir,dataFilename.value)
  functionType = dataFilename.parameterValues['type']

Example Reading subnodes:

for child in paramInput.subparts:
  if child.getName() == 'Metric':
    metricType = child.parameterValues['type']
    metricText = child.value

LaTeX descr

For some of the classes, the descr parameter is used to automatically generate LaTeX documentation. See ravenframework/Optimizers/acquisitionFunctions/ProbabilityOfImprovement.py for example. Note that doc/user_manual/generate_docs.sh needs to be manually run to do this. Also note that _ are automatically escaped, but if the _ is followed by a { it is not escaped (example l_{2} can be used to get a subscript).

    specs.addSub(InputData.parameterInputFactory('epsilon', contentType=InputTypes.FloatType,
                                                 descr=r"""$\epsilon_{0} is the minimum value""")

Custom Arbitrary Input

It is possible to allow a ParameterInput instance to collect undefined inputs; that is, input whose contents and structure is not known before run time. To do this, set the strictMode parameter when defining the spec to False.

When a ParameterInput node has strictMode set to False, any hierarchal input that is not defined specifically in the spec will be saved as a list of input tree elements (specifically, raven.utils.TreeStructure.InputNode instances). Each of these may have an arbitrary number of sub-elements, attributes, text, and so on. When read in, these are stored on the ParameterInput in an attribute called additionalInput, which is a list of the InputNode instances.

By default, the spec does not take any action on the additionalInput entries. The developer can pass these instances to custom code, parse information from them, etc.

For example, see within the unit test for the InputSpec, located at raven/tests/framework/unit_tests/testInputData.py. In that example, node <B> has strict mode disabled, and so stores each of the subnodes of <B> as InputNode instances in the B.additionalInput list.