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.
- fullflow-0.1.0/PKG-INFO +34 -0
- fullflow-0.1.0/README.md +8 -0
- fullflow-0.1.0/pyproject.toml +52 -0
- fullflow-0.1.0/src/fullflow/.DS_Store +0 -0
- fullflow-0.1.0/src/fullflow/Exceptions/LookupExceptions.py +5 -0
- fullflow-0.1.0/src/fullflow/Exceptions/__init__.py +1 -0
- fullflow-0.1.0/src/fullflow/Solvers/SparsityDetector.py +267 -0
- fullflow-0.1.0/src/fullflow/Solvers/SteadyState.py +1592 -0
- fullflow-0.1.0/src/fullflow/Solvers/Transient.py +11 -0
- fullflow-0.1.0/src/fullflow/Solvers/__init__.py +3 -0
- fullflow-0.1.0/src/fullflow/System/Balance.py +190 -0
- fullflow-0.1.0/src/fullflow/System/Component.py +207 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/CompressibleFlow.py +960 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/ConvectionCoefficients.py +980 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/DarcyWeisbach.py +259 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/DischargeCoefficient.py +185 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/FrictionFactors.py +181 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/HeatTransfer.py +391 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/Nozzles.py +41 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/Pumps.py +318 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/Regulators.py +89 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/Turbines.py +282 -0
- fullflow-0.1.0/src/fullflow/System/Components/Branches/__init__.py +10 -0
- fullflow-0.1.0/src/fullflow/System/Components/Lookups/FluidUtilities.py +65 -0
- fullflow-0.1.0/src/fullflow/System/Components/Lookups/IdealGasLookups.py +790 -0
- fullflow-0.1.0/src/fullflow/System/Components/Lookups/MaterialLookups.py +280 -0
- fullflow-0.1.0/src/fullflow/System/Components/Lookups/PropellantLookups.py +383 -0
- fullflow-0.1.0/src/fullflow/System/Components/Lookups/RealFluidLookups.py +523 -0
- fullflow-0.1.0/src/fullflow/System/Components/Lookups/__init__.py +5 -0
- fullflow-0.1.0/src/fullflow/System/Components/Nodes/Boundaries.py +35 -0
- fullflow-0.1.0/src/fullflow/System/Components/Nodes/CombustionChambers.py +28 -0
- fullflow-0.1.0/src/fullflow/System/Components/Nodes/Junctions.py +406 -0
- fullflow-0.1.0/src/fullflow/System/Components/Nodes/Solids.py +94 -0
- fullflow-0.1.0/src/fullflow/System/Components/Nodes/Tanks.py +72 -0
- fullflow-0.1.0/src/fullflow/System/Components/Nodes/Volumes.py +79 -0
- fullflow-0.1.0/src/fullflow/System/Components/Nodes/__init__.py +6 -0
- fullflow-0.1.0/src/fullflow/System/Components/Sensors/PressureTransducers.py +24 -0
- fullflow-0.1.0/src/fullflow/System/Components/Sensors/Thermocouples.py +24 -0
- fullflow-0.1.0/src/fullflow/System/Components/Sensors/__init__.py +2 -0
- fullflow-0.1.0/src/fullflow/System/Components/__init__.py +4 -0
- fullflow-0.1.0/src/fullflow/System/Composition.py +164 -0
- fullflow-0.1.0/src/fullflow/System/Model.py +349 -0
- fullflow-0.1.0/src/fullflow/System/Network.py +546 -0
- fullflow-0.1.0/src/fullflow/System/State.py +412 -0
- fullflow-0.1.0/src/fullflow/System/__init__.py +7 -0
- fullflow-0.1.0/src/fullflow/Utilities/.DS_Store +0 -0
- fullflow-0.1.0/src/fullflow/Utilities/RocketCEAUtilities.py +182 -0
- fullflow-0.1.0/src/fullflow/Utilities/__init__.py +1 -0
- fullflow-0.1.0/src/fullflow/__init__.py +10 -0
fullflow-0.1.0/PKG-INFO
ADDED
|
@@ -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
|
fullflow-0.1.0/README.md
ADDED
|
@@ -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 @@
|
|
|
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()
|