Skip to content

Commit 7c91106

Browse files
authored
Inclusion of Shaft and Propeller classes with tests (basic version) (#62)
* Inclusion of Shaft and Propeller classes with tests (basic version)
1 parent 94cb314 commit 7c91106

File tree

10 files changed

+91361
-15
lines changed

10 files changed

+91361
-15
lines changed

bladex/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""
22
BladeX init
33
"""
4-
__all__ = ['profilebase', 'profiles', 'blade', 'deform', 'params', 'ndinterpolator']
4+
__all__ = ['profilebase', 'profiles', 'blade', 'shaft', 'propeller', 'deform', 'params', 'ndinterpolator']
55

66
from .profilebase import ProfileBase
77
from .profiles import CustomProfile, NacaProfile
88
from .blade import Blade
9+
from .shaft import Shaft
10+
from .propeller import Propeller
911
from .deform import Deformation
1012
from .params import ParamFile
11-
from .ndinterpolator import RBF, reconstruct_f, scipy_bspline
13+
from .ndinterpolator import RBF, reconstruct_f, scipy_bspline

bladex/blade.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,22 +226,22 @@ def _planar_to_cylindrical(self):
226226
- :math:`y = r \\sin\\left( \\frac{y_i}{r} \\right) \\qquad
227227
\\forall y_i \\in Y`
228228
229-
- :math:`z = -r \\cos\\left( \\frac{y_i}{r} \\right) \\qquad
229+
- :math:`z = r \\cos\\left( \\frac{y_i}{r} \\right) \\qquad
230230
\\forall y_i \\in Y`
231231
232232
After transformation, the method also fills the numpy.ndarray
233233
"blade_coordinates_up" and "blade_coordinates_down" with the new
234234
:math:`(X, Y, Z)` coordinates.
235235
"""
236-
for section, radius in zip(self.sections, self.radii):
236+
for section, radius in zip(self.sections[::-1], self.radii[::-1]):
237237
theta_up = section.yup_coordinates / radius
238238
theta_down = section.ydown_coordinates / radius
239239

240240
y_section_up = radius * np.sin(theta_up)
241241
y_section_down = radius * np.sin(theta_down)
242242

243-
z_section_up = -radius * np.cos(theta_up)
244-
z_section_down = -radius * np.cos(theta_down)
243+
z_section_up = radius * np.cos(theta_up)
244+
z_section_down = radius * np.cos(theta_down)
245245

246246
self.blade_coordinates_up.append(
247247
np.array([section.xup_coordinates, y_section_up, z_section_up]))
@@ -464,7 +464,7 @@ def plot(self, elev=None, azim=None, ax=None, outfile=None):
464464
else:
465465
fig = plt.figure()
466466
ax = fig.gca(projection=Axes3D.name)
467-
ax.set_aspect('equal')
467+
ax.set_aspect('auto')
468468

469469
for i in range(self.n_sections):
470470
ax.plot(self.blade_coordinates_up[i][0],
@@ -474,7 +474,7 @@ def plot(self, elev=None, azim=None, ax=None, outfile=None):
474474
self.blade_coordinates_down[i][1],
475475
self.blade_coordinates_down[i][2])
476476

477-
plt.axis('equal')
477+
plt.axis('auto')
478478
ax.set_xlabel('X axis')
479479
ax.set_ylabel('Y axis')
480480
ax.set_zlabel('radii axis')
@@ -862,7 +862,7 @@ def generate_iges(self,
862862
display.DisplayShape(self.generated_root, update=True)
863863
start_display()
864864

865-
def generate_blade_solid(self,
865+
def generate_solid(self,
866866
max_deg=1,
867867
display=False,
868868
errors=None):

bladex/propeller.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""
2+
Module for the propeller with shaft bottom-up parametrized construction.
3+
"""
4+
import os
5+
import numpy as np
6+
from bladex import Blade, Shaft
7+
import OCC.Core.TopoDS
8+
from OCC.Core.gp import gp_Dir, gp_Pnt, gp_Ax1, gp_Trsf
9+
from OCC.Core.IGESControl import IGESControl_Reader, IGESControl_Writer
10+
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Transform, BRepBuilderAPI_Sewing
11+
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse
12+
from OCC.Extend.DataExchange import write_stl_file
13+
from OCC.Display.SimpleGui import init_display
14+
15+
class Propeller(object):
16+
"""
17+
Bottom-up parametrized propeller (including shaft) construction.
18+
The constructor requires PythonOCC to be installed.
19+
20+
:param shaft.Shaft shaft: shaft to be added to the propeller
21+
:param blade.Blade blade: blade of the propeller
22+
:param int n_blades: number of blades composing the propeller
23+
:cvar OCC.Core.TopoDS.TopoDS_Solid shaft_solid: solid shaft
24+
:cvar OCC.Core.TopoDS.TopoDS_Shell sewed_full_body: propeller with shaft shell
25+
"""
26+
27+
def __init__(self, shaft, blade, n_blades):
28+
self.shaft_solid = shaft.generate_solid()
29+
blade.apply_transformations(reflect=True)
30+
blade_solid = blade.generate_solid(max_deg=2,
31+
display=False,
32+
errors=None)
33+
blades = []
34+
blades.append(blade_solid)
35+
for i in range(n_blades-1):
36+
blade.rotate(rad_angle=1.0*2.0*np.pi/float(n_blades))
37+
blade_solid = blade.generate_solid(max_deg=2, display=False, errors=None)
38+
blades.append(blade_solid)
39+
blades_combined = blades[0]
40+
for i in range(len(blades)-1):
41+
boolean_union = BRepAlgoAPI_Fuse(blades_combined, blades[i+1])
42+
boolean_union.Build()
43+
if not boolean_union.IsDone():
44+
raise RuntimeError('Unsuccessful assembling of blade')
45+
blades_combined = boolean_union.Shape()
46+
boolean_union = BRepAlgoAPI_Fuse(self.shaft_solid, blades_combined)
47+
boolean_union.Build()
48+
result_compound = boolean_union.Shape()
49+
section_edges = boolean_union.SectionEdges()
50+
sewer = BRepBuilderAPI_Sewing(1e-2)
51+
sewer.Add(result_compound)
52+
sewer.Perform()
53+
self.sewed_full_body = sewer.SewedShape()
54+
55+
def generate_iges(self, filename):
56+
"""
57+
Export the .iges CAD for the propeller with shaft.
58+
59+
:param string filename: path (with the file extension) where to store
60+
the .iges CAD for the propeller and shaft
61+
:raises RuntimeError: if the solid assembling of blades is not
62+
completed successfully
63+
"""
64+
iges_writer = IGESControl_Writer()
65+
iges_writer.AddShape(self.sewed_full_body)
66+
iges_writer.Write(filename)
67+
68+
def generate_stl(self, filename):
69+
"""
70+
Export the .stl CAD for the propeller with shaft.
71+
72+
:param string filename: path (with the file extension) where to store
73+
the .stl CAD for the propeller and shaft
74+
:raises RuntimeError: if the solid assembling of blades is not
75+
completed successfully
76+
"""
77+
write_stl_file(self.sewed_full_body, filename)
78+
79+
def display(self):
80+
"""
81+
Display the propeller with shaft.
82+
"""
83+
display, start_display, add_menu, add_function_to_menu = init_display()
84+
display.DisplayShape(self.sewed_full_body, update=True)
85+
start_display()

bladex/shaft.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os
2+
from OCC.Core.IGESControl import IGESControl_Reader
3+
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeSolid, BRepBuilderAPI_Sewing
4+
import OCC.Core.TopoDS
5+
from OCC.Display.SimpleGui import init_display
6+
7+
class Shaft(object):
8+
"""
9+
Bottom-up parametrized shaft construction.
10+
11+
:param string filename: path (with the file extension) of a .iges file with
12+
stored shaft information.
13+
:cvar string filename: path (with the file extension) of a .iges file with
14+
stored shaft information.
15+
"""
16+
17+
def __init__(self, filename):
18+
self.filename = filename
19+
20+
def generate_solid(self):
21+
"""
22+
Generate an assembled solid shaft using the BRepBuilderAPI_MakeSolid
23+
algorithm. This method requires PythonOCC to be installed.
24+
25+
:raises RuntimeError: if the assembling of the solid shaft is not
26+
completed successfully
27+
:return: solid shaft
28+
:rtype: OCC.Core.TopoDS.TopoDS_Solid
29+
"""
30+
iges_reader = IGESControl_Reader()
31+
iges_reader.ReadFile(self.filename)
32+
iges_reader.TransferRoots()
33+
shaft_compound = iges_reader.Shape()
34+
sewer = BRepBuilderAPI_Sewing(1e-2)
35+
sewer.Add(shaft_compound)
36+
sewer.Perform()
37+
result_sewed_shaft = sewer.SewedShape()
38+
shaft_solid_maker = BRepBuilderAPI_MakeSolid()
39+
shaft_solid_maker.Add(OCC.Core.TopoDS.topods_Shell(result_sewed_shaft))
40+
if not shaft_solid_maker.IsDone():
41+
raise RuntimeError('Unsuccessful assembling of solid shaft')
42+
shaft_solid = shaft_solid_maker.Solid()
43+
return shaft_solid
44+
45+
def display(self):
46+
"""
47+
Display the shaft.
48+
"""
49+
shaft_solid = self.generate_solid()
50+
display, start_display, add_menu, add_function_to_menu = init_display()
51+
display.DisplayShape(shaft_solid, update=True)
52+
start_display()

tests/test_blade.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -776,28 +776,28 @@ def test_stl_smesh_export_exception(self):
776776
with self.assertRaises(ValueError):
777777
blade.generate_stl_smesh(min_length=1, max_length=10, outfile_stl=55)
778778

779-
def test_blade_solid_max_deg_exception(self):
779+
def test_solid_max_deg_exception(self):
780780
blade = create_sample_blade_NACA()
781781
blade.apply_transformations()
782782
with self.assertRaises(ValueError):
783-
blade.generate_blade_solid(
783+
blade.generate_solid(
784784
max_deg=-1,
785785
display=False,
786786
errors=None)
787787

788-
def test_blade_solid_errors_exception(self):
788+
def test_solid_errors_exception(self):
789789
blade = create_sample_blade_NACA()
790790
blade.apply_transformations()
791791
with self.assertRaises(ValueError):
792-
blade.generate_blade_solid(
792+
blade.generate_solid(
793793
max_deg=-1,
794794
display=False,
795795
errors='tests/test_datasets/errors')
796796

797-
def test_generate_blade_solid(self):
797+
def test_generate_solid(self):
798798
blade = create_sample_blade_NACA()
799799
blade.apply_transformations()
800-
blade_solid = blade.generate_blade_solid(max_deg=2, display=False,
800+
blade_solid = blade.generate_solid(max_deg=2, display=False,
801801
errors=None)
802802
self.assertIsInstance(blade_solid, TopoDS_Solid)
803803

0 commit comments

Comments
 (0)