Skip to content

Commit d7c33aa

Browse files
authored
Merge pull request #24 from mahgadalla/dev
Tutorials and Readme
2 parents ad276de + 3a0f103 commit d7c33aa

21 files changed

+2118
-52
lines changed

README.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
1-
**BladeX**: Python Package for Blade Generation
1+
**BladeX**: Python Package for Blade Deformation
22

33
<p align="center">
4-
<a href="https://github.com/mathLab/BladeX/blob/master/LICENSE" target="_blank">
4+
<a href="http://mathlab.github.io/BladeX/" target="_blank" >
5+
<img alt="Python Package for Blade Deformation" src="docs/source/_static/logo_bladex.png" width="200" />
6+
</a>
7+
</p>
8+
<p align="center">
9+
<a href="LICENSE.rst" target="_blank">
510
<img alt="Software License" src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square">
611
</a>
712
<a href="https://travis-ci.org/mathLab/BladeX" target="_blank">
813
<img alt="Build Status" src="https://travis-ci.org/mathLab/BladeX.svg">
914
</a>
10-
<a href="https://coveralls.io/github/mathLab/BladeX" target="_blank">
11-
<img alt="Coverage Status" src="https://coveralls.io/repos/github/mathLab/BladeX/badge.svg">
15+
<a href="https://coveralls.io/github/mathLab/BladeX?branch=master" target="_blank">
16+
<img alt="Coverage Status" src="https://coveralls.io/repos/github/mathLab/BladeX/badge.svg?branch=master">
1217
</a>
1318
<a href="https://www.codacy.com/app/mathLab/BladeX?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=mathLab/BladeX&amp;utm_campaign=Badge_Grade" target="_blank">
1419
<img alt="Codacy Badge" src="https://api.codacy.com/project/badge/Grade/75f02cdeed684c25a273eaffb0d89880">
1520
</a>
1621
</p>
1722

