Skip to content

Commit 4e7d7fe

Browse files
Merge pull request #85 from festim-dev/white_noise_node
Noise source nodes
2 parents 2729a11 + d2ee8f9 commit 4e7d7fe

File tree

5 files changed

+93
-36
lines changed

5 files changed

+93
-36
lines changed

src/App.jsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ const nodeTypes = {
5050
splitter2: Splitter2Node,
5151
splitter3: Splitter3Node,
5252
bubbler: BubblerNode,
53+
white_noise: SourceNode,
54+
pink_noise: SourceNode,
55+
5356
};
5457

5558
// Defining initial nodes and edges. In the data section, we have label, but also parameters specific to the node.
@@ -608,6 +611,13 @@ export default function App() {
608611
break;
609612
case 'bubbler':
610613
nodeData = { ...nodeData, conversion_efficiency: '0.95', vial_efficiency: '0.9', replacement_times: '' };
614+
break;
615+
case 'white_noise':
616+
nodeData = { ...nodeData, spectral_density: '1', sampling_rate: '' };
617+
break;
618+
case 'pink_noise':
619+
nodeData = { ...nodeData, spectral_density: '1', num_octaves: '16', sampling_rate: '' };
620+
break;
611621
default:
612622
// For any other types, just use basic data
613623
break;

src/custom_pathsim_blocks.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55

66

77
class Process(ODE):
8-
def __init__(self, residence_time=0, ic=0, gen=0):
8+
def __init__(self, residence_time=0, initial_value=0, source_term=0):
99
alpha = -1 / residence_time if residence_time != 0 else 0
1010
super().__init__(
11-
func=lambda x, u, t: x * alpha + sum(u) + gen, initial_value=ic
11+
func=lambda x, u, t: x * alpha + sum(u) + source_term,
12+
initial_value=initial_value,
1213
)
1314
self.residence_time = residence_time
14-
self.ic = ic
15-
self.gen = gen
15+
self.initial_value = initial_value
16+
self.source_term = source_term
1617

1718
def update(self, t):
1819
x = self.engine.get()
@@ -39,6 +40,22 @@ def update(self, t):
3940
self.outputs.update_from_array(self.fractions * u)
4041

4142

43+
class Splitter2(Splitter):
44+
def __init__(self, f1, f2):
45+
"""
46+
Splitter with two outputs, fractions are f1 and f2.
47+
"""
48+
super().__init__(n=2, fractions=[f1, f2])
49+
50+
51+
class Splitter3(Splitter):
52+
def __init__(self, f1, f2, f3):
53+
"""
54+
Splitter with three outputs, fractions are f1, f2 and f3.
55+
"""
56+
super().__init__(n=3, fractions=[f1, f2, f3])
57+
58+
4259
# BUBBLER SYSTEM
4360

4461

src/pathsim_utils.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
PID,
2020
Schedule,
2121
)
22-
from .custom_pathsim_blocks import Process, Splitter, Bubbler
22+
from pathsim.blocks.noise import WhiteNoise, PinkNoise
23+
from .custom_pathsim_blocks import Process, Splitter, Splitter2, Splitter3, Bubbler
2324
from flask import jsonify
25+
import inspect
2426

2527
NAME_TO_SOLVER = {
2628
"SSPRK22": pathsim.solvers.SSPRK22,
@@ -34,8 +36,8 @@
3436
"amplifier": Amplifier,
3537
"amplifier_reverse": Amplifier,
3638
"scope": Scope,
37-
"splitter2": Splitter,
38-
"splitter3": Splitter,
39+
"splitter2": Splitter2,
40+
"splitter3": Splitter3,
3941
"adder": Adder,
4042
"adder_reverse": Adder,
4143
"multiplier": Multiplier,
@@ -47,6 +49,8 @@
4749
"function": Function,
4850
"delay": Delay,
4951
"bubbler": Bubbler,
52+
"white_noise": WhiteNoise,
53+
"pink_noise": PinkNoise,
5054
}
5155

5256

@@ -296,14 +300,25 @@ def auto_block_construction(node: dict, eval_namespace: dict = None) -> Block:
296300

297301
block_class = map_str_to_object[block_type]
298302

299-
# skip 'self'
300-
parameters_for_class = block_class.__init__.__code__.co_varnames[1:]
303+
parameters_for_class = inspect.signature(block_class.__init__).parameters
304+
parameters = {}
305+
for k, value in parameters_for_class.items():
306+
if k == "self":
307+
continue
308+
# Skip 'operations' for Adder, as it is handled separately
309+
# https://github.com/festim-dev/fuel-cycle-sim/issues/73
310+
if k in ["operations"]:
311+
continue
312+
user_input = node["data"][k]
313+
if user_input == "":
314+
if value.default is inspect._empty:
315+
raise ValueError(
316+
f"expected parameter for {k} in {block_type} ({node['label']})"
317+
)
318+
parameters[k] = value.default
319+
else:
320+
parameters[k] = eval(user_input, eval_namespace)
301321

