Skip to content

Commit

Permalink
Add / execute variable converters (#92)
Browse files Browse the repository at this point in the history
* add converter parameter to variable()

* maybe convert in variable property setter

* maybe convert input values

* add/update tests

* update release notes
  • Loading branch information
benbovy committed Jan 13, 2020
1 parent 537555d commit 8a226cf
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 8 deletions.
3 changes: 3 additions & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Enhancements
in the class (:issue:`67`).
- Added :func:`~xsimlab.validators.in_bounds` and
:func:`~xsimlab.validators.is_subdtype` validators (:issue:`87`).
- :func:`xsimlab.variable` has now a ``converter`` parameter that can be used to
convert any input value before (maybe) validating it and setting the variable
(:issue:`92`).

Bug fixes
~~~~~~~~~
Expand Down
5 changes: 4 additions & 1 deletion xsimlab/drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ def _set_in_store(self, input_vars, check_static=True):
f"in process {p_name!r}"
)

self.store[key] = copy.copy(value)
if var.converter is not None:
self.store[key] = var.converter(value)
else:
self.store[key] = copy.copy(value)

def initialize_store(self, input_vars):
"""Pre-populate the simulation active data store with input
Expand Down
8 changes: 7 additions & 1 deletion xsimlab/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ def get_target_variable(var):
return target_process_cls, target_var


def _dummy_converter(value):
return value


def _make_property_variable(var):
"""Create a property for a variable or a foreign variable (after
some sanity checks).
Expand All @@ -157,6 +161,7 @@ def _make_property_variable(var):
"""
var_name = var.name
var_converter = var.converter or _dummy_converter

def get_from_store(self):
key = self.__xsimlab_store_keys__[var_name]
Expand All @@ -169,7 +174,7 @@ def get_on_demand(self):

def put_in_store(self, value):
key = self.__xsimlab_store_keys__[var_name]
self.__xsimlab_store__[key] = value
self.__xsimlab_store__[key] = var_converter(value)

target_process_cls, target_var = get_target_variable(var)

Expand Down Expand Up @@ -437,6 +442,7 @@ def _reset_attributes(self):
new_attributes[k] = attr.attrib(
metadata=attrib.metadata,
validator=attrib.validator,
converter=attrib.converter,
default=attrib.default,
init=False,
repr=False,
Expand Down
4 changes: 3 additions & 1 deletion xsimlab/tests/fixture_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ def _get_u_opposite(self):

@xs.process
class InitProfile:
n_points = xs.variable(description="nb. of profile points", static=True)
n_points = xs.variable(
description="nb. of profile points", converter=int, static=True
)
u = xs.foreign(Profile, "u", intent="out")

def initialize(self):
Expand Down
2 changes: 1 addition & 1 deletion xsimlab/tests/fixture_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ExampleProcess:

in_var = xs.variable(dims=["x", ("x", "y")], description="input variable")
out_var = xs.variable(groups="example_group", intent="out")
inout_var = xs.variable(intent="inout")
inout_var = xs.variable(intent="inout", converter=int)
od_var = xs.on_demand()

in_foreign_var = xs.foreign(SomeProcess, "some_var")
Expand Down
17 changes: 13 additions & 4 deletions xsimlab/tests/test_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,21 @@ def test_set_store_ignore(self, base_driver):
assert ("not-a-model", "input") not in base_driver.store

def test_set_store_copy(self, base_driver):
n = np.array(10)
input_vars = {("init_profile", "n_points"): n}
n = np.array([1, 2, 3, 4])
input_vars = {("add", "offset"): n}
base_driver.initialize_store(input_vars)

assert base_driver.store[("init_profile", "n_points")] == n
assert base_driver.store[("init_profile", "n_points")] is not n
actual = base_driver.store[("add", "offset")]
assert_array_equal(actual, n)
assert actual is not n

def test_set_store_converter(self, base_driver):
input_vars = {("init_profile", "n_points"): 10.2}
base_driver.initialize_store(input_vars)

actual = base_driver.store[("init_profile", "n_points")]
assert actual == 10
assert type(actual) is int

def test_initialize_store(self, base_driver):
input_vars = {("init_profile", "n_points"): 10}
Expand Down
8 changes: 8 additions & 0 deletions xsimlab/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ def test_process_properties_values(processes_with_store):
assert set(example_process.group_var) == {1, 4}


def test_process_properties_converter(processes_with_store):
_, _, example_process = processes_with_store

example_process.inout_var = 1.1
assert example_process.inout_var == 1
assert type(example_process.inout_var) is int


def test_runtime_decorator_noargs():
@xs.runtime
def meth(self):
Expand Down
7 changes: 7 additions & 0 deletions xsimlab/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def variable(
groups=None,
default=attr.NOTHING,
validator=None,
converter=None,
static=False,
description="",
attrs=None,
Expand Down Expand Up @@ -152,6 +153,11 @@ def variable(
is given.
If a ``list`` is passed, its items are all are treated as validators.
The validator can also be set using decorator notation.
converter : callable, optional
Callable that is called when setting a value for this variable, and that
converts the value to the desired format. The callable must accept
one argument and return one value. The value is converted before being
passed to the validator, if any.
static : bool, optional
If True, the value of the (input) variable must be set once
before the simulation starts and cannot be further updated
Expand Down Expand Up @@ -192,6 +198,7 @@ def variable(
metadata=metadata,
default=default,
validator=validator,
converter=converter,
init=_init,
repr=_repr,
kw_only=True,
Expand Down

0 comments on commit 8a226cf

Please sign in to comment.