fullflow 0.1.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.
Files changed (49) hide show
  1. fullflow-0.1.0/PKG-INFO +34 -0
  2. fullflow-0.1.0/README.md +8 -0
  3. fullflow-0.1.0/pyproject.toml +52 -0
  4. fullflow-0.1.0/src/fullflow/.DS_Store +0 -0
  5. fullflow-0.1.0/src/fullflow/Exceptions/LookupExceptions.py +5 -0
  6. fullflow-0.1.0/src/fullflow/Exceptions/__init__.py +1 -0
  7. fullflow-0.1.0/src/fullflow/Solvers/SparsityDetector.py +267 -0
  8. fullflow-0.1.0/src/fullflow/Solvers/SteadyState.py +1592 -0
  9. fullflow-0.1.0/src/fullflow/Solvers/Transient.py +11 -0
  10. fullflow-0.1.0/src/fullflow/Solvers/__init__.py +3 -0
  11. fullflow-0.1.0/src/fullflow/System/Balance.py +190 -0
  12. fullflow-0.1.0/src/fullflow/System/Component.py +207 -0
  13. fullflow-0.1.0/src/fullflow/System/Components/Branches/CompressibleFlow.py +960 -0
  14. fullflow-0.1.0/src/fullflow/System/Components/Branches/ConvectionCoefficients.py +980 -0
  15. fullflow-0.1.0/src/fullflow/System/Components/Branches/DarcyWeisbach.py +259 -0
  16. fullflow-0.1.0/src/fullflow/System/Components/Branches/DischargeCoefficient.py +185 -0
  17. fullflow-0.1.0/src/fullflow/System/Components/Branches/FrictionFactors.py +181 -0
  18. fullflow-0.1.0/src/fullflow/System/Components/Branches/HeatTransfer.py +391 -0
  19. fullflow-0.1.0/src/fullflow/System/Components/Branches/Nozzles.py +41 -0
  20. fullflow-0.1.0/src/fullflow/System/Components/Branches/Pumps.py +318 -0
  21. fullflow-0.1.0/src/fullflow/System/Components/Branches/Regulators.py +89 -0
  22. fullflow-0.1.0/src/fullflow/System/Components/Branches/Turbines.py +282 -0
  23. fullflow-0.1.0/src/fullflow/System/Components/Branches/__init__.py +10 -0
  24. fullflow-0.1.0/src/fullflow/System/Components/Lookups/FluidUtilities.py +65 -0
  25. fullflow-0.1.0/src/fullflow/System/Components/Lookups/IdealGasLookups.py +790 -0
  26. fullflow-0.1.0/src/fullflow/System/Components/Lookups/MaterialLookups.py +280 -0
  27. fullflow-0.1.0/src/fullflow/System/Components/Lookups/PropellantLookups.py +383 -0
  28. fullflow-0.1.0/src/fullflow/System/Components/Lookups/RealFluidLookups.py +523 -0
  29. fullflow-0.1.0/src/fullflow/System/Components/Lookups/__init__.py +5 -0
  30. fullflow-0.1.0/src/fullflow/System/Components/Nodes/Boundaries.py +35 -0
  31. fullflow-0.1.0/src/fullflow/System/Components/Nodes/CombustionChambers.py +28 -0
  32. fullflow-0.1.0/src/fullflow/System/Components/Nodes/Junctions.py +406 -0
  33. fullflow-0.1.0/src/fullflow/System/Components/Nodes/Solids.py +94 -0
  34. fullflow-0.1.0/src/fullflow/System/Components/Nodes/Tanks.py +72 -0
  35. fullflow-0.1.0/src/fullflow/System/Components/Nodes/Volumes.py +79 -0
  36. fullflow-0.1.0/src/fullflow/System/Components/Nodes/__init__.py +6 -0
  37. fullflow-0.1.0/src/fullflow/System/Components/Sensors/PressureTransducers.py +24 -0
  38. fullflow-0.1.0/src/fullflow/System/Components/Sensors/Thermocouples.py +24 -0
  39. fullflow-0.1.0/src/fullflow/System/Components/Sensors/__init__.py +2 -0
  40. fullflow-0.1.0/src/fullflow/System/Components/__init__.py +4 -0
  41. fullflow-0.1.0/src/fullflow/System/Composition.py +164 -0
  42. fullflow-0.1.0/src/fullflow/System/Model.py +349 -0
  43. fullflow-0.1.0/src/fullflow/System/Network.py +546 -0
  44. fullflow-0.1.0/src/fullflow/System/State.py +412 -0
  45. fullflow-0.1.0/src/fullflow/System/__init__.py +7 -0
  46. fullflow-0.1.0/src/fullflow/Utilities/.DS_Store +0 -0
  47. fullflow-0.1.0/src/fullflow/Utilities/RocketCEAUtilities.py +182 -0
  48. fullflow-0.1.0/src/fullflow/Utilities/__init__.py +1 -0
  49. fullflow-0.1.0/src/fullflow/__init__.py +10 -0
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: fullflow
3
+ Version: 0.1.0
4
+ Summary: A fluid, thermal, and propulsion network simulation framework.
5
+ Keywords: fluid,flow,thermodynamics,heat-transfer,rocket,propulsion,simulation,network-analysis
6
+ Author: Saaketh Ramoju
7
+ Author-email: Saaketh Ramoju <skramoju@gmail.com>
8
+ License-Expression: GPL-3.0-or-later
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering
16
+ Requires-Dist: numpy>=2.4.6
17
+ Requires-Dist: rich>=15.0.0
18
+ Requires-Dist: rocketcea>=1.2.3
19
+ Requires-Dist: scipy>=1.17.1
20
+ Requires-Dist: thermoprop>=0.3.3
21
+ Requires-Python: >=3.11
22
+ Project-URL: Homepage, https://github.com/saakethramoju/FullFlow
23
+ Project-URL: Repository, https://github.com/saakethramoju/FullFlow
24
+ Project-URL: Issues, https://github.com/saakethramoju/FullFlow/issues
25
+ Description-Content-Type: text/markdown
26
+
27
+ # FullFlow
28
+
29
+ FullFlow is a fluid, thermal, and propulsion network simulation framework.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install fullflow
@@ -0,0 +1,8 @@
1
+ # FullFlow
2
+
3
+ FullFlow is a fluid, thermal, and propulsion network simulation framework.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install fullflow
@@ -0,0 +1,52 @@
1
+ [project]
2
+ name = "fullflow"
3
+ version = "0.1.0"
4
+ description = "A fluid, thermal, and propulsion network simulation framework."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "GPL-3.0-or-later"
8
+
9
+ authors = [
10
+ { name = "Saaketh Ramoju", email = "skramoju@gmail.com" }
11
+ ]
12
+
13
+ keywords = [
14
+ "fluid",
15
+ "flow",
16
+ "thermodynamics",
17
+ "heat-transfer",
18
+ "rocket",
19
+ "propulsion",
20
+ "simulation",
21
+ "network-analysis",
22
+ ]
23
+
24
+ classifiers = [
25
+ "Development Status :: 3 - Alpha",
26
+ "Intended Audience :: Science/Research",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Programming Language :: Python :: 3.13",
31
+ "Topic :: Scientific/Engineering",
32
+ ]
33
+
34
+ dependencies = [
35
+ "numpy>=2.4.6",
36
+ "rich>=15.0.0",
37
+ "rocketcea>=1.2.3",
38
+ "scipy>=1.17.1",
39
+ "thermoprop>=0.3.3",
40
+ ]
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/saakethramoju/FullFlow"
44
+ Repository = "https://github.com/saakethramoju/FullFlow"
45
+ Issues = "https://github.com/saakethramoju/FullFlow/issues"
46
+
47
+ [project.scripts]
48
+ fullflow = "fullflow:main"
49
+
50
+ [build-system]
51
+ requires = ["uv_build>=0.11.18,<0.12.0"]
52
+ build-backend = "uv_build"
Binary file
@@ -0,0 +1,5 @@
1
+ class InvalidThermoStateError(Exception):
2
+ pass
3
+
4
+ class InvalidMaterialPropertyError(Exception):
5
+ pass
@@ -0,0 +1 @@
1
+ from .LookupExceptions import *
@@ -0,0 +1,267 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+
5
+
6
+ class SparsityDetector:
7
+ """
8
+ Detect the Jacobian sparsity structure of a network residual system.
9
+
10
+ This class empirically determines which residual equations depend on
11
+ which iteration variables by perturbing each variable independently
12
+ using symmetric 3-point finite differencing and observing which
13
+ residuals change.
14
+
15
+ The resulting sparsity matrix can later be supplied to
16
+ scipy.optimize.least_squares via `jac_sparsity` to accelerate
17
+ finite-difference Jacobian estimation and sparse linear solves.
18
+
19
+ Detection Method
20
+ ----------------
21
+ For each iteration variable x_j:
22
+
23
+ 1) Evaluate residuals at:
24
+ x + h e_j
25
+ x - h e_j
26
+
27
+ 2) Compare both perturbed residual vectors against the baseline.
28
+
29
+ 3) Mark residual r_i as depending on x_j if either perturbation
30
+ changes r_i by more than `residual_tolerance`.
31
+
32
+ Notes
33
+ -----
34
+ - The detected sparsity is conservative:
35
+ dependencies are only added, never removed.
36
+
37
+ - Slightly overestimating sparsity is acceptable and safe.
38
+
39
+ - Underestimating sparsity can break Jacobian estimation and solver
40
+ convergence.
41
+
42
+ Parameters
43
+ ----------
44
+ relative_step : float
45
+ Relative perturbation size used for finite differencing.
46
+
47
+ Perturbation magnitude is computed approximately as:
48
+
49
+ h = relative_step * max(abs(x_j), 1.0)
50
+
51
+ Larger values improve robustness to numerical noise but may
52
+ incorrectly detect weak nonlinear couplings.
53
+
54
+ absolute_step : float
55
+ Minimum absolute perturbation size.
56
+
57
+ Prevents extremely small perturbations when x_j is near zero.
58
+
59
+ residual_tolerance : float
60
+ Threshold used to determine whether a residual changed after a
61
+ perturbation.
62
+
63
+ Residual changes smaller than this value are treated as numerical
64
+ noise and ignored.
65
+ """
66
+ def __init__(self, network):
67
+ self.network = network
68
+
69
+ def residual(self, x: np.ndarray) -> np.ndarray:
70
+ self.network.assign_iteration_values(list(x))
71
+ self.network.evaluate_states()
72
+ return np.array(self.network.residuals, dtype=float)
73
+
74
+ def detect(
75
+ self,
76
+ relative_step: float = 1e-6,
77
+ absolute_step: float = 1e-8,
78
+ residual_tolerance: float = 1e-10,
79
+ ) -> np.ndarray:
80
+
81
+ x0 = np.array(
82
+ self.network.iteration_values,
83
+ dtype=float,
84
+ )
85
+
86
+ self.network.evaluate_states()
87
+
88
+ r0 = np.array(
89
+ self.network.residuals,
90
+ dtype=float,
91
+ )
92
+
93
+ n_residuals = len(r0)
94
+ n_variables = len(x0)
95
+
96
+ sparsity = np.zeros(
97
+ (n_residuals, n_variables),
98
+ dtype=bool,
99
+ )
100
+
101
+ for j in range(n_variables):
102
+
103
+ h = relative_step * max(abs(x0[j]), 1.0)
104
+ h = max(h, absolute_step)
105
+
106
+ x_plus = x0.copy()
107
+ x_minus = x0.copy()
108
+
109
+ x_plus[j] += h
110
+ x_minus[j] -= h
111
+
112
+ r_plus = self.residual(x_plus)
113
+ r_minus = self.residual(x_minus)
114
+
115
+ changed_plus = np.abs(r_plus - r0) > residual_tolerance
116
+ changed_minus = np.abs(r_minus - r0) > residual_tolerance
117
+
118
+ sparsity[:, j] = changed_plus | changed_minus
119
+
120
+ self.network.assign_iteration_values(list(x0))
121
+ self.network.evaluate_states()
122
+
123
+ return sparsity
124
+
125
+ def _iteration_variable_labels(self) -> list[str]:
126
+
127
+ labels = []
128
+
129
+ def find_label(target):
130
+ for component in self.network.component_list:
131
+ for attr_name, attr_value in component.__dict__.items():
132
+ if attr_value is target:
133
+ return f"{component.name}.{attr_name}"
134
+
135
+ for balance in self.network.balance_list:
136
+ for attr_name, attr_value in balance.__dict__.items():
137
+ if attr_value is target:
138
+ return f"{balance.name}.{attr_name}"
139
+
140
+ return str(target)
141
+
142
+ for var in self.network.collect_all_iteration_variables():
143
+ labels.append(find_label(var))
144
+
145
+ return labels
146
+
147
+
148
+ def _residual_labels(self) -> list[str]:
149
+
150
+ labels = []
151
+
152
+ for component in self.network.component_list:
153
+ component_residuals = component.residuals
154
+
155
+ if isinstance(component_residuals, (list, tuple)):
156
+ for i in range(len(component_residuals)):
157
+ labels.append(f"{component.name}.residual[{i}]")
158
+ else:
159
+ labels.append(f"{component.name}.residual")
160
+
161
+ for balance in self.network.balance_list:
162
+ balance_residuals = balance.residuals
163
+
164
+ if isinstance(balance_residuals, (list, tuple)):
165
+ for i in range(len(balance_residuals)):
166
+ labels.append(f"{balance.name}.residual[{i}]")
167
+ else:
168
+ labels.append(f"{balance.name}.residual")
169
+
170
+ return labels
171
+
172
+ def plot(
173
+ self,
174
+ sparsity: np.ndarray,
175
+ figsize: tuple = (14, 10),
176
+ filename: str = "jacobian_sparsity.png",
177
+ ) -> None:
178
+
179
+ import matplotlib.pyplot as plt
180
+ import numpy as np
181
+
182
+ variable_labels = self._iteration_variable_labels()
183
+ residual_labels = self._residual_labels()
184
+
185
+ fig, ax = plt.subplots(figsize=figsize)
186
+
187
+ ax.spy(
188
+ sparsity,
189
+ markersize=8,
190
+ color="#D84135",
191
+ )
192
+
193
+ ax.set_title(
194
+ "Jacobian Sparsity Pattern",
195
+ fontsize=16,
196
+ pad=20,
197
+ )
198
+
199
+ ax.set_xlabel("Iteration Variables")
200
+ ax.set_ylabel("Residuals")
201
+
202
+ ax.set_xticks(np.arange(len(variable_labels)))
203
+ ax.set_yticks(np.arange(len(residual_labels)))
204
+
205
+ ax.set_xticklabels(
206
+ [f"x[{i}]" for i in range(len(variable_labels))],
207
+ rotation=90,
208
+ fontsize=8,
209
+ )
210
+
211
+ ax.set_yticklabels(
212
+ [
213
+ f"r[{i}] {label}"
214
+ for i, label in enumerate(residual_labels)
215
+ ],
216
+ fontsize=8,
217
+ )
218
+
219
+ # Gridlines centered on each matrix entry.
220
+ ax.set_xticks(
221
+ np.arange(-0.5, len(variable_labels), 1),
222
+ minor=True,
223
+ )
224
+
225
+ ax.set_yticks(
226
+ np.arange(-0.5, len(residual_labels), 1),
227
+ minor=True,
228
+ )
229
+
230
+ ax.grid(
231
+ which="minor",
232
+ color="#2a2a40",
233
+ linestyle="-",
234
+ linewidth=0.5,
235
+ )
236
+
237
+ ax.tick_params(
238
+ which="minor",
239
+ bottom=False,
240
+ left=False,
241
+ )
242
+
243
+ legend_text = "\n".join(
244
+ [
245
+ f"x[{i}] = {label}"
246
+ for i, label in enumerate(variable_labels)
247
+ ]
248
+ )
249
+
250
+ fig.text(
251
+ 1.02,
252
+ 0.5,
253
+ legend_text,
254
+ fontsize=8,
255
+ va="center",
256
+ family="monospace",
257
+ )
258
+
259
+ plt.tight_layout()
260
+
261
+ plt.savefig(
262
+ filename,
263
+ dpi=300,
264
+ bbox_inches="tight",
265
+ )
266
+
267
+ plt.close()