302-
parameters = {
303-
k: eval(v, eval_namespace)
304-
for k, v in node["data"].items()
305-
if k in parameters_for_class
306-
}
307322
return block_class(**parameters)
308323

309324

@@ -329,21 +344,15 @@ def make_blocks(
329344
tau=eval(node["data"]["delay"], eval_namespace),
330345
)
331346
elif block_type == "splitter2":
332-
block = Splitter(
333-
n=2,
334-
fractions=[
335-
eval(node["data"]["f1"], eval_namespace),
336-
eval(node["data"]["f2"], eval_namespace),
337-
],
347+
block = Splitter2(
348+
f1=eval(node["data"]["f1"], eval_namespace),
349+
f2=eval(node["data"]["f2"], eval_namespace),
338350
)
339351
elif block_type == "splitter3":
340-
block = Splitter(
341-
n=3,
342-
fractions=[
343-
eval(node["data"]["f1"], eval_namespace),
344-
eval(node["data"]["f2"], eval_namespace),
345-
eval(node["data"]["f3"], eval_namespace),
346-
],
352+
block = Splitter3(
353+
f1=eval(node["data"]["f1"], eval_namespace),
354+
f2=eval(node["data"]["f2"], eval_namespace),
355+
f3=eval(node["data"]["f3"], eval_namespace),
347356
)
348357
elif block_type == "bubbler":
349358
block, events_bubbler = create_bubbler(node)

src/templates/template_with_macros.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
iterations_max={{ solverParams["iterations_max"] }},
4949
log={{ solverParams["log"].capitalize() }},
5050
tolerance_fpi={{ solverParams["tolerance_fpi"] }},
51+
{%- if solverParams["extra_params"] != '' -%}
5152
**{{ solverParams["extra_params"] }},
53+
{%- endif -%}
5254
)
5355

5456
if __name__ == "__main__":

test/test_backend.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
auto_block_construction,
44
create_function,
55
)
6-
from src.custom_pathsim_blocks import Process, Splitter
6+
from src.custom_pathsim_blocks import Process, Splitter2, Splitter3
77

88
import pathsim.blocks
99

@@ -33,17 +33,17 @@
3333
"data": {"expression": "3*x**2", "label": "Function"},
3434
},
3535
"delay": {"type": "delay", "data": {"tau": "1.0", "label": "Delay"}},
36-
"rng": {"type": "rng", "data": {"seed": "42", "label": "RNG"}},
36+
"rng": {"type": "rng", "data": {"sampling_rate": "2", "label": "RNG"}},
3737
"pid": {
3838
"type": "pid",
39-
"data": {"kp": "1.0", "ki": "0.0", "kd": "0.0", "label": "PID"},
39+
"data": {"Kp": "1.0", "Ki": "0.0", "Kd": "0.0", "f_max": "100", "label": "PID"},
4040
},
4141
"process": {
4242
"type": "process",
4343
"data": {
4444
"residence_time": "1.0",
45-
"ic": "0.0",
46-
"gen": "0.0",
45+
"initial_value": "0.0",
46+
"source_term": "0.0",
4747
"label": "Process",
4848
},
4949
},
@@ -56,6 +56,23 @@
5656
"data": {"f1": "1/3", "f2": "1/3", "f3": "1/3", "label": "Splitter 3"},
5757
},
5858
"scope": {"type": "scope", "data": {"label": "Scope"}},
59+
"white_noise": {
60+
"type": "white_noise",
61+
"data": {
62+
"spectral_density": "1",
63+
"sampling_rate": "2",
64+
"label": "White Noise Source",
65+
},
66+
},
67+
"pink_noise": {
68+
"type": "pink_noise",
69+
"data": {
70+
"spectral_density": "1",
71+
"num_octaves": "16",
72+
"sampling_rate": "5",
73+
"label": "Pink Noise Source",
74+
},
75+
},
5976
}
6077

6178

@@ -117,8 +134,10 @@ def test_create_integrator():
117134
("rng", pathsim.blocks.RNG),
118135
("pid", pathsim.blocks.PID),
119136
("process", Process),
120-
("splitter2", Splitter),
121-
("splitter3", Splitter),
137+
("splitter2", Splitter2),
138+
("splitter3", Splitter3),
139+
("white_noise", pathsim.blocks.noise.WhiteNoise),
140+
("pink_noise", pathsim.blocks.noise.PinkNoise),
122141
],
123142
)
124143
def test_auto_block_construction(node_factory, block_type, expected_class):
@@ -140,8 +159,8 @@ def test_auto_block_construction(node_factory, block_type, expected_class):
140159
("rng", pathsim.blocks.RNG),
141160
("pid", pathsim.blocks.PID),
142161
("process", Process),
143-
("splitter2", Splitter),
144-
("splitter3", Splitter),
162+
("white_noise", pathsim.blocks.noise.WhiteNoise),
163+
("pink_noise", pathsim.blocks.noise.PinkNoise),
145164
],
146165
)
147166
def test_auto_block_construction_with_var(node_factory, block_type, expected_class):

0 commit comments

Comments
 (0)