Skip to content

Commit c4963de

Browse files
overloaded Integrator with reset times instead of pathsim integrator
1 parent 606d5f5 commit c4963de

File tree

7 files changed

+87
-39
lines changed

7 files changed

+87
-39
lines changed

src/custom_pathsim_blocks.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,44 @@ def __init__(self, f1, f2, f3):
5656
super().__init__(n=3, fractions=[f1, f2, f3])
5757

5858

59+
class Integrator(pathsim.blocks.Integrator):
60+
"""Integrator block with a reset method."""
61+
62+
def __init__(self, initial_value=0.0, reset_times=None):
63+
"""
64+
Args:
65+
initial_value: Initial value of the integrator.
66+
reset_times: List of times at which the integrator is reset. If None, no reset events are created.
67+
"""
68+
super().__init__(initial_value=initial_value)
69+
self.reset_times = reset_times
70+
71+
def create_reset_events(self):
72+
"""Create reset events for the integrator based on the reset times.
73+
74+
Raises:
75+
ValueError: If reset_times is not valid.
76+
77+
Returns:
78+
list of reset events.
79+
"""
80+
if self.reset_times is None:
81+
return []
82+
if isinstance(self.reset_times, (int, float)):
83+
reset_times = [self.reset_times]
84+
elif isinstance(self.reset_times, list) and all(
85+
isinstance(t, (int, float)) for t in self.reset_times
86+
):
87+
reset_times = self.reset_times
88+
else:
89+
raise ValueError("reset_times must be a single value or a list of times")
90+
91+
return [
92+
pathsim.blocks.Schedule(t_start=t, t_end=t, func_act=self.reset)
93+
for t in reset_times
94+
]
95+
96+
5997
# BUBBLER SYSTEM
6098

6199

