Skip to content

Commit 52457f5

Browse files
docstrings for all functions
1 parent d0e5951 commit 52457f5

File tree

1 file changed

+182
-4
lines changed

1 file changed

+182
-4
lines changed

src/python/pathsim_utils.py

Lines changed: 182 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
"""
2+
Utilities for converting graph-based representations to PathSim simulations.
3+
4+
This module provides functionality to convert visual graph representations of simulation
5+
models into executable PathSim simulations. It handles the creation of blocks, connections,
6+
events, and solver configurations from JSON-like graph data structures.
7+
8+
The main workflow involves:
9+
1. Processing global variables and solver parameters
10+
2. Creating blocks from node data
11+
3. Establishing connections between blocks based on edges
12+
4. Setting up events and custom Python code execution
13+
5. Building the complete PathSim simulation model
14+
15+
Key mappings are provided for:
16+
- Block types (map_str_to_object): Maps string identifiers to PathSim block classes
17+
- Event types (map_str_to_event): Maps string identifiers to PathSim event classes
18+
- Solver types (NAME_TO_SOLVER): Maps string identifiers to PathSim solver classes
19+
"""
120
import math
221
import numpy as np
322
from pathsim import Simulation, Connection
@@ -146,14 +165,48 @@
146165

147166

148167
def find_node_by_id(node_id: str, nodes: list[dict]) -> dict:
168+
"""
169+
Find a node by its ID in a list of nodes.
170+
171+
Args:
172+
node_id: The ID of the node to find.
173+
nodes: A list of node dictionaries to search through.
174+
175+
Returns:
176+
The node dictionary with the matching ID, or None if not found.
177+
"""
149178
return next((node for node in nodes if node["id"] == node_id), None)
150179

151180

152181
def find_block_by_id(block_id: str, blocks: list[Block]) -> Block:
182+
"""
183+
Find a block by its ID in a list of blocks.
184+
185+
Args:
186+
block_id: The ID of the block to find.
187+
blocks: A list of Block objects to search through.
188+
189+
Returns:
190+
The Block object with the matching ID, or None if not found.
191+
"""
153192
return next((block for block in blocks if block.id == block_id), None)
154193

155194

156195
def make_global_variables(global_vars):
196+
"""
197+
Validate and execute global variable definitions to make them usable in the simulation.
198+
199+
Args:
200+
global_vars: A list of dictionaries containing variable definitions, where each
201+
dictionary has 'name' and 'value' keys.
202+
203+
Returns:
204+
dict: A namespace dictionary containing the global variables.
205+
206+
Raises:
207+
ValueError: If a variable name is invalid, is a Python keyword, or if there's
208+
an error evaluating the variable value.
209+
"""
157210
# Validate and exec global variables so that they are usable later in this script.
158211
# Return a namespace dictionary containing the global variables
159212
global_namespace = globals().copy()
@@ -193,6 +246,23 @@ def make_global_variables(global_vars):
193246

194247

195248
def make_solver_params(solver_prms, eval_namespace=None):
249+
"""
250+
Process and validate solver parameters from the graph data.
251+
252+
Args:
253+
solver_prms: Dictionary containing solver parameters including Solver type,
254+
simulation_duration, and other solver-specific parameters.
255+
eval_namespace: Optional namespace for evaluating parameter expressions.
256+
257+
Returns:
258+
tuple: A tuple containing:
259+
- solver_prms (dict): Processed solver parameters
260+
- extra_params (dict): Additional parameters for the solver
261+
- duration (float): Simulation duration
262+
263+
Raises:
264+
ValueError: If invalid parameter values are provided or if solver type is unknown.
265+
"""
196266
extra_params = solver_prms.pop("extra_params", "")
197267
if extra_params == "":
198268
extra_params = {}
@@ -295,6 +365,21 @@ def auto_event_construction(event_data: dict, eval_namespace: dict = None) -> Ev
295365
def get_parameters_for_event_class(
296366
event_class: type, event_data: dict, eval_namespace: dict = None
297367
):
368+
"""
369+
Extract and process parameters for an event class from event data.
370+
371+
Args:
372+
event_class: The event class type to create parameters for.
373+
event_data: Dictionary containing the event configuration data.
374+
eval_namespace: Optional namespace for evaluating expressions and executing functions.
375+
376+
Returns:
377+
dict: A dictionary of parameters ready to be passed to the event class constructor.
378+
379+
Raises:
380+
ValueError: If required parameters are missing, if function code execution fails,
381+
or if parameter evaluation fails.
382+
"""
298383
parameters_for_class = inspect.signature(event_class.__init__).parameters
299384

