Skip to content

Commit 0c84eab

Browse files
authored
Fix reversepropeller and dynamic thickness/camber (#121)
* Added possibility to specify thickness and camber max for each radius in the blade __init__ method. To do so, corresponding methods in baseprofile implemented. Added tests too * Fix: generalized reversepropeller code to work with generic blades with z as main direction. Also cleaned a little bit the code * Added case where blade's geometry is made by faces, like for BladeX generated blades * Refactored reversepropeller class by setting a base class and two inheritant classes * Fixed skew and rake computations * Added a tutorial on reversepropellerbladex * Run tutorial and included output cells
1 parent eed710f commit 0c84eab

7 files changed

Lines changed: 1124 additions & 409 deletions

File tree

bladex/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
from .deform import Deformation
1818
from .params import ParamFile
1919
from .ndinterpolator import RBF, reconstruct_f, scipy_bspline
20+
from .reversepropeller import ReversePropellerInterface
21+
from .reversepropeller import BaseReversePropeller
2022
from .reversepropeller import ReversePropeller
23+
from .reversepropeller import ReversePropellerBladeX
24+
from .reversepropeller.reversepropeller import ReversePropeller
2125
from .shaft.cylinder_shaft import CylinderShaft
2226
from .intepolatedface import InterpolatedFace
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
Profile init
3+
"""
4+
__all__ = ['ReversePropellerInterface', 'BaseReversePropeller',
5+
'ReversePropeller', 'ReversePropellerBladeX']
6+
7+
from .reversepropellerinterface import ReversePropellerInterface
8+
from .basereversepropeller import BaseReversePropeller
9+
from .reversepropeller import ReversePropeller
10+
from .reversepropellerbladex import ReversePropellerBladeX

bladex/reversepropeller/basereversepropeller.py

Lines changed: 385 additions & 0 deletions
Large diffs are not rendered by default.

bladex/reversepropeller.py renamed to bladex/reversepropeller/reversepropeller.py

Lines changed: 56 additions & 409 deletions
Large diffs are not rendered by default.

bladex/reversepropeller/reversepropellerbladex.py

Lines changed: 448 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Interface module that provides essential tools for the reversepropeller.
3+
"""
4+
5+
from abc import ABC, abstractmethod
6+
7+
8+
class ReversePropellerInterface(ABC):
9+
"""
10+
Interface for reverse problem propeller blade.
11+
12+
Given an iges file that has the blade's geometry, we solve an inverse problem
13+
to retrieve the properties of the blades both in terms of:
14+
- sectional properties (chord length, thickness, camber);
15+
- sections location in the space (skew angle, pitch and rake)
16+
"""
17+
@abstractmethod
18+
def _extract_solid_from_file(self):
19+
"""
20+
Abstract method that extract the solid object from the IGES file
21+
"""
22+
pass
23+
24+
@abstractmethod
25+
def _build_cylinder(self, radius):
26+
"""
27+
Abstract method that, given a radius, it builds the cylinder with specified radius
28+
and main axis along x direction. This cylinder will be intersected with the blade
29+
"""
30+
pass
31+
32+
@abstractmethod
33+
def _build_intersection_cylinder_blade(self):
34+
"""
35+
Abstract method that computes the intersection between the blade and the cylinder
36+
created by _build_cylinder() method
37+
"""
38+
pass
39+
40+
@abstractmethod
41+
def _camber_curve(self, radius):
42+
"""
43+
Abstract method that retrieves the camber curve once the intersection is computed
44+
"""
45+
pass
46+
47+
@abstractmethod
48+
def save_global_parameters(self, filename_csv):
49+
"""
50+
Abstract method to store the retrieved properties and section into a csv file
51+
"""
52+
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "c2ec2fd3",
6+
"metadata": {},
7+
"source": [
8+
"# BladeX"
9+
]
10+
},
11+
{
12+
"cell_type": "markdown",
13+
"id": "ad979fa2",
14+
"metadata": {},
15+
"source": [
16+
"## Tutorial 7: Retrieve blade's parameters from iges file using reversepropeller class"
17+
]
18+
},
19+
{
20+
"cell_type": "markdown",
21+
"id": "c0ab2647",
22+
"metadata": {},
23+
"source": [
24+
"The goal of this tutorial is to show how to start from an iges file containing a blade to obtain its parameters, both for the sections (camber, thickness) and for the whole blade (chord lengths, pitch, rake, skew angles)."
25+
]
26+
},
27+
{
28+
"cell_type": "markdown",
29+
"id": "5f21ab8e",
30+
"metadata": {},
31+
"source": [
32+
"We start from some useful imports, as usual."
33+
]
34+
},
35+
{
36+
"cell_type": "code",
37+
"execution_count": 1,
38+
"id": "4362678f",
39+
"metadata": {},
40+
"outputs": [],
41+
"source": [
42+
"import numpy as np\n",
43+
"from bladex import Blade, NacaProfile, ReversePropellerBladeX, ReversePropeller"
44+
]
45+
},
46+
{
47+
"cell_type": "markdown",
48+
"id": "45d4e8f4",
49+
"metadata": {},
50+
"source": [
51+
"Then, we create the same blade as for Tutorial 6 and store it as iges file."
52+
]
53+
},
54+
{
55+
"cell_type": "code",
56+
"execution_count": 2,
57+
"id": "aa8f973f",
58+
"metadata": {},
59+
"outputs": [
60+
{
61+
"name": "stdout",
62+
"output_type": "stream",
63+
"text": [
64+
"(10, 3, 50)\n",
65+
"(10, 3, 50)\n",
66+
"(2, 3, 50)\n",
67+
"(2, 3, 50)\n"
68+
]
69+
}
70+
],
71+
"source": [
72+
"# Create sections with NACAProfile\n",
73+
"n_sections = 10\n",
74+
"sections = np.asarray([NacaProfile(digits='4412', n_points=50) for i in range(n_sections)])\n",
75+
"\n",
76+
"# Define blade parameters\n",
77+
"radii = np.arange(1.0, 11.0, 1.0)\n",
78+
"chord_lengths = np.array([0.05, 2.5, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7])\n",
79+
"pitch = np.arange(1., 11.)\n",
80+
"rake = np.arange(0.1, 1.1, 0.1)\n",
81+
"skew_angles = np.arange(1., 21, 2.)\n",
82+
"\n",
83+
"# Create the Blade\n",
84+
"blade = Blade(sections=sections,\n",
85+
" radii=radii,\n",
86+
" chord_lengths=chord_lengths,\n",
87+
" pitch=pitch,\n",
88+
" rake=rake,\n",
89+
" skew_angles=skew_angles)\n",
90+
"\n",
91+
"# We build the blade to be able to store its iges version\n",
92+
"# The method build() internally calls apply_transformations()\n",
93+
"blade.build()\n",
94+
"\n",
95+
"# Storing the blade in iges format\n",
96+
"iges_filename='data/blade_reversepropeller.igs'\n",
97+
"blade.export_iges(iges_filename)"
98+
]
99+
},
100+
{
101+
"cell_type": "markdown",
102+
"id": "61aabd42",
103+
"metadata": {},
104+
"source": [
105+
"Now that we have our starting iges file, we can retrieve the blades parameters. In BladeX you can either use the `ReversePropellerBladeX`, if the blade has been generated using BladeX, or `ReversePropeller` otherwise. The former is robust and more accurate for BladeX blades because it exploits known structure of the iges file."
106+
]
107+
},
108+
{
109+
"cell_type": "code",
110+
"execution_count": 3,
111+
"id": "78b71d18",
112+
"metadata": {},
113+
"outputs": [
114+
{
115+
"name": "stdout",
116+
"output_type": "stream",
117+
"text": [
118+
"\u001b[32;1mTotal number of loaded entities 52.\n",
119+
"\u001b[0m\n",
120+
"[2.4999999998198215, 3.499999999872226, 3.9999999996733515, 4.500000000152133, 4.999999999959586, 5.499999999841911, 6.000000000221554, 6.500000000430857]\n",
121+
"[1.999999999872152, 2.9999999999180984, 3.9999999997822977, 4.9999999997217, 6.000000002259764, 7.000000003635259, 8.000000002720112, 9.000000000963933]\n",
122+
"[0.1999999999957766, 0.30000000000569976, 0.39999999999797053, 0.49999999999262956, 0.6000000000362385, 0.7000000000603949, 0.8000000000447604, 0.9000000000155906]\n",
123+
"[2.9999999959870425, 4.999999997162759, 6.999999998270603, 8.9999999995899, 11.000000000103695, 12.999999998485547, 15.00000000063232, 17.000000001394554]\n"
124+
]
125+
}
126+
],
127+
"source": [
128+
"# We need to specify: \n",
129+
"# i) the name of the iges file; \n",
130+
"# ii) values of the radii in mm at which the parameters are to be extracted;\n",
131+
"# iii) how many points to use to reconstruct the sections for each radius value\n",
132+
"# In this case we use the same radii values (extrema excluded) of the blade's generation for a better comparison\n",
133+
"radii_reverse = np.arange(2.0, 10., 1.)\n",
134+
"# This operation may take few minutes\n",
135+
"reverse = ReversePropellerBladeX(iges_filename, 1000*radii_reverse, num_points_top_bottom=100)\n",
136+
"\n",
137+
"# Now we can retrieve the blade's parameters\n",
138+
"print(reverse.chord_length_list)\n",
139+
"print(reverse.pitch_list)\n",
140+
"print(reverse.rake_list)\n",
141+
"print(reverse.skew_angles_list)\n",
142+
"\n",
143+
"# Finally, we can store the parameters to a csv file\n",
144+
"reverse.save_global_parameters('data/blade_parameters.csv')"
145+
]
146+
}
147+
],
148+
"metadata": {
149+
"kernelspec": {
150+
"display_name": "marinai",
151+
"language": "python",
152+
"name": "python3"
153+
},
154+
"language_info": {
155+
"codemirror_mode": {
156+
"name": "ipython",
157+
"version": 3
158+
},
159+
"file_extension": ".py",
160+
"mimetype": "text/x-python",
161+
"name": "python",
162+
"nbconvert_exporter": "python",
163+
"pygments_lexer": "ipython3",
164+
"version": "3.10.13"
165+
}
166+
},
167+
"nbformat": 4,
168+
"nbformat_minor": 5
169+
}

0 commit comments

Comments
 (0)