src/pathsim_utils.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
Amplifier,
1313
Adder,
1414
Multiplier,
15-
Integrator,
1615
Function,
1716
Delay,
1817
RNG,
@@ -27,6 +26,7 @@
2726
Splitter3,
2827
Bubbler,
2928
FestimWall,
29+
Integrator,
3030
)
3131
from flask import jsonify
3232
import inspect
@@ -82,24 +82,13 @@ def create_integrator(
8282
if eval_namespace is None:
8383
eval_namespace = globals()
8484

85-
block = Integrator(
86-
initial_value=eval(node["data"]["initial_value"], eval_namespace)
87-
if node["data"].get("initial_value") and node["data"]["initial_value"] != ""
88-
else 0.0,
85+
parameters = get_parameters_for_block_class(
86+
Integrator, node, eval_namespace=eval_namespace
8987
)
88+
89+
block = Integrator(**parameters)
9090
# add events to reset integrator if needed
91-
events = []
92-
if node["data"]["reset_times"] != "":
93-
94-
def reset_itg(_):
95-
block.reset()
96-
97-
reset_times = eval(node["data"]["reset_times"], eval_namespace)
98-
if isinstance(reset_times, (int, float)):
99-
# If it's a single number, convert it to a list
100-
reset_times = [reset_times]
101-
for t in reset_times:
102-
events.append(Schedule(t_start=t, t_end=t, func_act=reset_itg))
91+
events = block.create_reset_events()
10392
return block, events
10493

10594

@@ -161,7 +150,7 @@ def create_bubbler(node: dict) -> Bubbler:
161150
return block, events
162151

163152

164-
def create_scope(node: dict, edges, nodes) -> Scope:
153+
def make_labels_for_scope(node: dict, edges: list, nodes: list) -> list[str]:
165154
# Find all incoming edges to this node and sort by source id for consistent ordering
166155
incoming_edges = [edge for edge in edges if edge["target"] == node["id"]]
167156
incoming_edges.sort(key=lambda x: x["source"])
@@ -188,12 +177,17 @@ def create_scope(node: dict, edges, nodes) -> Scope:
188177
if edge["sourceHandle"]:
189178
labels[i] += f" ({edge['sourceHandle']})"
190179

191-
parameters = get_parameters_for_block_class(Scope, node, eval_namespace=globals())
192-
# remove labels from parameters, as they are set separately
193-
parameters.pop("labels", None)
180+
return labels, connections_order
181+
182+
183+
def create_scope(node: dict, edges, nodes) -> Scope:
184+
block = auto_block_construction(node, eval_namespace=globals())
194185

195-
block = Scope(labels=labels, **parameters)
186+
# override labels + add connections order
187+
# TODO this should be done in "make connections"
188+
labels, connections_order = make_labels_for_scope(node, edges, nodes)
196189
block._connections_order = connections_order
190+
block.labels = labels
197191

198192
return block
199193

src/templates/block_macros.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,9 @@
1313
{% macro create_integrator_block(node) -%}
1414
{{ create_block(node) }}
1515

16-
{%- if node["data"].get("reset_times") %}
17-
def reset_{{ node["var_name"] }}(_):
18-
{{ node["var_name"] }}.reset()
19-
20-
for t in {{ node["data"].get("reset_times", "[]") }}:
21-
events.append(
22-
pathsim.events.Schedule(
23-
t_start=t,
24-
t_end=t,
25-
func_act=reset_{{ node["var_name"] }},
26-
)
27-
)
16+
{%- if node["data"].get("replacement_times") %}
17+
events_{{ node["var_name"] }} = {{ node["var_name"] }}.create_reset_events()
18+
events += events_{{ node["var_name"] }}
2819
{%- endif %}
2920

3021
{%- endmacro -%}

test/test_backend.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
auto_block_construction,
44
create_function,
55
create_bubbler,
6+
create_scope,
67
)
7-
from src.custom_pathsim_blocks import Process, Splitter2, Splitter3, Bubbler
8+
from src.custom_pathsim_blocks import Process, Splitter2, Splitter3, Bubbler, Integrator
89

910
import pathsim.blocks
1011

@@ -64,7 +65,10 @@
6465
"type": "splitter3",
6566
"data": {"f1": "1/3", "f2": "1/3", "f3": "1/3", "label": "Splitter 3"},
6667
},
67-
"scope": {"type": "scope", "data": {"label": "Scope"}},
68+
"scope": {
69+
"type": "scope",
70+
"data": {"label": "Scope", "sampling_rate": "", "labels": ""},
71+
},
6872
"white_noise": {
6973
"type": "white_noise",
7074
"data": {
@@ -127,7 +131,7 @@ def test_create_integrator():
127131
}
128132
integrator, events = create_integrator(node)
129133

130-
assert isinstance(integrator, pathsim.blocks.Integrator)
134+
assert isinstance(integrator, Integrator)
131135
assert integrator.initial_value == 0
132136
for event in events:
133137
assert isinstance(event, pathsim.blocks.Schedule)
@@ -214,3 +218,15 @@ def test_create_bubbler():
214218
}
215219
block, events = create_bubbler(node)
216220
assert isinstance(block, Bubbler)
221+
222+
223+
def test_make_scope():
224+
node = {
225+
"id": "7",
226+
"type": "scope",
227+
"data": {"label": "scope 7", "sampling_rate": "", "labels": "", "t_wait": ""},
228+
}
229+
block = create_scope(node, edges=[], nodes=[node])
230+
assert isinstance(block, pathsim.blocks.Scope)
231+
assert block.labels == []
232+
assert block.sampling_rate is None

test/test_convert_python.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
"type": "scope",
3838
"data": {
3939
"label": "scope_1",
40+
"labels": "",
41+
"sampling_rate": "",
42+
"t_wait": "",
4043
},
4144
},
4245
],

test/test_files/constant_delay_scope.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@
4242
"y": 220
4343
},
4444
"data": {
45-
"label": "scope 2"
45+
"label": "scope 2",
46+
"labels": "",
47+
"sampling_rate": "",
48+
"t_wait": ""
4649
},
4750
"measured": {
4851
"width": 120,

test/test_files/same_label.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
"y": 400.9936264496624
2727
},
2828
"data": {
29-
"label": "scope 2"
29+
"label": "scope 2",
30+
"labels": "",
31+
"sampling_rate": "",
32+
"t_wait": ""
3033
},
3134
"measured": {
3235
"width": 120,

0 commit comments

Comments
 (0)