300385
# Create a local namespace for executing the event functions
@@ -345,6 +430,20 @@ def get_parameters_for_event_class(
345430

346431

347432
def get_parameters_for_block_class(block_class, node, eval_namespace):
433+
"""
434+
Extract and process parameters for a block class from node data.
435+
436+
Args:
437+
block_class: The block class type to create parameters for.
438+
node: Dictionary containing the node configuration data.
439+
eval_namespace: Namespace for evaluating parameter expressions.
440+
441+
Returns:
442+
dict: A dictionary of parameters ready to be passed to the block class constructor.
443+
444+
Raises:
445+
ValueError: If required parameters are missing or if parameter evaluation fails.
446+
"""
348447
parameters_for_class = inspect.signature(block_class.__init__).parameters
349448
parameters = {}
350449
for k, value in parameters_for_class.items():
@@ -374,6 +473,18 @@ def get_parameters_for_block_class(block_class, node, eval_namespace):
374473
def make_blocks(
375474
nodes: list[dict], eval_namespace: dict = None
376475
) -> tuple[list[Block], list[Event]]:
476+
"""
477+
Create Block objects from node data and collect any associated events.
478+
479+
Args:
480+
nodes: List of node dictionaries containing block configuration data.
481+
eval_namespace: Optional namespace for evaluating expressions.
482+
483+
Returns:
484+
tuple: A tuple containing:
485+
- blocks (list[Block]): List of created Block objects
486+
- events (list[Event]): List of events created by blocks (e.g., reset events)
487+
"""
377488
blocks, events = [], []
378489

379490
for node in nodes:
@@ -393,11 +504,16 @@ def get_input_index(block: Block, edge: dict, block_to_input_index: dict) -> int
393504
Get the input index for a block based on the edge data.
394505
395506
Args:
396-
block: The block object.
507+
block: The block object to get the input index for.
397508
edge: The edge dictionary containing source and target information.
509+
block_to_input_index: Dictionary mapping blocks to their current input index count.
398510
399511
Returns:
400-
The input index for the block.
512+
int: The input index for the block.
513+
514+
Raises:
515+
AssertionError: If the target block has multiple input ports but the connection
516+
method hasn't been implemented for that block type.
401517
"""
402518

403519
if edge["targetHandle"] is not None:
@@ -422,11 +538,16 @@ def get_output_index(block: Block, edge: dict) -> int:
422538
Get the output index for a block based on the edge data.
423539
424540
Args:
425-
block: The block object.
541+
block: The block object to get the output index for.
426542
edge: The edge dictionary containing source and target information.
427543
428544
Returns:
429-
The output index for the block.
545+
int: The output index for the block.
546+
547+
Raises:
548+
ValueError: If an invalid source handle is provided for a Splitter block.
549+
AssertionError: If the source block has multiple output ports but the connection
550+
method hasn't been implemented for that block type.
430551
"""
431552
if edge["sourceHandle"] is not None:
432553
if block._port_map_out:
@@ -456,6 +577,23 @@ def get_output_index(block: Block, edge: dict) -> int:
456577

457578

458579
def make_connections(nodes, edges, blocks) -> list[Connection]:
580+
"""
581+
Create PathSim Connection objects from nodes, edges, and blocks data.
582+
583+
This function processes the graph structure to create proper connections between blocks,
584+
handling special cases for scopes and different block types with multiple inputs/outputs.
585+
586+
Args:
587+
nodes: List of node dictionaries containing block information.
588+
edges: List of edge dictionaries defining connections between nodes.
589+
blocks: List of Block objects that have been created from the nodes.
590+
591+
Returns:
592+
list[Connection]: List of PathSim Connection objects linking block inputs and outputs.
593+
594+
Note:
595+
This function also handles labeling for Scope and Spectrum blocks automatically.
596+
"""
459597
# Create connections based on the sorted edges to match beta order
460598
connections_pathsim = []
461599

@@ -526,6 +664,21 @@ def make_events(events_data: list[dict], eval_namespace: dict = None) -> list[Ev
526664

527665

528666
def make_default_scope(nodes, blocks) -> tuple[Scope, list[Connection]]:
667+
"""
668+
Create a default Scope block that connects to all other blocks in the simulation.
669+
670+
This function creates a default scope when no explicit scope exists in the graph,
671+
ensuring that all block outputs are captured for visualization.
672+
673+
Args:
674+
nodes: List of node dictionaries containing block information (used for labels).
675+
blocks: List of Block objects to connect to the default scope.
676+
677+
Returns:
678+
tuple: A tuple containing:
679+
- scope_default (Scope): The created default Scope block
680+
- connections_pathsim (list[Connection]): List of connections from blocks to the scope
681+
"""
529682
scope_default = Scope(
530683
labels=[node["data"]["label"] for node in nodes],
531684
)
@@ -577,6 +730,31 @@ def make_var_name(node: dict) -> str:
577730

578731

579732
def make_pathsim_model(graph_data: dict) -> tuple[Simulation, float]:
733+
"""
734+
Create a complete PathSim simulation model from graph data.
735+
736+
This is the main function that orchestrates the creation of a PathSim simulation
737+
from a graph representation. It processes nodes, edges, solver parameters, global
738+
variables, events, and custom Python code to build a complete simulation model.
739+
740+
Args:
741+
graph_data: Dictionary containing the complete graph representation with keys:
742+
- nodes: List of node dictionaries representing blocks
743+
- edges: List of edge dictionaries representing connections
744+
- solverParams: Dictionary of solver configuration parameters
745+
- globalVariables: Dictionary of global variable definitions
746+
- events: List of event dictionaries (optional)
747+
- pythonCode: Custom Python code to execute (optional)
748+
749+
Returns:
750+
tuple: A tuple containing:
751+
- simulation (Simulation): The configured PathSim Simulation object
752+
- duration (float): The simulation duration
753+
754+
Raises:
755+
ValueError: If there are errors in processing any component of the graph data.
756+
Exception: If custom Python code execution fails.
757+
"""
580758
nodes = graph_data.get("nodes", [])
581759
edges = graph_data.get("edges", [])
582760
solver_prms = graph_data.get("solverParams", {})

0 commit comments

Comments
 (0)