From 0debe52ab6a514501c034b715ea16615bc36ed7d Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 12 Aug 2025 11:25:34 -0400 Subject: [PATCH 01/11] remove alerts --- src/App.jsx | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 6eed68e7..15b6f236 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -333,8 +333,6 @@ const DnDFlow = () => { await writable.write(JSON.stringify(graphData, null, 2)); await writable.close(); - // Success message - alert('Graph saved successfully!'); } catch (error) { if (error.name !== 'AbortError') { console.error('Error saving file:', error); @@ -357,8 +355,6 @@ const DnDFlow = () => { a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); - - alert('Graph downloaded successfully!'); } }; @@ -428,8 +424,6 @@ const DnDFlow = () => { setGlobalVariables(loadedGlobalVariables ?? []); setEvents(loadedEvents ?? []); setPythonCode(loadedPythonCode ?? "# Define your Python variables and functions here\n# Example:\n# my_variable = 42\n# def my_function(x):\n# return x * 2\n"); - - alert('Graph loaded successfully!'); } catch (error) { console.error('Error parsing file:', error); alert('Error reading file. Please make sure it\'s a valid JSON file.'); @@ -499,8 +493,6 @@ const DnDFlow = () => { setGlobalVariables(loadedGlobalVariables ?? []); setEvents(loadedEvents ?? []); setPythonCode(loadedPythonCode ?? "# Define your Python variables and functions here\n# Example:\n# my_variable = 42\n# def my_function(x):\n# return x * 2\n"); - - alert('Graph loaded successfully!'); } catch (error) { console.error('Error parsing file:', error); alert('Error reading file. Please make sure it\'s a valid JSON file.'); @@ -661,8 +653,6 @@ const DnDFlow = () => { const writable = await fileHandle.createWritable(); await writable.write(result.script); await writable.close(); - - alert('Python script generated and saved successfully!'); } catch (error) { if (error.name !== 'AbortError') { console.error('Error saving Python file:', error); @@ -681,8 +671,6 @@ const DnDFlow = () => { a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); - - alert('Python script generated and downloaded to your default downloads folder!'); } } else { alert(`Error generating Python script: ${result.error}`); @@ -733,13 +721,12 @@ const DnDFlow = () => { setCsvData(result.csv_data); setHtmlData(result.html); setActiveTab('results'); - alert('Pathsim simulation completed successfully! Check the Results tab.'); } else { alert(`Error running Pathsim simulation: ${result.error}`); } } catch (error) { console.error('Error:', error); - alert('Failed to run Pathsim simulation. Make sure the backend is running.'); + alert(`Failed to run Pathsim simulation. Make sure the backend is running. : ${error.message}`); } }; From 2d315bb99e0618c4676a8e6cf0fd103bb2dfd434 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 12 Aug 2025 11:27:09 -0400 Subject: [PATCH 02/11] removed AddNode --- src/App.jsx | 90 +---------------------------------------------------- 1 file changed, 1 insertion(+), 89 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 15b6f236..368da3ef 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -827,77 +827,6 @@ const DnDFlow = () => { })) ); }; - // Function to add a new node to the graph - const addNode = async () => { - // Get available node types from nodeTypes object - const availableTypes = Object.keys(nodeTypes); - - // Create options string for the prompt - const typeOptions = availableTypes.map((type, index) => `${index + 1}. ${type}`).join('\n'); - - const userInput = prompt( - `Select a node type by entering the number:\n\n${typeOptions}\n\nEnter your choice (1-${availableTypes.length}):` - ); - - // If user cancels the prompt - if (!userInput) { - return; - } - - // Parse the user input - const choiceIndex = parseInt(userInput) - 1; - - // Validate the choice - if (isNaN(choiceIndex) || choiceIndex < 0 || choiceIndex >= availableTypes.length) { - alert('Invalid choice. Please enter a number between 1 and ' + availableTypes.length); - return; - } - - const selectedType = availableTypes[choiceIndex]; - const newNodeId = nodeCounter.toString(); - - // Get default values and documentation for this node type (should be cached from preload) - let defaults = defaultValues[selectedType] || {}; - let docs = nodeDocumentation[selectedType] || { - html: '

No documentation available for this node type.

', - text: 'No documentation available for this node type.' - }; - - // Fallback: fetch if not cached (shouldn't happen normally) - if (!defaultValues[selectedType]) { - defaults = await fetchDefaultValues(selectedType); - setDefaultValues(prev => ({ - ...prev, - [selectedType]: defaults - })); - } - - if (!nodeDocumentation[selectedType]) { - docs = await fetchNodeDocumentation(selectedType); - setNodeDocumentation(prev => ({ - ...prev, - [selectedType]: docs - })); - } - - // Create node data with label and initialize all expected fields as empty strings - let nodeData = { label: `${selectedType} ${newNodeId}` }; - - // Initialize all expected parameters as empty strings - Object.keys(defaults).forEach(key => { - nodeData[key] = ''; - }); - - const newNode = { - id: newNodeId, - type: selectedType, - position: { x: 200 + nodes.length * 50, y: 200 }, - data: nodeData, - }; - - setNodes((nds) => [...nds, newNode]); - setNodeCounter((count) => count + 1); - }; // Function to pop context menu when right-clicking on a node const onNodeContextMenu = useCallback( @@ -1274,23 +1203,6 @@ const DnDFlow = () => { > Delete Node - -
- Keyboard Shortcuts:
- Ctrl+C: Copy selected node
- Ctrl+V: Paste copied node
- Ctrl+D: Duplicate selected node
- Del/Backspace: Delete selection
- Right-click: Context menu -
+ {showKeyboardShortcuts && ( +
+
+ Keyboard Shortcuts: + +
+ Ctrl+C: Copy selected node
+ Ctrl+V: Paste copied node
+ Ctrl+D: Duplicate selected node
+ Del/Backspace: Delete selection
+ Right-click: Context menu +
+ )} From 5b5deab487b8b3daa329c7e5a02c0e246c564b8e Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 12 Aug 2025 11:36:35 -0400 Subject: [PATCH 04/11] New graph instead of Reset Graph --- src/App.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.jsx b/src/App.jsx index 1d1cb58d..1b9e42a5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1253,7 +1253,7 @@ const DnDFlow = () => { }} onClick={resetGraph} > - Reset Graph + New graph From f77c2284bdce2cfb9e2f2ad9001a52b7dfa17845 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 12 Aug 2025 13:12:41 -0400 Subject: [PATCH 08/11] more solvers --- src/App.jsx | 26 ++++++++++++++++++++++++++ src/python/pathsim_utils.py | 26 +++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/App.jsx b/src/App.jsx index 9702d439..44e114ff 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1555,7 +1555,33 @@ const DnDFlow = () => { > + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/python/pathsim_utils.py b/src/python/pathsim_utils.py index cc56ca51..a5e1db42 100644 --- a/src/python/pathsim_utils.py +++ b/src/python/pathsim_utils.py @@ -37,8 +37,32 @@ from flask import jsonify import inspect - NAME_TO_SOLVER = { + "RK4": pathsim.solvers.RK4, + "RKBS32": pathsim.solvers.RKBS32, + "RKCK54": pathsim.solvers.RKCK54, + "RKDP54": pathsim.solvers.RKDP54, + "RKDP87": pathsim.solvers.RKDP87, + "RKF45": pathsim.solvers.RKF45, + "RKF78": pathsim.solvers.RKF78, + "RKV65": pathsim.solvers.RKV65, + "BDF": pathsim.solvers.BDF, + "EUF": pathsim.solvers.EUF, + "EUB": pathsim.solvers.EUB, + "GEAR21": pathsim.solvers.GEAR21, + "GEAR32": pathsim.solvers.GEAR32, + "GEAR43": pathsim.solvers.GEAR43, + "GEAR54": pathsim.solvers.GEAR54, + "GEAR52A": pathsim.solvers.GEAR52A, + "DIRK2": pathsim.solvers.DIRK2, + "DIRK3": pathsim.solvers.DIRK3, + "ESDIRK32": pathsim.solvers.ESDIRK32, + "ESDIRK4": pathsim.solvers.ESDIRK4, + "ESDIRK43": pathsim.solvers.ESDIRK43, + "ESDIRK54": pathsim.solvers.ESDIRK54, + "ESDIRK85": pathsim.solvers.ESDIRK85, + "SteadyState": pathsim.solvers.SteadyState, + "SSPRK34": pathsim.solvers.SSPRK34, "SSPRK22": pathsim.solvers.SSPRK22, "SSPRK33": pathsim.solvers.SSPRK33, "RKF21": pathsim.solvers.RKF21, From cbb46d90d5a1a2f81ef71fc28de4d3d982320510 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 12 Aug 2025 13:33:57 -0400 Subject: [PATCH 09/11] pass and execute python code when exporting to python --- src/App.jsx | 1 + src/python/convert_to_python.py | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 44e114ff..cd95b159 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -622,6 +622,7 @@ const DnDFlow = () => { nodeCounter, solverParams, globalVariables, + pythonCode, events }; diff --git a/src/python/convert_to_python.py b/src/python/convert_to_python.py index 9dc15c66..446bbaa1 100644 --- a/src/python/convert_to_python.py +++ b/src/python/convert_to_python.py @@ -77,8 +77,22 @@ def make_edge_data(data: dict) -> list[dict]: data = data.copy() # we need the namespace since we call make_blocks - namespace = make_global_variables(data["globalVariables"]) - blocks, _ = make_blocks(data["nodes"], eval_namespace=namespace) + + global_vars = data.get("globalVariables", {}) + + # Get the global variables namespace to use in eval calls + global_namespace = make_global_variables(global_vars) + + # Create a combined namespace that includes built-in functions and global variables + eval_namespace = globals().copy() + eval_namespace.update(global_namespace) + + # Execute python code first to define any variables that blocks might need + python_code = data.get("pythonCode", "") + if python_code: + exec(python_code, eval_namespace) + + blocks, _ = make_blocks(data["nodes"], eval_namespace=eval_namespace) # Process each node and its sorted incoming edges to create connections block_to_input_index = {b: 0 for b in blocks} From 6fc03aef8f80fb5eec61769dece18804c862af65 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 12 Aug 2025 13:48:03 -0400 Subject: [PATCH 10/11] fixed default values for solver --- src/App.jsx | 75 +++++++++++-------------------------- src/python/pathsim_utils.py | 6 ++- 2 files changed, 26 insertions(+), 55 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index cd95b159..6b62d761 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -28,6 +28,19 @@ import { createFunctionNode } from './components/nodes/FunctionNode.jsx'; // * Declaring variables * +// Default solver parameters +const DEFAULT_SOLVER_PARAMS = { + dt: '0.01', + dt_min: '1e-16', + dt_max: '', + Solver: 'SSPRK22', + tolerance_fpi: '1e-10', + iterations_max: '200', + log: 'true', + simulation_duration: '10.0', + extra_params: '{}' +}; + // Defining initial nodes and edges. In the data section, we have label, but also parameters specific to the node. const initialNodes = []; const initialEdges = []; @@ -78,17 +91,7 @@ const DnDFlow = () => { // Solver parameters state - const [solverParams, setSolverParams] = useState({ - dt: '0.01', - dt_min: '1e-6', - dt_max: '1.0', - Solver: 'SSPRK22', - tolerance_fpi: '1e-6', - iterations_max: '100', - log: 'true', - simulation_duration: '50.0', - extra_params: '{}' - }); + const [solverParams, setSolverParams] = useState(DEFAULT_SOLVER_PARAMS); // Global variables state const [globalVariables, setGlobalVariables] = useState([]); @@ -411,17 +414,7 @@ const DnDFlow = () => { setEdges(loadedEdges || []); setSelectedNode(null); setNodeCounter(loadedNodeCounter ?? loadedNodes.length); - setSolverParams(loadedSolverParams ?? { - dt: '0.01', - dt_min: '1e-6', - dt_max: '1.0', - Solver: 'SSPRK22', - tolerance_fpi: '1e-6', - iterations_max: '100', - log: 'true', - simulation_duration: '50.0', - extra_params: '{}' - }); + setSolverParams(loadedSolverParams ?? DEFAULT_SOLVER_PARAMS); setGlobalVariables(loadedGlobalVariables ?? []); setEvents(loadedEvents ?? []); setPythonCode(loadedPythonCode ?? "# Define your Python variables and functions here\n# Example:\n# my_variable = 42\n# def my_function(x):\n# return x * 2\n"); @@ -480,17 +473,7 @@ const DnDFlow = () => { setEdges(loadedEdges || []); setSelectedNode(null); setNodeCounter(loadedNodeCounter ?? loadedNodes.length); - setSolverParams(loadedSolverParams ?? { - dt: '0.01', - dt_min: '1e-6', - dt_max: '1.0', - Solver: 'SSPRK22', - tolerance_fpi: '1e-6', - iterations_max: '100', - log: 'true', - simulation_duration: '50.0', - extra_params: '{}' - }); + setSolverParams(loadedSolverParams ?? DEFAULT_SOLVER_PARAMS); setGlobalVariables(loadedGlobalVariables ?? []); setEvents(loadedEvents ?? []); setPythonCode(loadedPythonCode ?? "# Define your Python variables and functions here\n# Example:\n# my_variable = 42\n# def my_function(x):\n# return x * 2\n"); @@ -515,17 +498,7 @@ const DnDFlow = () => { setEdges(initialEdges); setSelectedNode(null); setNodeCounter(0); - setSolverParams({ - dt: '0.01', - dt_min: '1e-6', - dt_max: '1.0', - Solver: 'SSPRK22', - tolerance_fpi: '1e-6', - iterations_max: '100', - log: 'true', - simulation_duration: '50.0', - extra_params: '{}' - }); + setSolverParams(DEFAULT_SOLVER_PARAMS); setGlobalVariables([]); }; const downloadCsv = async () => { @@ -1476,7 +1449,6 @@ const DnDFlow = () => { color: '#ffffff', fontSize: '14px' }} - placeholder="0.01" /> @@ -1502,7 +1474,6 @@ const DnDFlow = () => { color: '#ffffff', fontSize: '14px' }} - placeholder="1e-6" /> @@ -1528,7 +1499,6 @@ const DnDFlow = () => { color: '#ffffff', fontSize: '14px' }} - placeholder="1.0" /> @@ -1608,7 +1578,6 @@ const DnDFlow = () => { color: '#ffffff', fontSize: '14px' }} - placeholder="1e-6" /> @@ -1634,7 +1603,6 @@ const DnDFlow = () => { color: '#ffffff', fontSize: '14px' }} - placeholder="100" /> @@ -1660,7 +1628,6 @@ const DnDFlow = () => { color: '#ffffff', fontSize: '14px' }} - placeholder="50.0" /> @@ -1744,11 +1711,11 @@ const DnDFlow = () => { // Reset to default values setSolverParams({ dt: '0.01', - dt_min: '1e-6', - dt_max: '1.0', + dt_min: '1e-16', + dt_max: '', Solver: 'SSPRK22', - tolerance_fpi: '1e-6', - iterations_max: '100', + tolerance_fpi: '1e-10', + iterations_max: '200', log: 'true', simulation_duration: '50.0' }); diff --git a/src/python/pathsim_utils.py b/src/python/pathsim_utils.py index a5e1db42..51b0208c 100644 --- a/src/python/pathsim_utils.py +++ b/src/python/pathsim_utils.py @@ -203,7 +203,11 @@ def make_solver_params(solver_prms, eval_namespace=None): for k, v in solver_prms.items(): if k not in ["Solver", "log"]: try: - solver_prms[k] = eval(v, eval_namespace) + if v == "": + # TODO get the default from pathsim._constants + solver_prms[k] = None + else: + solver_prms[k] = eval(v, eval_namespace) except Exception as e: return jsonify( {"error": f"Invalid value for {k}: {v}. Error: {str(e)}"} From 8a6926a23a8470eea9593546a6e9ac84b8ab09bb Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 12 Aug 2025 13:48:20 -0400 Subject: [PATCH 11/11] more examples --- example_graphs/bouncing_ball.json | 167 +++++++++++++++++ example_graphs/pendulum.json | 236 ++++++++++++++++++++++++ example_graphs/thermostat.json | 288 ++++++++++++++++++++++++++++++ 3 files changed, 691 insertions(+) create mode 100644 example_graphs/bouncing_ball.json create mode 100644 example_graphs/pendulum.json create mode 100644 example_graphs/thermostat.json diff --git a/example_graphs/bouncing_ball.json b/example_graphs/bouncing_ball.json new file mode 100644 index 00000000..b679a4d6 --- /dev/null +++ b/example_graphs/bouncing_ball.json @@ -0,0 +1,167 @@ +{ + "nodes": [ + { + "id": "0", + "type": "constant", + "position": { + "x": 219.5, + "y": 219.5 + }, + "data": { + "label": "constant 0", + "nodeColor": "#DDE6ED", + "value": "-g" + }, + "measured": { + "width": 206, + "height": 54 + }, + "selected": false, + "dragging": false + }, + { + "id": "1", + "type": "integrator", + "position": { + "x": 367.8036110436579, + "y": 330.00000000000006 + }, + "data": { + "label": "integrator_v", + "nodeColor": "#DDE6ED", + "initial_value": "v0", + "reset_times": "" + }, + "measured": { + "width": 200, + "height": 48 + }, + "selected": false, + "dragging": false + }, + { + "id": "2", + "type": "integrator", + "position": { + "x": 639.8036110436578, + "y": 322.00000000000006 + }, + "data": { + "label": "integrator_x", + "nodeColor": "#DDE6ED", + "initial_value": "x0", + "reset_times": "" + }, + "measured": { + "width": 200, + "height": 48 + }, + "selected": false, + "dragging": false + }, + { + "id": "3", + "type": "scope", + "position": { + "x": 903.7342215603948, + "y": 264.84964717890483 + }, + "data": { + "label": "scope 3", + "nodeColor": "#DDE6ED", + "labels": "", + "sampling_rate": "", + "t_wait": "" + }, + "measured": { + "width": 120, + "height": 140 + }, + "selected": false, + "dragging": false + } + ], + "edges": [ + { + "id": "e0-1", + "source": "0", + "target": "1", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e1-2", + "source": "1", + "target": "2", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e2-3", + "source": "2", + "target": "3", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + } + ], + "nodeCounter": 4, + "solverParams": { + "dt": "0.01", + "dt_min": "1e-16", + "dt_max": "0.04", + "Solver": "RKBS32", + "tolerance_fpi": "1e-16", + "iterations_max": "100", + "log": "true", + "simulation_duration": "10", + "extra_params": "{\"tolerance_lte_rel\": 1e-5, \"tolerance_lte_abs\": 1e-7}" + }, + "globalVariables": [], + "events": [ + { + "name": "bounce", + "type": "ZeroCrossing", + "func_evt": "def func_evt(t):\n *_, x = integrator_x_2() #get block outputs and states\n return x", + "func_act": "def func_act(t):\n *_, x = integrator_x_2()\n *_, v = integrator_v_1()\n integrator_x_2.engine.set(abs(x))\n integrator_v_1.engine.set(-b*v)", + "tolerance": "1e-8", + "id": 1755015764685 + } + ], + "pythonCode": "#gravitational acceleration\ng = 9.81\n\n#elasticity of bounce\nb = 0.9\n\n#initial conditions\nx0, v0 = 1, 5" +} \ No newline at end of file diff --git a/example_graphs/pendulum.json b/example_graphs/pendulum.json new file mode 100644 index 00000000..5137e083 --- /dev/null +++ b/example_graphs/pendulum.json @@ -0,0 +1,236 @@ +{ + "nodes": [ + { + "id": "0", + "type": "integrator", + "position": { + "x": 31.949874679482008, + "y": 259.0878749359728 + }, + "data": { + "label": "In2", + "nodeColor": "#DDE6ED", + "initial_value": "phi0", + "reset_times": "" + }, + "measured": { + "width": 200, + "height": 48 + }, + "selected": false, + "dragging": false + }, + { + "id": "1", + "type": "integrator", + "position": { + "x": -57.80185604593987, + "y": 375.7296757198792 + }, + "data": { + "label": "In1", + "nodeColor": "#DDE6ED", + "initial_value": "omega0", + "reset_times": "" + }, + "measured": { + "width": 200, + "height": 48 + }, + "selected": false, + "dragging": false + }, + { + "id": "2", + "type": "amplifier", + "position": { + "x": 668.5474040864929, + "y": 284.57731162361034 + }, + "data": { + "label": "amp", + "nodeColor": "#DDE6ED", + "gain": "-g/l" + }, + "measured": { + "width": 90, + "height": 80 + }, + "selected": false, + "dragging": false + }, + { + "id": "3", + "type": "function", + "position": { + "x": 347.434213365941, + "y": 292.6194354154026 + }, + "data": { + "label": "function 3", + "nodeColor": "#DDE6ED", + "inputCount": 1, + "outputCount": 1, + "func": "np.sin" + }, + "measured": { + "width": 200, + "height": 80 + }, + "selected": false, + "dragging": false + }, + { + "id": "4", + "type": "scope", + "position": { + "x": 349.12259412272545, + "y": 495.40186575788914 + }, + "data": { + "label": "scope 4", + "nodeColor": "#DDE6ED", + "labels": "[ \"angle\", \"angular velocity\"]", + "sampling_rate": "", + "t_wait": "" + }, + "measured": { + "width": 120, + "height": 140 + }, + "selected": false, + "dragging": false + } + ], + "edges": [ + { + "id": "e1-0", + "source": "1", + "target": "0", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e1-4", + "source": "1", + "target": "4", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e0-3-to_target-0", + "source": "0", + "target": "3", + "sourceHandle": null, + "targetHandle": "target-0", + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e0-4", + "source": "0", + "target": "4", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e3-2-from_source-0", + "source": "3", + "target": "2", + "sourceHandle": "source-0", + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e2-1", + "source": "2", + "target": "1", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + } + ], + "nodeCounter": 5, + "solverParams": { + "dt": "0.1", + "dt_min": "1e-6", + "dt_max": "1.0", + "Solver": "RKCK54", + "tolerance_fpi": "1e-6", + "iterations_max": "100", + "log": "true", + "simulation_duration": "15", + "extra_params": "{\"tolerance_lte_rel\":1e-6, \"tolerance_lte_abs\": 1e-8}" + }, + "globalVariables": [], + "events": [], + "pythonCode": "import numpy as np\n#initial angle and angular velocity\nphi0, omega0 = 0.9*np.pi, 0\n\n#parameters (gravity, length)\ng, l = 9.81, 1" +} \ No newline at end of file diff --git a/example_graphs/thermostat.json b/example_graphs/thermostat.json new file mode 100644 index 00000000..9270319a --- /dev/null +++ b/example_graphs/thermostat.json @@ -0,0 +1,288 @@ +{ + "nodes": [ + { + "id": "0", + "type": "constant", + "position": { + "x": 362.0580206745773, + "y": 113.03456573089142 + }, + "data": { + "label": "Heater", + "nodeColor": "#DDE6ED", + "value": "H" + }, + "measured": { + "width": 206, + "height": 54 + }, + "selected": false, + "dragging": false + }, + { + "id": "1", + "type": "constant", + "position": { + "x": 358.8241979165283, + "y": 278.1947482777457 + }, + "data": { + "label": "ambient", + "nodeColor": "#DDE6ED", + "value": "a*Ta" + }, + "measured": { + "width": 206, + "height": 54 + }, + "selected": false, + "dragging": false + }, + { + "id": "2", + "type": "adder", + "position": { + "x": 701.2462888781952, + "y": 219.1770926972872 + }, + "data": { + "label": "adder 2", + "nodeColor": "#DDE6ED", + "operations": "" + }, + "measured": { + "width": 64, + "height": 64 + }, + "selected": false, + "dragging": false + }, + { + "id": "3", + "type": "integrator", + "position": { + "x": 848.807989889327, + "y": 225.51709663815075 + }, + "data": { + "label": "integ", + "nodeColor": "#DDE6ED", + "initial_value": "Ta", + "reset_times": "" + }, + "measured": { + "width": 200, + "height": 48 + }, + "selected": false, + "dragging": false + }, + { + "id": "4", + "type": "amplifier_reverse", + "position": { + "x": 796.3023843801966, + "y": 344.45184195466436 + }, + "data": { + "label": "amp", + "nodeColor": "#DDE6ED", + "gain": "-a" + }, + "measured": { + "width": 90, + "height": 80 + }, + "selected": false, + "dragging": false + }, + { + "id": "5", + "type": "scope", + "position": { + "x": 1157.8007854929265, + "y": 66.31785200000635 + }, + "data": { + "label": "scope 5", + "nodeColor": "#DDE6ED", + "labels": "", + "sampling_rate": "", + "t_wait": "" + }, + "measured": { + "width": 120, + "height": 140 + }, + "selected": false, + "dragging": false + } + ], + "edges": [ + { + "id": "e0-2", + "source": "0", + "target": "2", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e1-2", + "source": "1", + "target": "2", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e2-3", + "source": "2", + "target": "3", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e4-2", + "source": "4", + "target": "2", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e3-4", + "source": "3", + "target": "4", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e3-5", + "source": "3", + "target": "5", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + }, + { + "id": "e0-5", + "source": "0", + "target": "5", + "sourceHandle": null, + "targetHandle": null, + "type": "smoothstep", + "data": {}, + "style": { + "strokeWidth": 2, + "stroke": "#ECDFCC" + }, + "markerEnd": { + "type": "arrowclosed", + "width": 20, + "height": 20, + "color": "#ECDFCC" + } + } + ], + "nodeCounter": 6, + "solverParams": { + "dt": "0.01", + "dt_min": "1e-6", + "dt_max": "0.05", + "Solver": "SSPRK22", + "tolerance_fpi": "1e-6", + "iterations_max": "100", + "log": "true", + "simulation_duration": "30", + "extra_params": "{}" + }, + "globalVariables": [], + "events": [ + { + "name": "heater_off", + "type": "ZeroCrossingUp", + "func_evt": "def func_evt(t):\n *_, x = integ_3()\n return x - Kp", + "func_act": "def func_act(t):\n heater_0.off()", + "tolerance": "1e-8", + "id": 1755016407956 + }, + { + "name": "heater_on", + "type": "ZeroCrossingDown", + "func_evt": "def func_evt(t):\n *_, x = integ_3()\n return x - Km", + "func_act": "def func_act(t):\n heater_0.on()", + "tolerance": "1e-8", + "id": 1755016519588 + } + ], + "pythonCode": "a = 0.3 #thermal capacity of room\nTa = 10 #ambient temperature\nH = 5 #heater power\nKp = 25 #upper temperature threshold\nKm = 23 #lower temperature threshold" +} \ No newline at end of file