Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function custom XML nodes #368

Merged
merged 5 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading