pyBondGraph 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/PKG-INFO +6 -3
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/README.md +5 -3
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph/__init__.py +7 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph/bondgraph.py +125 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph/core.py +10 -2
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph/elements.py +20 -10
- pybondgraph-0.4.0/pyBondGraph/numerics.py +57 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph.egg-info/PKG-INFO +6 -3
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph.egg-info/SOURCES.txt +1 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph.egg-info/requires.txt +1 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyproject.toml +1 -1
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/requirements.txt +2 -1
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/LICENSE +0 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph/sensors.py +0 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph/subbondgraph.py +0 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph.egg-info/dependency_links.txt +0 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/pyBondGraph.egg-info/top_level.txt +0 -0
- {pybondgraph-0.3.0 → pybondgraph-0.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyBondGraph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Modelling tool for linear bond graph systems in Python
|
|
5
5
|
Author-email: Matthias Panny <matthias.panny@mci.edu>
|
|
6
6
|
License-Expression: CC-BY-NC-SA-4.0
|
|
@@ -19,6 +19,7 @@ Requires-Dist: networkx>=3.5
|
|
|
19
19
|
Requires-Dist: numpy>=2.3.0
|
|
20
20
|
Requires-Dist: matplotlib>=3.10.3
|
|
21
21
|
Requires-Dist: control>=0.10.2
|
|
22
|
+
Requires-Dist: pythonfmu3>=0.3.4
|
|
22
23
|
Provides-Extra: streamlit
|
|
23
24
|
Requires-Dist: streamlit>=1.47.0; extra == "streamlit"
|
|
24
25
|
Requires-Dist: streamlit-flow-component>=1.6.1; extra == "streamlit"
|
|
@@ -29,6 +30,8 @@ Dynamic: license-file
|
|
|
29
30
|
|
|
30
31
|
The library allows users to construct bond graph models programmatically, automatically derive the governing equations, and analyze the resulting dynamic systems using tools from control theory.
|
|
31
32
|
|
|
33
|
+
`pyBondGraph` can also export FMUs [(Functional Mock-up Units)](https://fmi-standard.org/) based on FMI 3.0 for Model Exchange, enabling interoperability with other simulation tools such as Simulink.
|
|
34
|
+
|
|
32
35
|
Bond graphs provide a **domain-independent modeling framework** for physical systems. Using a unified representation of power exchange, the same modeling approach can be used for electrical, mechanical, hydraulic, and multi-domain systems.
|
|
33
36
|
|
|
34
37
|
---
|
|
@@ -112,6 +115,8 @@ bg.plot()
|
|
|
112
115
|
|
|
113
116
|
# derive system equations in linear state space form
|
|
114
117
|
A, B, C, D, x, n_states, n_inputs, n_outputs = bg.get_state_space()
|
|
118
|
+
|
|
119
|
+
bg.to_fmu("rc_filter")
|
|
115
120
|
```
|
|
116
121
|
|
|
117
122
|
Causality can also be assigned **manually** by passing a `Causality` value to `connect()`, or in **mixed mode** where some bonds are fixed and SCAP resolves the rest.
|
|
@@ -199,6 +204,4 @@ Bond graph modeling is particularly useful for:
|
|
|
199
204
|
|
|
200
205
|
# Planned Features
|
|
201
206
|
|
|
202
|
-
* **FMU Export**: export bond graph models as Functional Mock-up Units (FMI standard) for interoperability with Simulink, Dymola, OpenModelica, and other FMI-compliant tools
|
|
203
207
|
* **Nonlinear element support**: general nonlinear constitutive laws with Jacobian linearization
|
|
204
|
-
* **Convenience bridge to python-control**: `to_control_ss(params)` method wrapping the existing manual pattern
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
The library allows users to construct bond graph models programmatically, automatically derive the governing equations, and analyze the resulting dynamic systems using tools from control theory.
|
|
5
5
|
|
|
6
|
+
`pyBondGraph` can also export FMUs [(Functional Mock-up Units)](https://fmi-standard.org/) based on FMI 3.0 for Model Exchange, enabling interoperability with other simulation tools such as Simulink.
|
|
7
|
+
|
|
6
8
|
Bond graphs provide a **domain-independent modeling framework** for physical systems. Using a unified representation of power exchange, the same modeling approach can be used for electrical, mechanical, hydraulic, and multi-domain systems.
|
|
7
9
|
|
|
8
10
|
---
|
|
@@ -86,6 +88,8 @@ bg.plot()
|
|
|
86
88
|
|
|
87
89
|
# derive system equations in linear state space form
|
|
88
90
|
A, B, C, D, x, n_states, n_inputs, n_outputs = bg.get_state_space()
|
|
91
|
+
|
|
92
|
+
bg.to_fmu("rc_filter")
|
|
89
93
|
```
|
|
90
94
|
|
|
91
95
|
Causality can also be assigned **manually** by passing a `Causality` value to `connect()`, or in **mixed mode** where some bonds are fixed and SCAP resolves the rest.
|
|
@@ -173,6 +177,4 @@ Bond graph modeling is particularly useful for:
|
|
|
173
177
|
|
|
174
178
|
# Planned Features
|
|
175
179
|
|
|
176
|
-
* **
|
|
177
|
-
* **Nonlinear element support**: general nonlinear constitutive laws with Jacobian linearization
|
|
178
|
-
* **Convenience bridge to python-control**: `to_control_ss(params)` method wrapping the existing manual pattern
|
|
180
|
+
* **Nonlinear element support**: general nonlinear constitutive laws with Jacobian linearization
|
|
@@ -18,6 +18,10 @@ from .subbondgraph import SubBondGraph
|
|
|
18
18
|
|
|
19
19
|
from .bondgraph import BondGraph
|
|
20
20
|
|
|
21
|
+
from .numerics import to_numpy, to_control_ss
|
|
22
|
+
|
|
23
|
+
from .fmu_export import to_fmu
|
|
24
|
+
|
|
21
25
|
__all__ = [
|
|
22
26
|
"Bond",
|
|
23
27
|
"Causality",
|
|
@@ -40,4 +44,7 @@ __all__ = [
|
|
|
40
44
|
"SubBondGraph",
|
|
41
45
|
"CausalityError",
|
|
42
46
|
"DerivativeCausalityError",
|
|
47
|
+
"to_numpy",
|
|
48
|
+
"to_control_ss",
|
|
49
|
+
"to_fmu",
|
|
43
50
|
]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
3
5
|
import sympy as sp
|
|
4
6
|
import networkx as nx
|
|
5
7
|
import numpy as np
|
|
@@ -14,6 +16,8 @@ from .core import Causality, CausalityError, DerivativeCausalityError, Node, Sta
|
|
|
14
16
|
from .elements import SourceEffort, SourceFlow, Capacitor, Inductor, Resistor, Transformer, Gyrator, OneJunction, ZeroJunction
|
|
15
17
|
|
|
16
18
|
from .core import Port
|
|
19
|
+
from .numerics import to_numpy, to_control_ss
|
|
20
|
+
from .fmu_export import to_fmu
|
|
17
21
|
|
|
18
22
|
if TYPE_CHECKING:
|
|
19
23
|
from .subbondgraph import SubBondGraph
|
|
@@ -265,6 +269,127 @@ class BondGraph:
|
|
|
265
269
|
|
|
266
270
|
return A, B, C, D, sp.Matrix(self.state_vars), n_states, n_inputs, n_outputs
|
|
267
271
|
|
|
272
|
+
def get_substitution_dict(self, overrides: dict[sp.Symbol, float] | None = None) -> dict[sp.Symbol, float]:
|
|
273
|
+
"""Build a substitution dictionary from elements that have a ``numeric_value`` set.
|
|
274
|
+
|
|
275
|
+
Iterates over all :class:`ElementOnePort` and :class:`ElementTwoPort` elements
|
|
276
|
+
in the bond graph and maps each element's symbolic ``value`` to its ``numeric_value``.
|
|
277
|
+
Elements without a ``numeric_value`` (i.e. ``None``) are silently skipped.
|
|
278
|
+
|
|
279
|
+
Parameters
|
|
280
|
+
----------
|
|
281
|
+
overrides : dict[sp.Symbol, float] | None, optional
|
|
282
|
+
Additional or overriding entries merged into the result.
|
|
283
|
+
This is useful for parameter sweeps or for supplying values
|
|
284
|
+
that are not stored on the elements (e.g. external inputs).
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
dict[sp.Symbol, float]
|
|
289
|
+
Mapping from symbolic parameter to numeric value.
|
|
290
|
+
|
|
291
|
+
Raises
|
|
292
|
+
------
|
|
293
|
+
ValueError
|
|
294
|
+
If any element with a ``numeric_value`` would overwrite an
|
|
295
|
+
already-collected symbol with a *different* value (duplicate
|
|
296
|
+
symbols with the same numeric value are fine).
|
|
297
|
+
"""
|
|
298
|
+
subs: dict[sp.Symbol, float] = {}
|
|
299
|
+
|
|
300
|
+
for elem in self.elements:
|
|
301
|
+
|
|
302
|
+
if isinstance(elem, (ElementOnePort, ElementTwoPort)) and elem.numeric_value is not None:
|
|
303
|
+
if elem.value in subs and subs[elem.value] != elem.numeric_value:
|
|
304
|
+
raise ValueError(
|
|
305
|
+
f"Conflicting numeric values for symbol '{elem.value}': "
|
|
306
|
+
f"{subs[elem.value]} vs {elem.numeric_value} (element '{elem.name}')."
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
subs[elem.value] = elem.numeric_value
|
|
310
|
+
|
|
311
|
+
# update dict with manually provded overrides (if any)
|
|
312
|
+
if overrides:
|
|
313
|
+
subs.update(overrides)
|
|
314
|
+
|
|
315
|
+
return subs
|
|
316
|
+
|
|
317
|
+
def get_numeric_state_space(
|
|
318
|
+
self,
|
|
319
|
+
subs: dict[sp.Symbol, float] | None = None,
|
|
320
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
321
|
+
"""Return the numeric (numpy) state-space matrices ``(A, B, C, D)``.
|
|
322
|
+
|
|
323
|
+
This is a convenience wrapper around :meth:`get_state_space` that
|
|
324
|
+
substitutes numeric parameter values and converts the resulting
|
|
325
|
+
symbolic matrices to :class:`numpy.ndarray`.
|
|
326
|
+
|
|
327
|
+
Parameters
|
|
328
|
+
----------
|
|
329
|
+
subs : dict[sp.Symbol, float] | None, optional
|
|
330
|
+
Explicit substitution dictionary. If ``None``,
|
|
331
|
+
:meth:`get_substitution_dict` is called to collect the values
|
|
332
|
+
stored on the elements. If provided, it is used as-is (no
|
|
333
|
+
merging with element values).
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]
|
|
338
|
+
Numeric matrices ``(A, B, C, D)``.
|
|
339
|
+
|
|
340
|
+
Raises
|
|
341
|
+
------
|
|
342
|
+
ValueError
|
|
343
|
+
If the symbolic state-space cannot be computed, or if free
|
|
344
|
+
symbols remain after substitution (i.e. some parameters have
|
|
345
|
+
no numeric value).
|
|
346
|
+
"""
|
|
347
|
+
A, B, C, D, _x, _ns, _ni, _no = self.get_state_space()
|
|
348
|
+
|
|
349
|
+
if subs is None:
|
|
350
|
+
subs = self.get_substitution_dict()
|
|
351
|
+
|
|
352
|
+
# Check for remaining free symbols before conversion
|
|
353
|
+
free = set()
|
|
354
|
+
for M in (A, B, C, D):
|
|
355
|
+
free |= M.free_symbols
|
|
356
|
+
remaining = free - set(subs.keys())
|
|
357
|
+
|
|
358
|
+
if remaining:
|
|
359
|
+
raise ValueError(
|
|
360
|
+
f"The following symbols have no numeric value: {remaining}. "
|
|
361
|
+
f"Set numeric_value on the corresponding elements or pass them via the subs parameter."
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
return tuple(to_numpy(M, subs) for M in (A, B, C, D))
|
|
365
|
+
|
|
366
|
+
def to_control_ss(self):
|
|
367
|
+
"""Generate a control.StateSpace object from the bond graph's numeric state-space matrices.
|
|
368
|
+
|
|
369
|
+
Returns
|
|
370
|
+
-------
|
|
371
|
+
control.StateSpace
|
|
372
|
+
The :class:`control.StateSpace` object representing the bond graph's linear dynamics.
|
|
373
|
+
|
|
374
|
+
Raises
|
|
375
|
+
------
|
|
376
|
+
ValueError
|
|
377
|
+
Raises a value error through :meth:`get_numeric_state_space` if not all numerical values required are set beforehand in the bondgraph elements
|
|
378
|
+
"""
|
|
379
|
+
|
|
380
|
+
matrices = self.get_numeric_state_space()
|
|
381
|
+
return to_control_ss(*matrices)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def to_fmu(self, model_name: str = "BondGraphExport", dest: str | Path = ".", author_name: str = "MtP", description: str = "A bond graph model.", keep_slave_python_code: bool = False) -> None:
|
|
386
|
+
# if default class name is used, use the bondgraph name if it is set
|
|
387
|
+
if model_name == "BondGraphExport" and self.name:
|
|
388
|
+
model_name = self.name
|
|
389
|
+
|
|
390
|
+
to_fmu(self, dest=dest, class_name=model_name, author_name=author_name, description=description, keep_slave_python_code=keep_slave_python_code)
|
|
391
|
+
|
|
392
|
+
|
|
268
393
|
def add_subbondgraph(self, sub_bondgraph: SubBondGraph, instance_name: str | None = None, is_prefix: bool = True) -> Port:
|
|
269
394
|
"""Instantiate a SubBondGraph into this bond graph.
|
|
270
395
|
|
|
@@ -213,7 +213,7 @@ class ElementOnePort(Node, ABC):
|
|
|
213
213
|
This can also be just one equation.
|
|
214
214
|
"""
|
|
215
215
|
|
|
216
|
-
def __init__(self, name: str, value: str):
|
|
216
|
+
def __init__(self, name: str, value: str, numeric_value: float | None = None):
|
|
217
217
|
"""ABC for a one-port element. Acts as base class for all one-port elements like Inductor, Capacitor, Resistor, SourceEffort, SourceFlow.
|
|
218
218
|
Stores an associated bond and a symbolic value (real and positive) for its defining characteristics e.g. resistance, capacitance, etc.
|
|
219
219
|
|
|
@@ -223,12 +223,16 @@ class ElementOnePort(Node, ABC):
|
|
|
223
223
|
The name of the element. Will be shown on the bond graph plot.
|
|
224
224
|
value : str
|
|
225
225
|
The name of the value associated with the element, is internally used for creating a `sympy.Symbol`.
|
|
226
|
+
numeric_value : float | None, optional
|
|
227
|
+
Numeric value for the element parameter. If provided, it is used
|
|
228
|
+
when building substitution dictionaries for numeric evaluation.
|
|
226
229
|
"""
|
|
227
230
|
|
|
228
231
|
super().__init__(name)
|
|
229
232
|
self.value = sp.Symbol(
|
|
230
233
|
value, real=True, positive=True
|
|
231
234
|
) # Ensure value is a positive real number
|
|
235
|
+
self.numeric_value: float | None = numeric_value
|
|
232
236
|
self.bond: Bond = None # bond that connects this element to a bond graph
|
|
233
237
|
|
|
234
238
|
@property
|
|
@@ -246,7 +250,7 @@ class ElementTwoPort(Node, ABC):
|
|
|
246
250
|
Requires implementation of an `equations` property that returns the list of symbolic equations defining the element's behavior.
|
|
247
251
|
"""
|
|
248
252
|
|
|
249
|
-
def __init__(self, name: str, value: str):
|
|
253
|
+
def __init__(self, name: str, value: str, numeric_value: float | None = None):
|
|
250
254
|
"""ABC for a two-port element. Acts as base class for all two-port elements like Transformer, Gyrator.
|
|
251
255
|
Stores two associated bonds and a symbolic value (real and positive) for its defining characteristics e.g. conversion factor, ratio, etc.
|
|
252
256
|
|
|
@@ -256,10 +260,14 @@ class ElementTwoPort(Node, ABC):
|
|
|
256
260
|
The name of the element. Will be shown on the bond graph plot.
|
|
257
261
|
value : str
|
|
258
262
|
The name of the value associated with the element, is internally used for creating a `sympy.Symbol`.
|
|
263
|
+
numeric_value : float | None, optional
|
|
264
|
+
Numeric value for the element parameter. If provided, it is used
|
|
265
|
+
when building substitution dictionaries for numeric evaluation.
|
|
259
266
|
"""
|
|
260
267
|
|
|
261
268
|
super().__init__(name)
|
|
262
269
|
self.value = sp.Symbol(value, real=True, positive=True)
|
|
270
|
+
self.numeric_value: float | None = numeric_value
|
|
263
271
|
self.bond1: Bond = None # ElementOther --(bond1)--> ElementTwoPort
|
|
264
272
|
self.bond2: Bond = None # ElementTwoPort --(bond2)--> ElementOther
|
|
265
273
|
|
|
@@ -53,7 +53,7 @@ class Capacitor(ElementOnePort, StatefulElement):
|
|
|
53
53
|
A capacitance relates the effort of its port with the integral of its flow by a constant capacitance value.
|
|
54
54
|
"""
|
|
55
55
|
|
|
56
|
-
def __init__(self, name: str, value: str):
|
|
56
|
+
def __init__(self, name: str, value: str, numeric_value: float = None):
|
|
57
57
|
"""Create a linear compliance/capacitance element in the bond graph.
|
|
58
58
|
|
|
59
59
|
Parameters
|
|
@@ -62,8 +62,10 @@ class Capacitor(ElementOnePort, StatefulElement):
|
|
|
62
62
|
The name of the element. Forwarded to the `Node` base class.
|
|
63
63
|
value : str
|
|
64
64
|
The name of the element value, is internally used for creating a `sympy.Symbol`.
|
|
65
|
+
numeric_value : float, optional
|
|
66
|
+
The numeric value of the capacitance, used for numerical simulations.
|
|
65
67
|
"""
|
|
66
|
-
super().__init__(name, value)
|
|
68
|
+
super().__init__(name, value, numeric_value)
|
|
67
69
|
|
|
68
70
|
@property
|
|
69
71
|
def state_var(self) -> sp.Symbol:
|
|
@@ -102,7 +104,7 @@ class Inductor(ElementOnePort, StatefulElement):
|
|
|
102
104
|
An inductor relates the flow of its port with the integral of its effort by a constant inductance value.
|
|
103
105
|
"""
|
|
104
106
|
|
|
105
|
-
def __init__(self, name: str, value: str):
|
|
107
|
+
def __init__(self, name: str, value: str, numeric_value: float = None):
|
|
106
108
|
"""Create a linear inertia/inductance element in the bond graph.
|
|
107
109
|
|
|
108
110
|
Parameters
|
|
@@ -111,8 +113,10 @@ class Inductor(ElementOnePort, StatefulElement):
|
|
|
111
113
|
The name of the element. Forwarded to the `Node` base class.
|
|
112
114
|
value : str
|
|
113
115
|
The name of the element value, is internally used for creating a `sympy.Symbol`.
|
|
116
|
+
numeric_value : float, optional
|
|
117
|
+
The numeric value of the inductance, used for numerical simulations.
|
|
114
118
|
"""
|
|
115
|
-
super().__init__(name, value)
|
|
119
|
+
super().__init__(name, value, numeric_value)
|
|
116
120
|
|
|
117
121
|
@property
|
|
118
122
|
def state_var(self) -> sp.Symbol:
|
|
@@ -151,7 +155,7 @@ class Resistor(ElementOnePort):
|
|
|
151
155
|
A resistor relates the effort and flow of its port by a constant resistance value.
|
|
152
156
|
"""
|
|
153
157
|
|
|
154
|
-
def __init__(self, name: str, value: str):
|
|
158
|
+
def __init__(self, name: str, value: str, numeric_value: float = None):
|
|
155
159
|
"""Create a linear resistance element.
|
|
156
160
|
|
|
157
161
|
Parameters
|
|
@@ -160,8 +164,10 @@ class Resistor(ElementOnePort):
|
|
|
160
164
|
The name of the resistance. Forwarded to the `Node` base class.
|
|
161
165
|
value : str
|
|
162
166
|
The name of the resistance value, is internally used for creating a `sympy.Symbol`.
|
|
167
|
+
numeric_value : float, optional
|
|
168
|
+
The numeric value of the resistance, used for numerical simulations.
|
|
163
169
|
"""
|
|
164
|
-
super().__init__(name, value)
|
|
170
|
+
super().__init__(name, value, numeric_value)
|
|
165
171
|
|
|
166
172
|
@property
|
|
167
173
|
def equations(self) -> list[sp.Expr]:
|
|
@@ -190,7 +196,7 @@ class Transformer(ElementTwoPort):
|
|
|
190
196
|
A transformer relates the efforts of its two ports and the flows of its two ports by a constant ratio.
|
|
191
197
|
"""
|
|
192
198
|
|
|
193
|
-
def __init__(self, name: str, value: str):
|
|
199
|
+
def __init__(self, name: str, value: str, numeric_value: float = None):
|
|
194
200
|
"""Create a transformer element in the bond graph.
|
|
195
201
|
|
|
196
202
|
Parameters
|
|
@@ -199,8 +205,10 @@ class Transformer(ElementTwoPort):
|
|
|
199
205
|
The name of the element. Forwarded to the `Node` base class.
|
|
200
206
|
value : str
|
|
201
207
|
The name of the element value, is internally used for creating a `sympy.Symbol`.
|
|
208
|
+
numeric_value : float, optional
|
|
209
|
+
The numeric value of the transformer ratio, used for numerical simulations.
|
|
202
210
|
"""
|
|
203
|
-
super().__init__(name, value)
|
|
211
|
+
super().__init__(name, value, numeric_value)
|
|
204
212
|
|
|
205
213
|
@property
|
|
206
214
|
def equations(self) -> list[sp.Expr]:
|
|
@@ -242,7 +250,7 @@ class Gyrator(ElementTwoPort):
|
|
|
242
250
|
A gyrator relates the effort of one port with the flow of the other (and vice versa) by a constant ratio.
|
|
243
251
|
"""
|
|
244
252
|
|
|
245
|
-
def __init__(self, name: str, value: str):
|
|
253
|
+
def __init__(self, name: str, value: str, numeric_value: float = None):
|
|
246
254
|
"""Create a gyrator element in the bond graph.
|
|
247
255
|
|
|
248
256
|
Parameters
|
|
@@ -251,8 +259,10 @@ class Gyrator(ElementTwoPort):
|
|
|
251
259
|
The name of the element. Forwarded to the `Node` base class.
|
|
252
260
|
value : str
|
|
253
261
|
The name of the element value, is internally used for creating a `sympy.Symbol`.
|
|
262
|
+
numeric_value : float, optional
|
|
263
|
+
The numeric value of the gyrator ratio, used for numerical simulations.
|
|
254
264
|
"""
|
|
255
|
-
super().__init__(name, value)
|
|
265
|
+
super().__init__(name, value, numeric_value)
|
|
256
266
|
|
|
257
267
|
@property
|
|
258
268
|
def equations(self) -> list[sp.Expr]:
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sympy as sp
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def to_numpy(
|
|
6
|
+
M: sp.Matrix,
|
|
7
|
+
subs: dict[sp.Symbol, float],
|
|
8
|
+
) -> np.ndarray:
|
|
9
|
+
"""Convert a symbolic SymPy matrix to a numeric NumPy array.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
M : sp.Matrix
|
|
14
|
+
Symbolic matrix.
|
|
15
|
+
subs : dict[sp.Symbol, float]
|
|
16
|
+
Substitution dictionary mapping symbols to numeric values.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
np.ndarray
|
|
21
|
+
Float64 array.
|
|
22
|
+
"""
|
|
23
|
+
return np.array(M.subs(subs), dtype=np.float64)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def to_control_ss(A: sp.Matrix | np.ndarray, B: sp.Matrix | np.ndarray, C: sp.Matrix | np.ndarray, D: sp.Matrix | np.ndarray, subs: dict[sp.Symbol, float] | None = None):
|
|
27
|
+
"""Convert state-space matrices to a :class:`control.StateSpace` object.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
A, B, C, D : sp.Matrix or np.ndarray
|
|
32
|
+
State-space matrices (symbolic or already numeric).
|
|
33
|
+
subs : dict, optional
|
|
34
|
+
Substitution dictionary. If provided the symbolic matrices are
|
|
35
|
+
substituted and converted to NumPy arrays first.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
control.StateSpace
|
|
40
|
+
|
|
41
|
+
Raises
|
|
42
|
+
------
|
|
43
|
+
ImportError
|
|
44
|
+
If the ``control`` package is not installed.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
import control
|
|
48
|
+
except ImportError:
|
|
49
|
+
raise ImportError(
|
|
50
|
+
"python-control is required for to_control_ss(). "
|
|
51
|
+
"Install it with: pip install control"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if subs is not None:
|
|
55
|
+
A, B, C, D = (to_numpy(M, subs) for M in (A, B, C, D))
|
|
56
|
+
|
|
57
|
+
return control.ss(A, B, C, D)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyBondGraph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Modelling tool for linear bond graph systems in Python
|
|
5
5
|
Author-email: Matthias Panny <matthias.panny@mci.edu>
|
|
6
6
|
License-Expression: CC-BY-NC-SA-4.0
|
|
@@ -19,6 +19,7 @@ Requires-Dist: networkx>=3.5
|
|
|
19
19
|
Requires-Dist: numpy>=2.3.0
|
|
20
20
|
Requires-Dist: matplotlib>=3.10.3
|
|
21
21
|
Requires-Dist: control>=0.10.2
|
|
22
|
+
Requires-Dist: pythonfmu3>=0.3.4
|
|
22
23
|
Provides-Extra: streamlit
|
|
23
24
|
Requires-Dist: streamlit>=1.47.0; extra == "streamlit"
|
|
24
25
|
Requires-Dist: streamlit-flow-component>=1.6.1; extra == "streamlit"
|
|
@@ -29,6 +30,8 @@ Dynamic: license-file
|
|
|
29
30
|
|
|
30
31
|
The library allows users to construct bond graph models programmatically, automatically derive the governing equations, and analyze the resulting dynamic systems using tools from control theory.
|
|
31
32
|
|
|
33
|
+
`pyBondGraph` can also export FMUs [(Functional Mock-up Units)](https://fmi-standard.org/) based on FMI 3.0 for Model Exchange, enabling interoperability with other simulation tools such as Simulink.
|
|
34
|
+
|
|
32
35
|
Bond graphs provide a **domain-independent modeling framework** for physical systems. Using a unified representation of power exchange, the same modeling approach can be used for electrical, mechanical, hydraulic, and multi-domain systems.
|
|
33
36
|
|
|
34
37
|
---
|
|
@@ -112,6 +115,8 @@ bg.plot()
|
|
|
112
115
|
|
|
113
116
|
# derive system equations in linear state space form
|
|
114
117
|
A, B, C, D, x, n_states, n_inputs, n_outputs = bg.get_state_space()
|
|
118
|
+
|
|
119
|
+
bg.to_fmu("rc_filter")
|
|
115
120
|
```
|
|
116
121
|
|
|
117
122
|
Causality can also be assigned **manually** by passing a `Causality` value to `connect()`, or in **mixed mode** where some bonds are fixed and SCAP resolves the rest.
|
|
@@ -199,6 +204,4 @@ Bond graph modeling is particularly useful for:
|
|
|
199
204
|
|
|
200
205
|
# Planned Features
|
|
201
206
|
|
|
202
|
-
* **FMU Export**: export bond graph models as Functional Mock-up Units (FMI standard) for interoperability with Simulink, Dymola, OpenModelica, and other FMI-compliant tools
|
|
203
207
|
* **Nonlinear element support**: general nonlinear constitutive laws with Jacobian linearization
|
|
204
|
-
* **Convenience bridge to python-control**: `to_control_ss(params)` method wrapping the existing manual pattern
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|