Skip to content

Commit eef4410

Browse files
committed
MNT: Add CAD data for tank geometry validation.
1 parent c01bb49 commit eef4410

4 files changed

Lines changed: 250 additions & 2 deletions

File tree

rocketpy/motors/tank_geometry.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from functools import cached_property
22

33
import numpy as np
4-
54
from ..mathutils.function import Function, funcify_method
65
from ..mathutils.piecewise_function import PiecewiseFunction
76
from ..plots.tank_geometry_plots import _TankGeometryPlots
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
M1670-BS 75 757 0 3.101 5.231 CTI
2+
0.155 100
3+
0.192 1500
4+
0.2 2000
5+
0.25 2200
6+
0.3 1800
7+
0.6 1950
8+
1.1 2034
9+
1.6 2000
10+
2.1 1900
11+
2.6 1760
12+
3 1700
13+
3.1 1650
14+
3.4 530
15+
3.5 350
16+
4.0 0; this is a comment
17+
; this is a last line comment
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# CadQuery Validation of Tank Geometry Parameters"
8+
]
9+
},
10+
{
11+
"cell_type": "code",
12+
"execution_count": 1,
13+
"metadata": {},
14+
"outputs": [],
15+
"source": [
16+
"%load_ext autoreload\n",
17+
"%autoreload 2"
18+
]
19+
},
20+
{
21+
"cell_type": "code",
22+
"execution_count": 2,
23+
"metadata": {},
24+
"outputs": [],
25+
"source": [
26+
"import cadquery as cq\n",
27+
"import numpy as np\n",
28+
"import csv\n",
29+
"from rocketpy import CylindricalTank, SphericalTank"
30+
]
31+
},
32+
{
33+
"cell_type": "code",
34+
"execution_count": 3,
35+
"metadata": {},
36+
"outputs": [
37+
{
38+
"name": "stdout",
39+
"output_type": "stream",
40+
"text": [
41+
"Warning: Adding spherical caps to the tank will not modify the total height of the tank 0.8068 m. Its cylindrical portion height will be reduced to 0.6579999999999999 m.\n",
42+
"Warning: Adding spherical caps to the tank will not modify the total height of the tank 0.981 m. Its cylindrical portion height will be reduced to 0.846 m.\n"
43+
]
44+
}
45+
],
46+
"source": [
47+
"# Create fixtures geometries\n",
48+
"geometry_map = {}\n",
49+
"geometry_map[\"sphere\"] = {\"spherical_oxidizer_tank\": SphericalTank(0.05)}\n",
50+
"geometry_map[\"cylinder\"] = {\n",
51+
" \"cylindrical_oxidizer_tank\": CylindricalTank(0.0744, 0.8068, True),\n",
52+
" \"cylindrical_pressurant_tank\": CylindricalTank(0.135 / 2, 0.981, True),\n",
53+
"}"
54+
]
55+
},
56+
{
57+
"cell_type": "code",
58+
"execution_count": 4,
59+
"metadata": {},
60+
"outputs": [],
61+
"source": [
62+
"def export_expected_parameters(name, datapoints):\n",
63+
" with open(f'{name}_expected.csv', mode='w') as file:\n",
64+
" writer = csv.writer(file)\n",
65+
" writer.writerow(['level_height', 'volume', 'center_of_mass', 'inertia'])\n",
66+
" for data_row in datapoints:\n",
67+
" writer.writerow([*data_row])"
68+
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": 5,
73+
"metadata": {},
74+
"outputs": [],
75+
"source": [
76+
"def evaluate_sphere_parameters(radius, level_height):\n",
77+
" \"\"\"Computes the volume, center of mass and inertia\n",
78+
" (with respect to the origin) of a sphere 'filled' up\n",
79+
" to a certain level height.\n",
80+
"\n",
81+
" Parameters\n",
82+
" ----------\n",
83+
" radius : float\n",
84+
" The radius of the sphere.\n",
85+
" level_height : float\n",
86+
" The height of the liquid inside the sphere.\n",
87+
"\n",
88+
" Returns\n",
89+
" -------\n",
90+
" volume : float\n",
91+
" The volume of the sphere.\n",
92+
" center_of_mass : float\n",
93+
" The center of mass of the sphere.\n",
94+
" inertia : float\n",
95+
" The inertia of the sphere with respect to the origin.\n",
96+
" \"\"\"\n",
97+
" sphere = cq.Workplane(\"XY\").sphere(radius)\n",
98+
"\n",
99+
" # Cut the sphere to the level height\n",
100+
" drill_height = 10 * radius\n",
101+
" sphere = sphere.cut(\n",
102+
" cq.Workplane(\"XY\")\n",
103+
" .cylinder(drill_height, radius)\n",
104+
" .translate((0, 0, drill_height / 2 + level_height))\n",
105+
" )\n",
106+
"\n",
107+
" # Uncomment to display the CAD\n",
108+
" # display(sphere)\n",
109+
"\n",
110+
" primitive = sphere.val()\n",
111+
"\n",
112+
" volume = primitive.Volume()\n",
113+
" center_of_mass = primitive.centerOfMass(primitive)\n",
114+
" inertia_tensor = primitive.matrixOfInertia(primitive)\n",
115+
"\n",
116+
" return volume, center_of_mass.z, inertia_tensor[0][0] + volume * center_of_mass.z**2"
117+
]
118+
},
119+
{
120+
"cell_type": "code",
121+
"execution_count": 6,
122+
"metadata": {},
123+
"outputs": [],
124+
"source": [
125+
"for name, sphere_geometry in geometry_map[\"sphere\"].items():\n",
126+
" radius = sphere_geometry.total_height / 2\n",
127+
" datapoints = []\n",
128+
" for h in np.linspace(-radius, radius, 25):\n",
129+
" datapoints.append([h, *evaluate_sphere_parameters(radius, h)])\n",
130+
"\n",
131+
" export_expected_parameters(name, datapoints)"
132+
]
133+
},
134+
{
135+
"cell_type": "code",
136+
"execution_count": 7,
137+
"metadata": {},
138+
"outputs": [],
139+
"source": [
140+
"def evaluate_cylinder_with_caps(total_length, radius, level_height):\n",
141+
" \"\"\"Computes the volume, center of mass and inertia (with respect\n",
142+
" to the origin) of a cylinder with spherical caps 'filled' up to a\n",
143+
" certain level height.\n",
144+
"\n",
145+
" Parameters\n",
146+
" ----------\n",
147+
" total_length : float\n",
148+
" The total length of the cylinder (with caps).\n",
149+
" radius : float\n",
150+
" The radius of the cylinder.\n",
151+
"\n",
152+
" Returns\n",
153+
" -------\n",
154+
" volume : float\n",
155+
" The volume of the cylinder.\n",
156+
" center_of_mass : float\n",
157+
" The z-coordinate of the center of mass.\n",
158+
" inertia : float\n",
159+
" The inertia of the cylinder with respect to the origin.\n",
160+
" \"\"\"\n",
161+
" cylinder_height = total_length - 2 * radius\n",
162+
"\n",
163+
" cylinder = cq.Workplane(\"XY\").cylinder(cylinder_height, radius)\n",
164+
" top_cap = (\n",
165+
" cq.Workplane(\"XY\").sphere(radius).translate((0, 0, total_length / 2 - radius))\n",
166+
" )\n",
167+
" bottom_cap = (\n",
168+
" cq.Workplane(\"XY\").sphere(radius).translate((0, 0, -total_length / 2 + radius))\n",
169+
" )\n",
170+
"\n",
171+
" solid = cylinder.union(top_cap).union(bottom_cap)\n",
172+
"\n",
173+
" # Remove solid above the level_height\n",
174+
" drill_height = 10 * total_length\n",
175+
" solid = solid.cut(\n",
176+
" cq.Workplane(\"XY\")\n",
177+
" .cylinder(drill_height, radius)\n",
178+
" .translate((0, 0, drill_height / 2 + level_height))\n",
179+
" )\n",
180+
" # Uncomment to display the CAD\n",
181+
" # display(solid)\n",
182+
"\n",
183+
" primitive = solid.val()\n",
184+
"\n",
185+
" volume = primitive.Volume()\n",
186+
" center_of_mass = primitive.centerOfMass(primitive)\n",
187+
" inertia_tensor = primitive.matrixOfInertia(primitive)\n",
188+
"\n",
189+
" return volume, center_of_mass.z, inertia_tensor[0][0] + volume * center_of_mass.z**2"
190+
]
191+
},
192+
{
193+
"cell_type": "code",
194+
"execution_count": 8,
195+
"metadata": {},
196+
"outputs": [],
197+
"source": [
198+
"for name, cylinder_geometry in geometry_map[\"cylinder\"].items():\n",
199+
" radius = cylinder_geometry.radius(0)\n",
200+
" length = cylinder_geometry.total_height\n",
201+
" datapoints = []\n",
202+
" for h in np.linspace(-length / 2, length / 2, 25):\n",
203+
" datapoints.append([h, *evaluate_cylinder_with_caps(length, radius, h)])\n",
204+
"\n",
205+
" export_expected_parameters(name, datapoints)"
206+
]
207+
}
208+
],
209+
"metadata": {
210+
"kernelspec": {
211+
"display_name": "venv312",
212+
"language": "python",
213+
"name": "python3"
214+
},
215+
"language_info": {
216+
"codemirror_mode": {
217+
"name": "ipython",
218+
"version": 3
219+
},
220+
"file_extension": ".py",
221+
"mimetype": "text/x-python",
222+
"name": "python",
223+
"nbconvert_exporter": "python",
224+
"pygments_lexer": "ipython3",
225+
"version": "3.12.3"
226+
}
227+
},
228+
"nbformat": 4,
229+
"nbformat_minor": 2
230+
}

tests/fixtures/motor/tanks_fixtures.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,9 @@ def oxidizer_tank(oxidizer_fluid, oxidizer_pressurant, propellant_tank_geometry)
431431

432432

433433
@pytest.fixture
434-
def spherical_oxidizer_tank(oxidizer_fluid, oxidizer_pressurant):
434+
def spherical_oxidizer_tank(
435+
oxidizer_fluid, oxidizer_pressurant, spherical_oxidizer_geometry
436+
):
435437
"""An example of a oxidizer spherical tank.
436438
437439
Parameters

0 commit comments

Comments
 (0)