Skip to content

Commit

Permalink
Merge pull request #33 from Aerosmite/master
Browse files Browse the repository at this point in the history
Added Components class.
  • Loading branch information
arvedes committed Aug 17, 2023
2 parents 2f85b36 + a17e164 commit 56cfe2d
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v1.2.0
- add Component class

v1.1.5
- allow duplicate Materials, Solvers, etc. to be added

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ The basic working principle of pyelmer is the representation of sif-file entries
- *Material*
- *Body*
- *Boundary*
- *Component*
- *BodyForce*
- *InitialCondition*

Expand Down
101 changes: 97 additions & 4 deletions pyelmer/elmer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def __init__(self):
self.bodies = {}
self.boundaries = {}
self.body_forces = {}
self.components = {}
self.initial_conditions = {}
self.solvers = {}
self.equations = {}
Expand Down Expand Up @@ -93,6 +94,12 @@ def write_sif(self, simulation_dir):
f.write(self._dict_to_str(body_force.get_data()))
f.write("End\n\n")
f.write("\n")
for component_name, component in self.components.items():
f.write("! " + component_name + "\n")
f.write("Component " + str(component.id) + "\n")
f.write(self._dict_to_str(component.get_data()))
f.write("End\n\n")
f.write("\n")
for (
initial_condition_name,
initial_condition,
Expand Down Expand Up @@ -138,6 +145,7 @@ def _set_ids(self):
self.bodies,
self.boundaries,
self.body_forces,
self.components,
self.initial_conditions,
]
# give each object id
Expand Down Expand Up @@ -185,12 +193,12 @@ def __init__(self, simulation, name, body_ids=None, data=None):
else:
self.data = data
# optional parameters
self.equation = None #: optional reference to an Equation object
self.equation = None # : optional reference to an Equation object
self.initial_condition = (
None #: optional reference to an InitialCondition object
None # : optional reference to an InitialCondition object
)
self.material = None #: optional reference to a Material object
self.body_force = None #: optional reference to a BodyForce object
self.material = None # : optional reference to a Material object
self.body_force = None # : optional reference to a BodyForce object

def get_data(self):
"""Generate dictionary with data for sif-file."""
Expand Down Expand Up @@ -347,6 +355,91 @@ def __str__(self):
return str(self.id)


class Component:
"""Wrapper for components in sif-file."""

def __new__(
cls, simulation, name, master_bodies=None, master_boundaries=None, data=None
):
"""Intercept object construction to return an existing object if possible."""
if name in simulation.components:
existing = simulation.components[name]
new_master_bodies = [] if master_bodies is None else master_bodies
new_master_boundaries = (
[] if master_boundaries is None else master_boundaries
)
new_data = {} if data is None else data
if [new_master_bodies, new_master_boundaries, new_data] != [
existing.master_bodies,
existing.master_boundaries,
existing.data,
]:
raise ValueError(f'Component name clash: "{name}".')
return existing
else:
return super().__new__(cls)

def __init__(
self, simulation, name, master_bodies=None, master_boundaries=None, data=None
):
"""Create component object.
Args:
simulation (Simulation Object): The component is added to
this simulation object.
name (str): Name of the component
master_bodies (list of Body, optional): Master Body objects.
master_bodies (list of Boundary, optional): Master Boundary objects.
data (dict, optional): Component data as in sif-file.
"""
simulation.components.update({name: self})
self.id = 0
self.name = name
if master_bodies is None:
self.master_bodies = []
else:
self.master_bodies = master_bodies
if master_boundaries is None:
self.master_boundaries = []
else:
self.master_boundaries = master_boundaries
if data is None:
self.data = {}
else:
self.data = data

def get_data(self):
"""Generate dictionary with data for sif-file."""
d = {
"Name": self.name,
}
if self.master_bodies != []:
body_names = ""
for body in self.master_bodies:
body_names += f"{body.name}, "
body_names = body_names.strip()
d.update(
{
f"Master Bodies({len(self.master_bodies)})": f"{' '.join([str(x) for x in self.master_bodies])} ! {body_names}"
}
)
if self.master_boundaries != []:
boundary_names = ""
for boundary in self.master_boundaries:
boundary_names += f"{boundary.name}, "
boundary_names = boundary_names.strip()
d.update(
{
f"Master Boundaries({len(self.master_boundaries)})": f"{' '.join([str(x) for x in self.master_boundaries])} ! {boundary_names}"
}
)
d.update(self.data)
return d

def __str__(self):
return str(self.id)


class InitialCondition:
"""Wrapper for initial condition in sif-file."""

Expand Down
89 changes: 86 additions & 3 deletions pyelmer/test/test_elmer.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,39 @@ def test_body_force():
assert empty_test_material.get_data() == {}


def test_component():
# setup simulation
sim = elmer.Simulation()
body1 = elmer.Body(sim, "body1")
body2 = elmer.Body(sim, "body2")
boundary1 = elmer.Boundary(sim, "boundary1")
boundary2 = elmer.Boundary(sim, "boundary2")
# set id manually for testing purposes, don't do that in a productive environment
body1.id = 1
body2.id = 2
boundary1.id = 3
boundary2.id = 4

