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

ENH: Add _cells and _name in exported spaces #134

Merged
merged 2 commits into from
Jul 14, 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
2 changes: 2 additions & 0 deletions modelx/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ def name(self):
"""Name of the object."""
return self._impl.name

_name = name

@property
def fullname(self):
"""Dotted name of the object.
Expand Down
2 changes: 2 additions & 0 deletions modelx/core/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ def cells(self):
"""A mapping of cells names to the cells objects in the space."""
return self._impl.cells.interfaces

_cells = cells

@property
def spaces(self):
"""A mapping associating names to named spaces."""
Expand Down
17 changes: 17 additions & 0 deletions modelx/export/_mx_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class BaseParent(BaseMxObject):
_mx_spaces: Dict[str, 'BaseSpace']
_parent: 'BaseParent'
_model: 'BaseModel'
_name: str

def _mx_walk(self, skip_self: bool = False):
"""Generator yielding spaces in breadth-first order"""
Expand Down Expand Up @@ -67,6 +68,22 @@ def _mx_load_io(self):

class BaseSpace(BaseParent):

# Class variable
_mx_cells_names: list

# Instance variables
_mx_is_cells_set: bool
_mx_cells: dict

@property
def _cells(self):
if not self._mx_is_cells_set:
for name in self._mx_cells_names:
self._mx_cells[name] = getattr(self, name)
self._mx_is_cells_set = True

return self._mx_cells


def _mx_is_in(self, parent: BaseParent):
p = self
Expand Down
21 changes: 20 additions & 1 deletion modelx/export/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ def __init__(self):
# modelx variables
self._parent = None
self._model = self
self._name = "{name}"

{space_assigns}
{space_dict}
Expand Down Expand Up @@ -341,15 +342,20 @@ class SpaceTranslator(ParentTranslator):
class_template = textwrap.dedent("""\
class _c_{name}(_mx_sys.BaseSpace):

{cells_names_assign}

def __init__(self, parent):

# modelx variables
self._space = self
self._parent = parent
self._model = parent._model
self._name = "{name}"

{space_assigns}
{space_dict}
self._mx_cells = {{}} # Populated on calling self._cells
self._mx_is_cells_set = False
{itemspace_dict}
self._mx_roots = [] # Dynamic Space only

Expand Down Expand Up @@ -530,6 +536,7 @@ def _get_class_def(self, space: BaseSpace):

return self.class_template.format(
name=space.name,
cells_names_assign=textwrap.indent(self.cells_names_assign(space), ' ' * 4),
space_assigns=textwrap.indent(self.space_assigns(space), ' ' * 8),
space_dict=textwrap.indent(self.space_dict(space), ' ' * 8),
itemspace_dict=textwrap.indent(itemspace_dict, ' ' * 8),
Expand Down Expand Up @@ -569,4 +576,16 @@ def param_assigns(self, params, copy=False):
else:
result.append('pass')

return "\n".join(result)
return "\n".join(result)

def cells_names_assign(self, space):
str_elm = []
if space.cells:
str_elm.append("\n")
for name in space.cells:
str_elm.append("'" + name + "'")
str_elm.append(",\n")
template = textwrap.dedent("""\
_mx_cells_names = [{names}]
""")
return template.format(names=textwrap.indent(''.join(str_elm), ' ' * 4))
34 changes: 34 additions & 0 deletions modelx/tests/export/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,40 @@ def BEL_LAPSE():
for i in range(1, 5):
nomx.RA[i].BEL_LAPSE() == 120

finally:
sys.path.pop(0)
m.close()


def test_space_properties(tmp_path_factory):

m = mx.new_model()
s1 = m.new_space("Space1")
s2 = s1.new_space("Space2")

@mx.defcells(space=s1)
def get_parent():
return _space._parent

@mx.defcells(space=s2)
def get_parent():
return _space._parent

@mx.defcells(space=s1)
def get_name():
return _space._name

nomx_path = tmp_path_factory.mktemp('model')
m.export(nomx_path / 'TestSpaceProperties_nomx')

try:
sys.path.insert(0, str(nomx_path))
from TestSpaceProperties_nomx import mx_model as nomx

assert nomx.Space1.get_parent() is nomx
assert nomx.Space1.Space2.get_parent() is nomx.Space1
assert nomx.Space1.get_name() == "Space1"

finally:
sys.path.pop(0)
m.close()
29 changes: 29 additions & 0 deletions modelx/tests/export/test_lifelib.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,35 @@ def test_basiclife_and_savings(basiclife_and_savings):
)


def test_appliedlife(tmp_path_factory):
import lifelib
library, name = 'appliedlife', 'IntegratedLife'

work_dir = tmp_path_factory.mktemp('tmp') / library
lifelib.create(library, work_dir)

model = mx.read_model(work_dir / name)
model.export(work_dir / (name + '_nomx'))

try:
sys.path.insert(0, str(work_dir))
nomx = importlib.import_module((name + '_nomx')).mx_model

pd.testing.assert_frame_equal(
model.Run[1].GMXB.result_pv(),
nomx.Run[1].GMXB.result_pv()
)

pd.testing.assert_frame_equal(
model.Run[1].GMXB.result_sample(),
nomx.Run[1].GMXB.result_sample()
)

finally:
sys.path.pop(0)
model.close()


def test_assets(tmp_path_factory):
import lifelib
library, name, arg = 'assets', 'BasicBonds', None
Expand Down
Loading