23+
[BladeX](http://mathlab.github.io/BladeX/) (Python Blade Deformation) is a Python package for geometrical parametrization and bottom-up construction of propeller blades. It allows to generate and deform a blade based on the radial distribution of its parameters.
24+
1825
## Table of contents
1926
* [Description](#description)
2027
* [Dependencies and installation](#dependencies-and-installation)
2128
* [Installing from source](#installing-from-source)
2229
* [Documentation](#documentation)
2330
* [Testing](#testing)
31+
* [Examples](#examples)
2432
* [Authors and contributors](#authors-and-contributors)
2533
* [How to contribute](#how-to-contribute)
2634
* [Submitting a patch](#submitting-a-patch)
2735
* [License](#license)
2836

2937
## Description
30-
**BladeX** is a Python package for blade generation.
38+
**BladeX** is a Python package for geometrical parametrization and bottom-up construction of propeller blades. It allows to generate and deform a blade based on the radial distribution of its parameters such as `pitch`, `rake`, `skew`, and the sectional foils' parameters such as `chord` and `camber`. The package is ideally suited for parametric simulations on large number of blade deformations. It provides an automated procedure for the CAD generation, hence reducing the time and effort required for modelling. The main scope of BladeX is to deal with propeller blades, however it can be flexible to be applied on further applications with analogous geometrical structures such as aircraft wings, turbomachinery, or wind turbine blades.
3139

40+
See the [**Examples**](#examples) section below and the [**Tutorials**](tutorials/README.md) to have an idea of the potential of this package.
3241

3342
## Dependencies and installation
3443
**BladeX** requires requires `numpy`, `scipy`, `matplotlib`, and `sphinx` (for the documentation). The code is compatible with Python 2.7 and Python 3.6. It can be installed using `pip` or directly from the source code.
@@ -73,13 +82,24 @@ To run tests locally:
7382
> python test.py
7483
```
7584

85+
## Examples
86+
You can find useful tutorials on how to use the package in the [tutorials](tutorials/README.md) folder.
87+
Here we show a bottom-up parametrized construction of the [Potsdam Propeller Test Case (PPTC)](https://www.sva-potsdam.de/pptc-smp11-workshop) provided the sectional profiles as well as the radial distribution of the `pitch`, `rake`, `skew`. The blade is generated and exported to .iges and .stl formats.
88+
89+
<p align="center">
90+
<img src="readme/PPTC.png" alt>
91+
</p>
92+
<p align="center">
93+
<em>PPTC blade generation according to the radial distribution of the pitch, rake, skew. The generated blade is then exported to .iges and .stl formats.</em>
94+
</p>
95+
7696

7797
## Authors and contributors
7898
**BladeX** is currently developed and mantained at [SISSA mathLab](http://mathlab.sissa.it/) by
99+
* [Mahmoud Gadalla](mailto:[email protected])
79100
* [Marco Tezzele](mailto:[email protected])
80-
* [Mahmoud Gadalla](mailto:[email protected])
81101

82-
under the supervision of [Prof. Gianluigi Rozza](mailto:[email protected]).
102+
under the supervision of [Dr. Andrea Mola](mailto:[email protected]) and [Prof. Gianluigi Rozza](mailto:[email protected]).
83103

84104
Contact us by email for further information or questions about **BladeX**, or suggest pull requests. Contributions improving either the code or the documentation are welcome!
85105

bladex/blade.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ def _planar_to_cylindrical(self):
235235
y_section_up = radius * np.sin(theta_up)
236236
y_section_down = radius * np.sin(theta_down)
237237

238-
z_section_up = -radius * np.cos(theta_up)
239-
z_section_down = -radius * np.cos(theta_down)
238+
z_section_up = radius * np.cos(theta_up)
239+
z_section_down = radius * np.cos(theta_down)
240240

241241
self.blade_coordinates_up.append(
242242
np.array([section.xup_coordinates, y_section_up, z_section_up]))

bladex/deform.py

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ def _optimum_control_points(X, Y, degree, nbasis, rbf_points):
145145
# x and y of the ctrl points with constrained least square.
146146
# we subtract the contribution of the first and last basis function
147147
cvt_x = np.linalg.lstsq(
148-
At, X - A[:, 0] * X[0] - A[:, -1] * X[-1])[0]
148+
At, X - A[:, 0] * X[0] - A[:, -1] * X[-1], rcond=-1)[0]
149149
cvt_y = np.linalg.lstsq(
150-
At, Y - A[:, 0] * Y[0] - A[:, -1] * Y[-1])[0]
150+
At, Y - A[:, 0] * Y[0] - A[:, -1] * Y[-1], rcond=-1)[0]
151151

152152
# fill with the constraints the first and last point
153153
opt_ctrl = np.zeros((nbasis, 2))
@@ -251,8 +251,7 @@ def update_control_points(self, param):
251251
if not self.control_points[param].shape[0] == len(
252252
self.param.deformations[param]):
253253
raise ValueError(
254-
'array of deformations must equal to number of control points'
255-
)
254+
'array of deformations must equal to number of control points')
256255

257256
for i in range(self.control_points[param].shape[0]):
258257
self.control_points[param][i, 1] += self.param.deformations[param][
@@ -364,40 +363,41 @@ def compute_all(self,
364363
self.generate_spline(param=param)
365364
self.compute_deformed_parameters(param=param, tol=tols[param])
366365

367-
def plot(self,
368-
param,
369-
original=True,
370-
ctrl_points=True,
371-
spline=True,
372-
rbf=False,
373-
rbf_points=500,
374-
deformed=False,
375-
outfile=None):
366+
def _plot_parametric_curve(self,
367+
param,
368+
original=True,
369+
ctrl_points=True,
370+
spline=True,
371+
rbf=False,
372+
rbf_points=500,
373+
deformed=False,
374+
outfile=None):
376375
"""
377-
Plot the parametric curve. Several options can be specified.
376+
Private method to plot the parametric curve. Several options
377+
can be specified.
378378
379379
:param str param: parameter corresponding to the parametric curve
380380
needs to be plotted. possible values are `chord`, `pitch`, `rake`,
381381
`skew`, `camber`
382382
:param bool original: if True, then plot the original points of the
383-
parameter at the radii sections. Default value is True
383+
parameter at the radii sections.
384384
:param bool ctrl_points: if True, then plot the control points of
385-
that parametric curve. Default value is True
385+
that parametric curve.
386386
:param bool spline: If True, then plot the B-spline interpolation of
387-
the parametric curve. Default value is True
387+
the parametric curve.
388388
:param bool rbf: if True, then plot the radial basis functions
389-
interpolation of the parametric curve. Default value is True
389+
interpolation of the parametric curve.
390390
:param int rbf_points: number of points used for the rbf interpolation,
391391
if the flag `rbf` is set True. Beware that this argument does not
392392
have the same function of that when computing the control points,
393393
although both uses the radial basis function interpolation with
394-
the Wendland basis. Default value is 500
394+
the Wendland basis.
395395
:param bool deformed: if True, then plot the deformed points of the
396396
parameter radial distribution, estimated using the B-spline
397-
interpolations within a given tolerance. Default value is False
397+
interpolations within a given tolerance.
398398
:param str outfile: if string is passed, then the plot is saved
399399
with that name. If the value is None, then the plot is shown on
400-
the screen. Default value is None
400+
the screen.
401401
"""
402402
self._check_param(param=param)
403403

@@ -457,6 +457,56 @@ def plot(self,
457457
else:
458458
plt.show()
459459

460+
def plot(self,
461+
param,
462+
original=True,
463+
ctrl_points=True,
464+
spline=True,
465+
rbf=False,
466+
rbf_points=500,
467+
deformed=False,
468+
outfile=None):
469+
"""
470+
Plot the parametric curve. Several options
471+
can be specified.
472+
473+
:param array_like param: array_like of strings corresponding to the
474+
parametric curve that needs to be plotted. possible values are
475+
`chord`, `pitch`, `rake`, `skew`, `camber`
476+
:param bool original: if True, then plot the original points of the
477+
parameter at the radii sections. Default value is True
478+
:param bool ctrl_points: if True, then plot the control points of
479+
that parametric curve. Default value is True
480+
:param bool spline: If True, then plot the B-spline interpolation of
481+
the parametric curve. Default value is True
482+
:param bool rbf: if True, then plot the radial basis functions
483+
interpolation of the parametric curve. Default value is True
484+
:param int rbf_points: number of points used for the rbf interpolation,
485+
if the flag `rbf` is set True. Beware that this argument does not
486+
have the same function of that when computing the control points,
487+
although both uses the radial basis function interpolation with
488+
the Wendland basis. Default value is 500
489+
:param bool deformed: if True, then plot the deformed points of the
490+
parameter radial distribution, estimated using the B-spline
491+
interpolations within a given tolerance. Default value is False
492+
:param str outfile: if string is passed, then the plot is saved
493+
with that name. If the value is None, then the plot is shown on
494+
the screen. Default value is None
495+
"""
496+
if not isinstance(param, (list, tuple, np.ndarray)):
497+
param = [param]
498+
499+
for par in param:
500+
self._plot_parametric_curve(
501+
param=par,
502+
original=original,
503+
ctrl_points=ctrl_points,
504+
spline=spline,
505+
rbf=rbf,
506+
rbf_points=rbf_points,
507+
deformed=deformed,
508+
outfile=outfile)
509+
460510
def export_param_file(self, outfile='parameters_mod.prm'):
461511
"""
462512
Export a new parameter file with the new deformed parameters, while
@@ -471,7 +521,10 @@ def export_param_file(self, outfile='parameters_mod.prm'):
471521
prm.radii = self.param.radii
472522
params = ['chord', 'pitch', 'rake', 'skew', 'camber']
473523
for param in params:
474-
prm.parameters[param] = self.deformed_parameters[param]
524+
if np.all(self.param.deformations[param] == 0):
525+
prm.parameters[param] = self.param.parameters[param]
526+
else:
527+
prm.parameters[param] = self.deformed_parameters[param]
475528
prm.nbasis[param] = self.param.nbasis[param]
476529
prm.degree[param] = self.param.nbasis[param]
477530
prm.npoints[param] = self.param.npoints[param]

bladex/profilebase.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,10 @@ def rotate(self, rad_angle=None, deg_angle=None):
439439
raise ValueError(
440440
'You have to pass either the angle in radians or in degrees,' \
441441
' not both.')
442-
if rad_angle:
442+
if rad_angle is not None:
443443
cosine = np.cos(rad_angle)
444444
sine = np.sin(rad_angle)
445-
elif deg_angle:
445+
elif deg_angle is not None:
446446
cosine = np.cos(np.radians(deg_angle))
447447
sine = np.sin(np.radians(deg_angle))
448448
else:
@@ -540,8 +540,14 @@ def plot(self,
540540
raise ValueError('One or all the coordinates have None value.')
541541

542542
if profile:
543-
plt.plot(self.xup_coordinates, self.yup_coordinates, label='Upper profile')
544-
plt.plot(self.xdown_coordinates, self.ydown_coordinates, label='Lower profile')
543+
plt.plot(
544+
self.xup_coordinates,
545+
self.yup_coordinates,
546+
label='Upper profile')
547+
plt.plot(
548+
self.xdown_coordinates,
549+
self.ydown_coordinates,
550+
label='Lower profile')
545551

546552
if chord_line:
547553
if self.chord_line is None:
@@ -553,10 +559,15 @@ def plot(self,
553559
if self.camber_line is None:
554560
raise ValueError(
555561
'Camber line is None. You must compute it first')
556-
plt.plot(self.camber_line[0], self.camber_line[1], label='Camber line')
562+
plt.plot(
563+
self.camber_line[0], self.camber_line[1], label='Camber line')
557564

558565
if ref_point:
559-
plt.scatter(self.reference_point[0], self.reference_point[1], s=15, label='Reference point')
566+
plt.scatter(
567+
self.reference_point[0],
568+
self.reference_point[1],
569+
s=15,
570+
label='Reference point')
560571

561572
plt.grid(linestyle='dotted')
562573
plt.axis('equal')
60.9 KB
Loading

readme/PPTC.png

271 KB
Loading

tests/test_blade.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,31 +267,32 @@ def test_transformations_reflect_blade_up(self):
267267
blade = create_sample_blade_NACA()
268268
blade.apply_transformations(reflect=True)
269269
blade_coordinates_up_expected = np.load(
270-
'tests/test_datasets/blade_up_after_transformation_flip.npy')
270+
'tests/test_datasets/blade_up_after_transformation_reflect.npy')
271271
np.testing.assert_almost_equal(blade.blade_coordinates_up,
272272
blade_coordinates_up_expected)
273273

274274
def test_transformations_reflect_blade_down(self):
275275
blade = create_sample_blade_NACA()
276276
blade.apply_transformations(reflect=True)
277277
blade_coordinates_down_expected = np.load(
278-
'tests/test_datasets/blade_down_after_transformation_flip.npy')
278+
'tests/test_datasets/blade_down_after_transformation_reflect.npy')
279279
np.testing.assert_almost_equal(blade.blade_coordinates_down,
280280
blade_coordinates_down_expected)
281281

282282
def test_transformations_no_reflect_blade_up(self):
283283
blade = create_sample_blade_NACA()
284284
blade.apply_transformations(reflect=False)
285285
blade_coordinates_up_expected = np.load(
286-
'tests/test_datasets/blade_up_after_transformation_no_flip.npy')
286+
'tests/test_datasets/blade_up_after_transformation_no_reflect.npy')
287287
np.testing.assert_almost_equal(blade.blade_coordinates_up,
288288
blade_coordinates_up_expected)
289289

290290
def test_transformations_no_reflect_blade_down(self):
291291
blade = create_sample_blade_NACA()
292292
blade.apply_transformations(reflect=False)
293293
blade_coordinates_down_expected = np.load(
294-
'tests/test_datasets/blade_down_after_transformation_no_flip.npy')
294+
'tests/test_datasets/blade_down_after_transformation_no_reflect.npy'
295+
)
295296
np.testing.assert_almost_equal(blade.blade_coordinates_down,
296297
blade_coordinates_down_expected)
297298

tests/test_datasets/blade_down_after_transformation_no_flip.npy renamed to tests/test_datasets/blade_down_after_transformation_no_reflect.npy

56.4 KB
Binary file not shown.

tests/test_datasets/blade_down_after_transformation_flip.npy renamed to tests/test_datasets/blade_down_after_transformation_reflect.npy

56.4 KB
Binary file not shown.

tests/test_datasets/blade_up_after_transformation_no_flip.npy renamed to tests/test_datasets/blade_up_after_transformation_no_reflect.npy

56.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)