Skip to content

Commit

Permalink
Merge pull request #368 from PaulTalbot-INL/custfx_xml
Browse files Browse the repository at this point in the history
Function custom XML nodes
  • Loading branch information
GabrielSoto-INL authored Jun 18, 2024
2 parents 97bf72e + 2f5d9d6 commit 76b8a53
Show file tree
Hide file tree
Showing 13 changed files with 62 additions and 44 deletions.
44 changes: 11 additions & 33 deletions src/ValuedParamHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,20 @@ class ValuedParamHandler(MessageUser):
with a variety of sources (fixed values, parametric values, data histories, function
evaluations, etc).
"""
@classmethod
def get_input_specs(cls, name, descr=""):
"""
Template for parameters that can take a scalar, an ARMA history, or a function
@ In, name, string, name for spec (tag)
@ In, descr, string, base description for item (this will add to it)
@ Out, spec, InputData, value-based spec
"""
spec = InputData.parameterInputFactory(name,
descr=descr + r"""This value can be taken from any \emph{one} of the sources described below.""")
# VP sources
for vp_type in VPFactory.knownTypes():
spec.addSub(vp_type.get_input_specs())
# addons
spec.addSub(InputData.parameterInputFactory('multiplier', contentType=InputTypes.FloatType,
descr=r"""Multiplies any value obtained by this parameter by the given value. \default{1}"""))
# for when the result obtained needs to grow from year to year
# TODO
# growth = InputData.parameterInputFactory('growth', contentType=InputTypes.FloatType,
# descr=r"""if this node is given, the value will be adjusted from cycle to cycle by the provided amount.""")
# growth_mode = InputTypes.makeEnumType('growthType', 'growthType', ['linear', 'exponential'])
# growth.addParam('mode', param_type=growth_mode, required=True,
# descr=r"""determines whether the growth factor should be taken as linear or exponential (compounding).""")
# spec.addSub(growth)
return spec

# NOTE: the inputSpec is defined in the ValuedParams Factory module.
def __init__(self, name):
"""
Constructor.
@ In, name, str, name of this valued param
@ Out, None
"""
super().__init__()
self.name = name # member whom this ValuedParam provides values, e.g. Component.economics.alpha
self._vp = None # ValuedParam instance
self._multiplier = None # scalar multiplier for evaluation values
self._growth_val = None # used to grow the value year-by-year
self._growth_mode = None # mode for growth (e.g. exponenetial, linear)
self.name = name # member whom this ValuedParam provides values, e.g. Component.economics.alpha
self._vp = None # ValuedParam instance
self._multiplier = None # scalar multiplier for evaluation values
self._growth_val = None # used to grow the value year-by-year
self._growth_mode = None # mode for growth (e.g. exponenetial, linear)
self._custom_input = None # additional info from input to pass through

def __repr__(self):
"""
Expand Down Expand Up @@ -116,6 +92,8 @@ def read(self, comp_name: str, spec: InputData.ParameterInput, mode: str, alias_
# elif sub.getName() == 'growth':
# self._growth_val = sub.value
# self._growth_mode = sub.parameterValues['mode']
elif sub.getName() == 'AdditionalInfo':
self._custom_input = sub.additionalInput
if not found:
self.raiseAnError(IOError, f'Component "{comp_name}" node <{spec.getName()}> expected a ValuedParam ' +
f'to define its value source, but none was found! Options include: {knownVPs}')
Expand Down Expand Up @@ -203,14 +181,14 @@ def set_object(self, obj):
"""
self._vp.set_object(obj)

def evaluate(self, *args, util_factor=False, **kwargs):
def evaluate(self, *args, **kwargs):
"""
Evaluate the ValuedParam, wherever it gets its data from
@ In, args, list, positional arguments for ValuedParam
@ In, kwargs, dict, keyword arguements for ValuedParam
@ Out, evaluate, object, stuff from ValuedParam evaluation
"""
data, meta = self._vp.evaluate(*args, **kwargs)
data, meta = self._vp.evaluate(*args, custom_input=self._custom_input, **kwargs)
if self._multiplier is not None:
for key in data:
data[key] *= self._multiplier
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/Activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,13 @@ def crosscheck(self, interaction):
'was not found among this Component\'s input/output resources; options are:' +
f'{", ".join(str_avail)}')

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
Expand Down
13 changes: 12 additions & 1 deletion src/ValuedParams/Factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,25 @@ def make_input_specs(self, name, descr=None, allowed=None, kind='singular'):
for typ, klass in self._registeredTypes.items():
if typ in allowed:
spec.addSub(klass.get_input_specs())
# addons
# addons
spec.addSub(
InputData.parameterInputFactory(
'multiplier',
contentType=InputTypes.FloatType,
descr=r"""Multiplies any value obtained by this parameter by the given value. \default{1}"""
)
)
addl_spec = InputData.parameterInputFactory(
'AdditionalInfo',
descr=r"""Additional arbitrary input information. For custom-defined parameters, such as \xmlNode{Function},
the additional information will be passed as part of the `texttt{meta[``HERON''][``custom_input'']}
passed to the user-supplied custom evaluation definition.
In the case of the \xmlNode{Function}, this is passed to the method in the Python module indicated by
the user, and will be unique for each use of the \xmlNode{Function} in the HERON input. Note that
the custom input nodes are not checked in any way by the input parsing."""
)
addl_spec.setStrictMode(False)
spec.addSub(addl_spec)
return spec

factory = ValuedParamFactory('ValuedParam')
Expand Down
5 changes: 4 additions & 1 deletion src/ValuedParams/Function.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,20 @@ def read(self, comp_name, spec, mode, alias_dict=None):
self._method_name = spec.parameterValues['method']
return [self._method_name]

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, data, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
if aliases is None:
aliases = {}
if custom_input is not None:
inputs['HERON']['custom_input'] = custom_input
# TODO how to handle aliases for functions?
# the "request" is what we're asking for from the function, the first argument given.
# -> note it can be None if the function is not a transfer-type function
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/Parametric.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,13 @@ def set_value(self, value):
"""
self._parametric = value

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/ROM.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,13 @@ def make_sub_vp(self, name, comp, spec, mode):
self._inputs[name] = {'vp': vp, 'signals': [signal]}
return signal

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, run information from RAVEN, including meta and other run info
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, inputs, dict, possibly-modified dictionary of run information
"""
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/RandomVariable.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ def read(self, comp_name, spec, mode, alias_dict=None):
self._distribution = self.convert_spec_to_xml(sub_distribution)
return []

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/StaticHistory.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,13 @@ def read(self, comp_name, spec, mode, alias_dict=None):
self._var_name = spec.parameterValues['variable']
return [self._var_name]

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/SyntheticHistory.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ def read(self, comp_name, spec, mode, alias_dict=None):
self._var_name = spec.parameterValues['variable']
return [self._var_name]

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/ValuedParam.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,13 @@ def set_object(self, obj):
"""
self._target_obj = obj

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional custom inputs
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
Expand Down
3 changes: 2 additions & 1 deletion src/ValuedParams/Variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ def read(self, comp_name, spec, mode, alias_dict=None):
self._raven_var = spec.value
return [self._raven_var]

def evaluate(self, inputs, target_var=None, aliases=None):
def evaluate(self, inputs, target_var=None, aliases=None, custom_input=None):
"""
Evaluate this ValuedParam, wherever it gets its data from
@ In, inputs, dict, stuff from RAVEN, particularly including the keys 'meta' and 'raven_vars'
@ In, target_var, str, optional, requested outgoing variable name if not None
@ In, aliases, dict, optional, alternate variable names for searching in variables
@ In, custom_input, list, optional, additional input nodes from user input
@ Out, value, dict, dictionary of resulting evaluation as {vars: vals}
@ Out, meta, dict, dictionary of meta (possibly changed during evaluation)
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@
</driver>
<reference_price>
<Function method="flex_price">transfers</Function>
<AdditionalInfo>
<General>
<TheAnswer>42</TheAnswer>
<TheQuestion>unknown</TheQuestion>
</General>
<scalar>-2</scalar>
<loc>-0.5</loc>
</AdditionalInfo>
</reference_price>
</CashFlow>
</economics>
Expand Down
12 changes: 11 additions & 1 deletion tests/integration_tests/mechanics/storage_func/transfers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,18 @@ def flex_price(data, meta):
"""
sine = meta['HERON']['RAVEN_vars']['Signal']
t = meta['HERON']['time_index']
# check for the existince of the custom functions
for node in meta['HERON']['custom_input']:
if node.tag == 'General':
ans = node.find('TheAnswer').text
qus = node.find('TheQuestion').text
elif node.tag == 'scalar':
scalar = float(node.text)
elif node.tag == 'loc':
loc = float(node.text)
print(f'The answer to the question "{qus}" is "{ans}".')
# DispatchManager
# scale electricity consumed to flex between -1 and 1
amount = - 2 * (sine[t] - 0.5)
amount = scalar * (sine[t] + loc)
data = {'reference_price': amount}
return data, meta

0 comments on commit 76b8a53

Please sign in to comment.