# initialize data for test component
test_component = elmer.Component(
sim,
"test_component",
master_bodies=[body1, body2],
master_boundaries=[boundary1, boundary2],
data={"test_parameter": 1.0},
)
assert test_component.get_data() == {
"Name": "test_component",
"Master Bodies(2)": "1 2 ! body1, body2,",
"Master Boundaries(2)": "3 4 ! boundary1, boundary2,",
"test_parameter": 1.0,
}
empty_test_component = elmer.Component(sim, "empty_test_component")
assert empty_test_component.get_data() == {
"Name": "empty_test_component",
}


def test_initial_condition():
# setup simulation
sim = elmer.Simulation()
Expand Down Expand Up @@ -201,7 +234,7 @@ def test_load_body_force():
elmer.load_body_force(
"test_body_force",
sim,
os.path.join(file_dir, "test_data", "body_forces.yml")
os.path.join(file_dir, "test_data", "body_forces.yml"),
).get_data()
== data
)
Expand Down Expand Up @@ -354,6 +387,46 @@ def test_duplicate_equation():
elmer.Equation(sim, "duplicate", [solver2], data={"test": 7})


def test_duplicate_component():
sim = elmer.Simulation()

# test with master body
body = elmer.Body(sim, "test_body")

obj1 = elmer.Component(sim, "duplicate", master_bodies=[body], data={"test": 7})
obj2 = elmer.Component(sim, "duplicate", master_bodies=[body], data={"test": 7})
assert obj1 is obj2

obj3 = elmer.Component(sim, "different")
assert obj1 is not obj3

with pytest.raises(ValueError):
elmer.Component(sim, "duplicate", master_bodies=[body])

with pytest.raises(ValueError):
elmer.Component(sim, "duplicate", data={"test": 7})

# test with master boundary
boundary = elmer.Body(sim, "test_boundary")

obj1 = elmer.Component(
sim, "duplicate2", master_boundaries=[boundary], data={"test": 7}
)
obj2 = elmer.Component(
sim, "duplicate2", master_boundaries=[boundary], data={"test": 7}
)
assert obj1 is obj2

obj3 = elmer.Component(sim, "different")
assert obj1 is not obj3

with pytest.raises(ValueError):
elmer.Component(sim, "duplicate2", master_boundaries=[boundary])

with pytest.raises(ValueError):
elmer.Component(sim, "duplicate2", data={"test": 7})


def test_write_sif():
# setup gmsh
gmsh.initialize()
Expand Down Expand Up @@ -384,7 +457,7 @@ def test_write_sif():
test_initial_condtion = elmer.load_initial_condition(
"test_initial_condition",
sim,
os.path.join(file_dir, "test_data", "initial_conditions.yml")
os.path.join(file_dir, "test_data", "initial_conditions.yml"),
)

test_body_force = elmer.load_body_force(
Expand All @@ -397,6 +470,7 @@ def test_write_sif():
test_material = elmer.load_material(
"test_material", sim, os.path.join(file_dir, "test_data", "materials.yml")
)

test_body.material = test_material
test_body.initial_condition = test_initial_condtion
test_body.equation = test_eqn
Expand All @@ -407,6 +481,11 @@ def test_write_sif():

# initialize test boundary
test_boundary = elmer.Boundary(sim, "test_boundary", [ph_test_boundary])

# setup component object
test_component = elmer.Component(
sim, "test_component", [test_body], [test_boundary], {"test_parameter": "1.0"}
)
test_boundary.data.update({"test_parameter": "1.0"})

with tempfile.TemporaryDirectory() as tempdir:
Expand All @@ -429,6 +508,7 @@ def test_write_sif():
bodies = {}
boundaries = {}
body_forces = {}
components = {}
initial_conditions = {}
names = [
"Simulation",
Expand All @@ -439,6 +519,7 @@ def test_write_sif():
"Body",
"Boundary Condition",
"Body Force",
"Component",
"Initial Condition",
]
objects = [
Expand All @@ -450,6 +531,7 @@ def test_write_sif():
bodies,
boundaries,
body_forces,
components,
initial_conditions,
]

Expand Down Expand Up @@ -484,7 +566,7 @@ def test_write_sif():
read_object = True
if any(map(str.isdigit, line)):
object_name, object_number, _ = map(
str.strip, re.split("(\d+)", line)
str.strip, re.split(r"(\d+)", line)
)
for i, name in enumerate(names):
if name == object_name:
Expand Down Expand Up @@ -512,6 +594,7 @@ def test_write_sif():
assert bodies["test_body"] == test_body.get_data()
assert boundaries["test_boundary"] == test_boundary.get_data()
assert body_forces["test_body_force"] == test_body_force.get_data()
assert components["test_component"] == test_component.get_data()
assert (
initial_conditions["test_initial_condition"] == test_initial_condtion.get_data()
)
9 changes: 9 additions & 0 deletions pyelmer/test/test_simdata/case.sif
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ Body Force 1
End


! test_component
Component 1
Name = test_component
Master Bodies(1) = 1 ! test_body,
Master Boundaries(1) = 1 ! test_boundary,
test_parameter = 1.0
End


! test_initial_condition
Initial Condition 1
test_parameter = 1.0
Expand Down

0 comments on commit 56cfe2d

Please sign in to comment.