From 4354ad90cf8683805fcf50cac69517e5a5019758 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 09:39:29 -0400 Subject: [PATCH 01/11] bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 63ab43b7..af310f37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] requires-python = ">=3.8" dependencies = [ - "pathsim>=0.7.0", + "pathsim>=0.8.0", "matplotlib>=3.7.0", "numpy>=1.24.0", "plotly>=6.0", From f9ae0cb4464bad36485a2bd39b6689778dc52313 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 09:56:33 -0400 Subject: [PATCH 02/11] added proper mappings to all blocks --- src/python/custom_pathsim_blocks.py | 55 ++++++++++++++++++----------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/python/custom_pathsim_blocks.py b/src/python/custom_pathsim_blocks.py index c2b172e7..544c4fce 100644 --- a/src/python/custom_pathsim_blocks.py +++ b/src/python/custom_pathsim_blocks.py @@ -6,6 +6,7 @@ class Process(ODE): name_to_output_port = {"inv": 0, "mass_flow_rate": 1} + _port_map_out = {"inv": 0, "mass_flow_rate": 1} def __init__(self, residence_time=0, initial_value=0, source_term=0): alpha = -1 / residence_time if residence_time != 0 else 0 @@ -25,8 +26,8 @@ def update(self, t): mass_rate = x / self.residence_time # first output is the inv, second is the mass_flow_rate outputs = [None, None] - outputs[self.name_to_output_port["inv"]] = x - outputs[self.name_to_output_port["mass_flow_rate"]] = mass_rate + outputs[self._port_map_out["inv"]] = x + outputs[self._port_map_out["mass_flow_rate"]] = mass_rate # update the outputs self.outputs.update_from_array(outputs) @@ -48,6 +49,7 @@ def update(self, t): class Splitter2(Splitter): name_to_output_port = {"source1": 0, "source2": 1} + _port_map_out = {"source1": 0, "source2": 1} def __init__(self, f1=0.5, f2=0.5): """ @@ -58,6 +60,7 @@ def __init__(self, f1=0.5, f2=0.5): class Splitter3(Splitter): name_to_output_port = {"source1": 0, "source2": 1, "source3": 2} + _port_map_out = {"source1": 0, "source2": 1, "source3": 2} def __init__(self, f1=1 / 3, f2=1 / 3, f3=1 / 3): """ @@ -126,6 +129,17 @@ class Bubbler(Subsystem): "vial4": 3, "sample_out": 4, } + _port_map_out = { + "vial1": 0, + "vial2": 1, + "vial3": 2, + "vial4": 3, + "sample_out": 4, + } + _port_map_in = { + "sample_in_soluble": 0, + "sample_in_insoluble": 1, + } def __init__( self, @@ -183,7 +197,7 @@ def __init__( ] connections = [ Connection( - interface[self.name_to_input_port["sample_in_soluble"]], col_eff1 + interface[self._port_map_in["sample_in_soluble"]], col_eff1 ), Connection(col_eff1[0], vial_1), Connection(col_eff1[1], col_eff2), @@ -192,18 +206,18 @@ def __init__( Connection(conversion_eff[0], add1[0]), Connection(conversion_eff[1], add2[0]), Connection( - interface[self.name_to_input_port["sample_in_insoluble"]], add1[1] + interface[self._port_map_in["sample_in_insoluble"]], add1[1] ), Connection(add1, col_eff3), Connection(col_eff3[0], vial_3), Connection(col_eff3[1], col_eff4), Connection(col_eff4[0], vial_4), Connection(col_eff4[1], add2[1]), - Connection(vial_1, interface[self.name_to_output_port["vial1"]]), - Connection(vial_2, interface[self.name_to_output_port["vial2"]]), - Connection(vial_3, interface[self.name_to_output_port["vial3"]]), - Connection(vial_4, interface[self.name_to_output_port["vial4"]]), - Connection(add2, interface[self.name_to_output_port["sample_out"]]), + Connection(vial_1, interface[self._port_map_out["vial1"]]), + Connection(vial_2, interface[self._port_map_out["vial2"]]), + Connection(vial_3, interface[self._port_map_out["vial3"]]), + Connection(vial_4, interface[self._port_map_out["vial4"]]), + Connection(add2, interface[self._port_map_out["sample_out"]]), ] super().__init__(blocks, connections) @@ -262,17 +276,20 @@ class FestimWall(Block): name_to_output_port = {"flux_0": 0, "flux_L": 1} name_to_input_port = {"c_0": 0, "c_L": 1} + _port_map_out = {"flux_0": 0, "flux_L": 1} + _port_map_in = {"c_0": 0, "c_L": 1} + def __init__( self, thickness, temperature, D_0, E_D, surface_area=1, n_vertices=100 ): - super().__init__() try: import festim as F except ImportError: raise ImportError("festim is needed for FestimWall node.") + super().__init__() - self.inputs = Register(size=2) - self.outputs = Register(size=2) + self.inputs = Register(size=2, mapping=self._port_map_in) + self.outputs = Register(size=2, mapping=self._port_map_out) self.thickness = thickness self.temperature = temperature @@ -345,10 +362,8 @@ def update(self, t): # return 0.0 # block inputs - inputs = self.inputs.to_array() - c_0 = inputs[self.name_to_input_port["c_0"]] - c_L = inputs[self.name_to_input_port["c_L"]] - # print(c_0, c_L) + c_0 = self.inputs["c_0"] + c_L = self.inputs["c_L"] if t == 0.0: flux_0, flux_L = 0, 0 @@ -359,7 +374,7 @@ def update(self, t): flux_0 *= self.surface_area flux_L *= self.surface_area - outputs = [None, None] - outputs[self.name_to_output_port["flux_0"]] = flux_0 - outputs[self.name_to_output_port["flux_L"]] = flux_L - return self.outputs.update_from_array(outputs) + + self.outputs["flux_0"] = flux_0 + self.outputs["flux_L"] = flux_L + return self.outputs From 5d52ce91cd433a0867f958d50eba4cfd7a25435c Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 10:18:04 -0400 Subject: [PATCH 03/11] replaced by correct mapping --- src/python/custom_pathsim_blocks.py | 17 ----------------- src/python/pathsim_utils.py | 19 ++++++++++++------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/python/custom_pathsim_blocks.py b/src/python/custom_pathsim_blocks.py index 544c4fce..a0ec8a74 100644 --- a/src/python/custom_pathsim_blocks.py +++ b/src/python/custom_pathsim_blocks.py @@ -5,7 +5,6 @@ class Process(ODE): - name_to_output_port = {"inv": 0, "mass_flow_rate": 1} _port_map_out = {"inv": 0, "mass_flow_rate": 1} def __init__(self, residence_time=0, initial_value=0, source_term=0): @@ -48,7 +47,6 @@ def update(self, t): class Splitter2(Splitter): - name_to_output_port = {"source1": 0, "source2": 1} _port_map_out = {"source1": 0, "source2": 1} def __init__(self, f1=0.5, f2=0.5): @@ -59,7 +57,6 @@ def __init__(self, f1=0.5, f2=0.5): class Splitter3(Splitter): - name_to_output_port = {"source1": 0, "source2": 1, "source3": 2} _port_map_out = {"source1": 0, "source2": 1, "source3": 2} def __init__(self, f1=1 / 3, f2=1 / 3, f3=1 / 3): @@ -118,17 +115,6 @@ class Bubbler(Subsystem): n_soluble_vials: float n_insoluble_vials: float - name_to_input_port = { - "sample_in_soluble": 0, - "sample_in_insoluble": 1, - } - name_to_output_port = { - "vial1": 0, - "vial2": 1, - "vial3": 2, - "vial4": 3, - "sample_out": 4, - } _port_map_out = { "vial1": 0, "vial2": 1, @@ -273,9 +259,6 @@ def create_reset_events(self) -> list[pathsim.blocks.Schedule]: class FestimWall(Block): - name_to_output_port = {"flux_0": 0, "flux_L": 1} - name_to_input_port = {"c_0": 0, "c_L": 1} - _port_map_out = {"flux_0": 0, "flux_L": 1} _port_map_in = {"c_0": 0, "c_L": 1} diff --git a/src/python/pathsim_utils.py b/src/python/pathsim_utils.py index 110cd2eb..cb20b4b9 100644 --- a/src/python/pathsim_utils.py +++ b/src/python/pathsim_utils.py @@ -347,9 +347,12 @@ def get_input_index(block: Block, edge: dict, block_to_input_index: dict) -> int Returns: The input index for the block. """ - if hasattr(block, "name_to_input_port"): - return block.name_to_input_port[edge["targetHandle"]] - elif isinstance(block, Function): + + if edge["targetHandle"] is not None: + if block._port_map_in: + return block._port_map_in[edge["targetHandle"]] + + if isinstance(block, Function): return int(edge["targetHandle"].replace("target-", "")) else: # make sure that the target block has only one input port (ie. that targetHandle is None) @@ -372,9 +375,11 @@ def get_output_index(block: Block, edge: dict) -> int: Returns: The output index for the block. """ - if hasattr(block, "name_to_output_port"): - return block.name_to_output_port[edge["sourceHandle"]] - elif isinstance(block, Splitter): + if edge["sourceHandle"] is not None: + if block._port_map_out: + return block._port_map_out[edge["sourceHandle"]] + + if isinstance(block, Splitter): # Splitter outputs are always in order, so we can use the handle directly assert edge["sourceHandle"], edge output_index = int(edge["sourceHandle"].replace("source", "")) - 1 @@ -428,7 +433,7 @@ def make_connections(nodes, edges, blocks) -> list[Connection]: if edge["sourceHandle"]: label += f" ({edge['sourceHandle']})" target_block.labels.append(label) - + print(f"Connecting {source_block.id} output {output_index} to {target_block.id} input {input_index}") connection = Connection( source_block[output_index], target_block[input_index], From 9679f9b5357d076cc2c719d90e956ecb0b309a67 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 10:53:23 -0400 Subject: [PATCH 04/11] return aliases directly --- src/python/pathsim_utils.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/python/pathsim_utils.py b/src/python/pathsim_utils.py index cb20b4b9..6e50069e 100644 --- a/src/python/pathsim_utils.py +++ b/src/python/pathsim_utils.py @@ -350,7 +350,11 @@ def get_input_index(block: Block, edge: dict, block_to_input_index: dict) -> int if edge["targetHandle"] is not None: if block._port_map_in: - return block._port_map_in[edge["targetHandle"]] + # aliases are not yet supported for subsystems + if isinstance(block, pathsim.Subsystem): + return block._port_map_in[edge["targetHandle"]] + else: + return edge["targetHandle"] if isinstance(block, Function): return int(edge["targetHandle"].replace("target-", "")) @@ -377,7 +381,11 @@ def get_output_index(block: Block, edge: dict) -> int: """ if edge["sourceHandle"] is not None: if block._port_map_out: - return block._port_map_out[edge["sourceHandle"]] + # aliases are not yet supported for subsystems + if isinstance(block, pathsim.Subsystem): + return block._port_map_out[edge["sourceHandle"]] + else: + return edge["sourceHandle"] if isinstance(block, Splitter): # Splitter outputs are always in order, so we can use the handle directly @@ -433,7 +441,7 @@ def make_connections(nodes, edges, blocks) -> list[Connection]: if edge["sourceHandle"]: label += f" ({edge['sourceHandle']})" target_block.labels.append(label) - print(f"Connecting {source_block.id} output {output_index} to {target_block.id} input {input_index}") + connection = Connection( source_block[output_index], target_block[input_index], From 31abb5efa4b4481f610cd3637411336133b92d0f Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 11:01:05 -0400 Subject: [PATCH 05/11] support for ScheduleList --- src/components/EventsTab.jsx | 12 +++++++++--- src/python/pathsim_utils.py | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/EventsTab.jsx b/src/components/EventsTab.jsx index 247a66c8..64054610 100644 --- a/src/components/EventsTab.jsx +++ b/src/components/EventsTab.jsx @@ -9,6 +9,11 @@ const eventDefaults = { func_act: '', tolerance: '1e-16' }, + 'ScheduleList': { + times_evt: '', + func_act: '', + tolerance: '1e-16' + }, 'ZeroCrossingDown': { func_evt: '', func_act: '', @@ -47,7 +52,8 @@ const EventsTab = ({ events, setEvents }) => { const eventTypes = [ 'Condition', - 'Schedule', + 'Schedule', + 'ScheduleList', 'ZeroCrossing', 'ZeroCrossingUp', 'ZeroCrossingDown' @@ -75,13 +81,13 @@ const EventsTab = ({ events, setEvents }) => { // Validate required fields based on event type // For Schedule, func_act is required - if (currentEvent.type === 'Schedule' && !currentEvent.func_act) { + if (['Schedule', 'ScheduleList'].includes(currentEvent.type) && !currentEvent.func_act) { alert('func_act is required for Schedule events'); return; } // For other event types, both func_evt and func_act are typically required - if (currentEvent.type !== 'Schedule' && (!currentEvent.func_evt || !currentEvent.func_act)) { + if (!['Schedule', 'ScheduleList'].includes(currentEvent.type) && (!currentEvent.func_evt || !currentEvent.func_act)) { alert('Both func_evt and func_act are required for this event type'); return; } diff --git a/src/python/pathsim_utils.py b/src/python/pathsim_utils.py index 6e50069e..0b9a1ebd 100644 --- a/src/python/pathsim_utils.py +++ b/src/python/pathsim_utils.py @@ -93,6 +93,7 @@ "ZeroCrossingUp": pathsim.events.ZeroCrossingUp, "ZeroCrossing": pathsim.events.ZeroCrossing, "Schedule": pathsim.events.Schedule, + "ScheduleList": pathsim.events.ScheduleList, "Condition": pathsim.events.Condition, } From f2e6573c88de6a94284c536dacf30ecbc43b3d8b Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 11:53:36 -0400 Subject: [PATCH 06/11] reset_times instead of replacement_times --- src/python/templates/block_macros.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/templates/block_macros.py b/src/python/templates/block_macros.py index 559c3a15..9edaadaf 100644 --- a/src/python/templates/block_macros.py +++ b/src/python/templates/block_macros.py @@ -13,7 +13,7 @@ {% macro create_integrator_block(node) -%} {{ create_block(node) }} -{%- if node["data"].get("replacement_times") %} +{%- if node["data"].get("reset_times") %} events_{{ node["var_name"] }} = {{ node["var_name"] }}.create_reset_events() events += events_{{ node["var_name"] }} {%- endif %} From 0d05ddad3ba055bae623cde9461af8815d70f656 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 11:55:07 -0400 Subject: [PATCH 07/11] handle port references as strings --- src/python/convert_to_python.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/python/convert_to_python.py b/src/python/convert_to_python.py index fda816bc..9dc15c66 100644 --- a/src/python/convert_to_python.py +++ b/src/python/convert_to_python.py @@ -110,8 +110,14 @@ def make_edge_data(data: dict) -> list[dict]: edge["source_var_name"] = node["var_name"] edge["target_var_name"] = target_node["var_name"] - edge["source_port"] = f"[{output_index}]" - edge["target_port"] = f"[{input_index}]" + if isinstance(output_index, str): + edge["source_port"] = f"['{output_index}']" + else: + edge["source_port"] = f"[{output_index}]" + if isinstance(input_index, str): + edge["target_port"] = f"['{input_index}']" + else: + edge["target_port"] = f"[{input_index}]" block_to_input_index[target_block] += 1 return data["edges"] From 45c29c46145fc3e7bbbadc9b3bcb4379f5e993b1 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 12:00:49 -0400 Subject: [PATCH 08/11] simplified subsystem ports + commented out all schedulelists for now --- src/python/custom_pathsim_blocks.py | 33 ++++++++++++++++++----------- src/python/pathsim_utils.py | 14 +++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/python/custom_pathsim_blocks.py b/src/python/custom_pathsim_blocks.py index a0ec8a74..d38e9a19 100644 --- a/src/python/custom_pathsim_blocks.py +++ b/src/python/custom_pathsim_blocks.py @@ -1,5 +1,6 @@ from pathsim.blocks import Block, ODE import pathsim.blocks +import pathsim.events from pathsim import Subsystem, Interface, Connection import numpy as np @@ -102,6 +103,8 @@ def create_reset_events(self): pathsim.blocks.Schedule(t_start=t, t_end=t, func_act=self.reset) for t in reset_times ] + # need https://github.com/milanofthe/pathsim/pull/66 + # return pathsim.events.ScheduleList(times_evt=reset_times, func_act=self.reset) # BUBBLER SYSTEM @@ -163,7 +166,11 @@ def __init__( add1 = pathsim.blocks.Adder() add2 = pathsim.blocks.Adder() + # can be simplified when https://github.com/milanofthe/pathsim/pull/65 is merged interface = Interface() + interface._port_map_in = self._port_map_out + interface._port_map_out = self._port_map_in + interface.__init__() # reinitialize to rebuild registers self.vials = [vial_1, vial_2, vial_3, vial_4] @@ -182,28 +189,24 @@ def __init__( interface, ] connections = [ - Connection( - interface[self._port_map_in["sample_in_soluble"]], col_eff1 - ), + Connection(interface["sample_in_soluble"], col_eff1), Connection(col_eff1[0], vial_1), Connection(col_eff1[1], col_eff2), Connection(col_eff2[0], vial_2), Connection(col_eff2[1], conversion_eff), Connection(conversion_eff[0], add1[0]), Connection(conversion_eff[1], add2[0]), - Connection( - interface[self._port_map_in["sample_in_insoluble"]], add1[1] - ), + Connection(interface["sample_in_insoluble"], add1[1]), Connection(add1, col_eff3), Connection(col_eff3[0], vial_3), Connection(col_eff3[1], col_eff4), Connection(col_eff4[0], vial_4), Connection(col_eff4[1], add2[1]), - Connection(vial_1, interface[self._port_map_out["vial1"]]), - Connection(vial_2, interface[self._port_map_out["vial2"]]), - Connection(vial_3, interface[self._port_map_out["vial3"]]), - Connection(vial_4, interface[self._port_map_out["vial4"]]), - Connection(add2, interface[self._port_map_out["sample_out"]]), + Connection(vial_1, interface["vial1"]), + Connection(vial_2, interface["vial2"]), + Connection(vial_3, interface["vial3"]), + Connection(vial_4, interface["vial4"]), + Connection(add2, interface["sample_out"]), ] super().__init__(blocks, connections) @@ -221,7 +224,13 @@ def reset_itg(_): ) return events - def create_reset_events(self) -> list[pathsim.blocks.Schedule]: + # need https://github.com/milanofthe/pathsim/pull/66 + # events = pathsim.events.ScheduleList( + # times_evt=reset_times, func_act=reset_itg + # ) + # return events + + def create_reset_events(self) -> list[pathsim.events.Schedule]: """Create reset events for all vials based on the replacement times. Raises: diff --git a/src/python/pathsim_utils.py b/src/python/pathsim_utils.py index 0b9a1ebd..114ba5e8 100644 --- a/src/python/pathsim_utils.py +++ b/src/python/pathsim_utils.py @@ -348,14 +348,10 @@ def get_input_index(block: Block, edge: dict, block_to_input_index: dict) -> int Returns: The input index for the block. """ - + if edge["targetHandle"] is not None: if block._port_map_in: - # aliases are not yet supported for subsystems - if isinstance(block, pathsim.Subsystem): - return block._port_map_in[edge["targetHandle"]] - else: - return edge["targetHandle"] + return edge["targetHandle"] if isinstance(block, Function): return int(edge["targetHandle"].replace("target-", "")) @@ -382,11 +378,7 @@ def get_output_index(block: Block, edge: dict) -> int: """ if edge["sourceHandle"] is not None: if block._port_map_out: - # aliases are not yet supported for subsystems - if isinstance(block, pathsim.Subsystem): - return block._port_map_out[edge["sourceHandle"]] - else: - return edge["sourceHandle"] + return edge["sourceHandle"] if isinstance(block, Splitter): # Splitter outputs are always in order, so we can use the handle directly From 54bac8bf5a62fb47335bcc28d72071e7b743da33 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 12:32:12 -0400 Subject: [PATCH 09/11] added new maths blocks --- src/nodeConfig.js | 48 ++++++++++++++++++++++++++++++++++--- src/python/pathsim_utils.py | 21 ++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/nodeConfig.js b/src/nodeConfig.js index d2017be3..db823b83 100644 --- a/src/nodeConfig.js +++ b/src/nodeConfig.js @@ -7,7 +7,7 @@ import IntegratorNode from './components/nodes/IntegratorNode'; import AdderNode from './components/nodes/AdderNode'; import ScopeNode from './components/nodes/ScopeNode'; import StepSourceNode from './components/nodes/StepSourceNode'; -import {createFunctionNode} from './components/nodes/FunctionNode'; +import { createFunctionNode } from './components/nodes/FunctionNode'; import DefaultNode from './components/nodes/DefaultNode'; import MultiplierNode from './components/nodes/MultiplierNode'; import { Splitter2Node, Splitter3Node } from './components/nodes/Splitters'; @@ -60,6 +60,32 @@ export const nodeTypes = { fir: DefaultNode }; +export const nodeMathTypes = { + sin: DefaultNode, + cos: DefaultNode, + sqrt: DefaultNode, + abs: DefaultNode, + pow: DefaultNode, + exp: DefaultNode, + log: DefaultNode, + log10: DefaultNode, + tan: DefaultNode, + sinh: DefaultNode, + cosh: DefaultNode, + tanh: DefaultNode, + atan: DefaultNode, + norm: DefaultNode, + mod: DefaultNode, + clip: DefaultNode, +} + +// add nodeMathTypes to nodeTypes +Object.keys(nodeMathTypes).forEach(type => { + if (!nodeTypes[type]) { + nodeTypes[type] = nodeMathTypes[type]; + } +}); + // Node categories for better organization export const nodeCategories = { 'Sources': { @@ -71,7 +97,7 @@ export const nodeCategories = { description: 'Signal processing and transformation nodes' }, 'Math': { - nodes: ['adder', 'multiplier', 'splitter2', 'splitter3'], + nodes: ['adder', 'multiplier', 'splitter2', 'splitter3'].concat(Object.keys(nodeMathTypes)), description: 'Mathematical operation nodes' }, 'Control': { @@ -124,8 +150,24 @@ export const getNodeDisplayName = (nodeType) => { 'scope': 'Scope', 'spectrum': 'Spectrum', 'differentiator': 'Differentiator', + 'sin': 'Sine', + 'cos': 'Cosine', + 'sqrt': 'Square Root', + 'abs': 'Absolute', + 'pow': 'Power', + 'exp': 'Exponential', + 'log': 'Logarithm', + 'log10': 'Logarithm (Base 10)', + 'tan': 'Tangent', + 'sinh': 'Hyperbolic Sine', + 'cosh': 'Hyperbolic Cosine', + 'tanh': 'Hyperbolic Tangent', + 'atan': 'Inverse Tangent', + 'norm': 'Normalization', + 'mod': 'Modulo', + 'clip': 'Clipping', }; - + return displayNames[nodeType] || nodeType.charAt(0).toUpperCase() + nodeType.slice(1); }; diff --git a/src/python/pathsim_utils.py b/src/python/pathsim_utils.py index 114ba5e8..c2df81db 100644 --- a/src/python/pathsim_utils.py +++ b/src/python/pathsim_utils.py @@ -88,6 +88,27 @@ "fir": pathsim.blocks.FIR, } +math_blocks = { + "sin": pathsim.blocks.Sin, + "cos": pathsim.blocks.Cos, + "sqrt": pathsim.blocks.Sqrt, + "abs": pathsim.blocks.Abs, + "pow": pathsim.blocks.Pow, + "exp": pathsim.blocks.Exp, + "log": pathsim.blocks.Log, + "log10": pathsim.blocks.Log10, + "tan": pathsim.blocks.Tan, + "sinh": pathsim.blocks.Sinh, + "cosh": pathsim.blocks.Cosh, + "tanh": pathsim.blocks.Tanh, + "atan": pathsim.blocks.Atan, + "norm": pathsim.blocks.Norm, + "mod": pathsim.blocks.Mod, + "clip": pathsim.blocks.Clip, +} + +map_str_to_object.update(math_blocks) + map_str_to_event = { "ZeroCrossingDown": pathsim.events.ZeroCrossingDown, "ZeroCrossingUp": pathsim.events.ZeroCrossingUp, From 1e7cd0651f33dbbae48d4450e8ee359313b5a1cd Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 12:41:25 -0400 Subject: [PATCH 10/11] fixed ScheduleList temp --- src/python/custom_pathsim_blocks.py | 40 +++++++++++++---------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/python/custom_pathsim_blocks.py b/src/python/custom_pathsim_blocks.py index d38e9a19..31cadf60 100644 --- a/src/python/custom_pathsim_blocks.py +++ b/src/python/custom_pathsim_blocks.py @@ -99,12 +99,15 @@ def create_reset_events(self): else: raise ValueError("reset_times must be a single value or a list of times") - return [ - pathsim.blocks.Schedule(t_start=t, t_end=t, func_act=self.reset) - for t in reset_times - ] - # need https://github.com/milanofthe/pathsim/pull/66 - # return pathsim.events.ScheduleList(times_evt=reset_times, func_act=self.reset) + def func_act(_): + self.reset() + + # can be simplified after https://github.com/milanofthe/pathsim/pull/66 + event = pathsim.events.ScheduleList(times_evt=reset_times, func_act=func_act) + event.func_act = func_act + event.t_start = 0 + event.t_end = None + return [event] # BUBBLER SYSTEM @@ -212,25 +215,18 @@ def __init__( def _create_reset_events_one_vial( self, block, reset_times - ) -> list[pathsim.blocks.Schedule]: - events = [] - + ) -> pathsim.events.ScheduleList: def reset_itg(_): block.reset() - for t in reset_times: - events.append( - pathsim.blocks.Schedule(t_start=t, t_end=t, func_act=reset_itg) - ) - return events - - # need https://github.com/milanofthe/pathsim/pull/66 - # events = pathsim.events.ScheduleList( - # times_evt=reset_times, func_act=reset_itg - # ) - # return events + event = pathsim.events.ScheduleList(times_evt=reset_times, func_act=reset_itg) + # won't be needed after https://github.com/milanofthe/pathsim/pull/66 + event.func_act = reset_itg + event.t_start = 0 + event.t_end = None + return event - def create_reset_events(self) -> list[pathsim.events.Schedule]: + def create_reset_events(self) -> list[pathsim.events.ScheduleList]: """Create reset events for all vials based on the replacement times. Raises: @@ -258,7 +254,7 @@ def create_reset_events(self) -> list[pathsim.events.Schedule]: "reset_times must be a single value or a list with the same length as the number of vials" ) for i, vial in enumerate(self.vials): - events.extend(self._create_reset_events_one_vial(vial, reset_times[i])) + events.append(self._create_reset_events_one_vial(vial, reset_times[i])) return events From 5582651be3a83d8d69f2d5bcdbf1da511eb1c99e Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Fri, 8 Aug 2025 12:48:55 -0400 Subject: [PATCH 11/11] typing --- src/python/custom_pathsim_blocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/custom_pathsim_blocks.py b/src/python/custom_pathsim_blocks.py index 31cadf60..291b4dac 100644 --- a/src/python/custom_pathsim_blocks.py +++ b/src/python/custom_pathsim_blocks.py @@ -79,7 +79,7 @@ def __init__(self, initial_value=0.0, reset_times=None): super().__init__(initial_value=initial_value) self.reset_times = reset_times - def create_reset_events(self): + def create_reset_events(self) -> list[pathsim.events.ScheduleList]: """Create reset events for the integrator based on the reset times. Raises: