Skip to content
37 changes: 22 additions & 15 deletions .github/workflows/testing_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,40 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
python-version: [3.7, 3.8]
python-version: [3.8, 3.9]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4


- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Setup conda
uses: s-weigand/setup-[email protected]
- name: Setup Conda
uses: conda-incubator/setup-miniconda@v3
with:
update-conda: true
python-version: ${{ matrix.python-version }}
conda-channels: anaconda, conda-forge

channels: conda-forge, defaults
activate-environment: ""

- name: Install Python dependencies on Linux/MacOS
shell: bash -el {0}
if: startsWith(matrix.os, 'windows') != true
run: |
conda install --yes pythonocc-core=7.4.1
python3 -m pip install --upgrade pip
python3 -m pip install smithers[vtk]
python3 -m pip install .[test]
conda create -n occ python=${{ matrix.python-version }} pythonocc-core
conda info
conda activate occ
conda info
python -m pip install --upgrade pip
python -m pip install smithers[vtk]
python -m pip install .[test]
python -c 'import OCC'

- name: Install Python dependencies on Windows
if: startsWith(matrix.os, 'windows')
run: |
conda install --yes pythonocc-core=7.4.1
conda install --yes pythonocc-core
python -m pip install --upgrade pip
python -m pip install smithers[vtk]
python -m pip install .[test]
Expand All @@ -52,5 +56,8 @@ jobs:
run: python -m pytest

- name: Test with pytest on Linux/MacOS
shell: bash -el {0}
if: startsWith(matrix.os, 'windows') != true
run: python3 -m pytest
run: |
conda activate occ
python -m pytest
66 changes: 63 additions & 3 deletions bladex/blade.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,6 @@ def scale(self, factor):
self.blade_coordinates_down[i][1] = new_coord_matrix_down[1]
self.blade_coordinates_down[i][2] = new_coord_matrix_down[2]



def plot(self, elev=None, azim=None, ax=None, outfile=None):
"""
Plot the generated blade sections.
Expand Down Expand Up @@ -1088,7 +1086,59 @@ def generate_stl_blade(self, filename):

write_stl_file(self.sewed_full, filename)

def generate_iges_blade(self, filename):
def _generate_leading_edge_curves(self):
"""
Private method to generate curves that follow the leading edge of the blade
(top and bottom surfaces).
"""
self._import_occ_libs()

# Extract points at leftmost 5% for upper and lower surfaces
upper_points = []
lower_points = []

for i in range(self.n_sections):
min_x = np.min(self.sections[i].xdown_coordinates)
max_x = np.max(self.sections[i].xdown_coordinates)
delta_x = max_x - min_x

target_x = min_x + 0.95 * delta_x

idx = np.abs(self.sections[i].xdown_coordinates - target_x).argmin()

# Create points for upper and lower curves
upper_points.append(gp_Pnt(
1000 * self.blade_coordinates_up[i][0][idx],
1000 * self.blade_coordinates_up[i][1][idx],
1000 * self.blade_coordinates_up[i][2][idx]
))

lower_points.append(gp_Pnt(
1000 * self.blade_coordinates_down[i][0][idx],
1000 * self.blade_coordinates_down[i][1][idx],
1000 * self.blade_coordinates_down[i][2][idx]
))

# Create arrays of points for interpolation
upper_array = TColgp_HArray1OfPnt(1, len(upper_points))
lower_array = TColgp_HArray1OfPnt(1, len(lower_points))

for i, (up, low) in enumerate(zip(upper_points, lower_points)):
upper_array.SetValue(i + 1, up)
lower_array.SetValue(i + 1, low)

# Create interpolated curves
upper_curve = GeomAPI_Interpolate(upper_array, False, 1e-9)
lower_curve = GeomAPI_Interpolate(lower_array, False, 1e-9)

upper_curve.Perform()
lower_curve.Perform()

# Convert to edges
self.upper_le_edge = BRepBuilderAPI_MakeEdge(upper_curve.Curve()).Edge()
self.lower_le_edge = BRepBuilderAPI_MakeEdge(lower_curve.Curve()).Edge()

def generate_iges_blade(self, filename, include_le_curves=False):
"""
Generate and export the .IGES file for the entire blade.
This method requires PythonOCC (7.4.0) to be installed.
Expand All @@ -1099,11 +1149,20 @@ def generate_iges_blade(self, filename):
self._generate_lower_face(max_deg=1)
self._generate_root(max_deg=1)
self._generate_tip(max_deg=1)

if include_le_curves:
self._generate_leading_edge_curves()

iges_writer = IGESControl_Writer()
iges_writer.AddShape(self.generated_upper_face)
iges_writer.AddShape(self.generated_lower_face)
iges_writer.AddShape(self.generated_root)
iges_writer.AddShape(self.generated_tip)

if include_le_curves:
iges_writer.AddShape(self.upper_le_edge)
iges_writer.AddShape(self.lower_le_edge)

iges_writer.Write(filename)

@staticmethod
Expand Down Expand Up @@ -1244,6 +1303,7 @@ def export_ppg(self,
if i == len(hub_offsets) - 1:
output_string += str("%.8e" % offset[0]) + ' ' + str(
"%.8e" % hub_offsets[i][1])

continue
output_string += str("%.8e" % offset[0]) + ' ' + str(
"%.8e" % offset[1]) + '\n'
Expand Down
10 changes: 5 additions & 5 deletions bladex/reversepropeller.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
BRep_Tool_CurveOnSurface)
import OCC.Core.TopoDS
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse
from OCC.Core.BRepAlgo import BRepAlgo_Section
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Section
from OCC.Core.TopTools import TopTools_ListOfShape, TopTools_MapOfShape
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE, TopAbs_WIRE
Expand All @@ -29,7 +29,7 @@
from OCC.Core.TopoDS import TopoDS_Shape
from OCC.Core.TColgp import TColgp_HArray1OfPnt, TColgp_Array1OfPnt
from OCC.Core.GeomAPI import GeomAPI_Interpolate, GeomAPI_IntCS, GeomAPI_ProjectPointOnSurf
from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_HCurve
from OCC.Core.BRepAdaptor import BRepAdaptor_Curve
from OCC.Core.GCPnts import GCPnts_AbscissaPoint
from OCC.Core.BRep import BRep_Tool
from OCC.Core.IntTools import IntTools_FClass2d
Expand All @@ -38,7 +38,7 @@
from OCC.Core.TopoDS import topods, TopoDS_Edge, TopoDS_Compound
from subprocess import call
from OCC.Core.IntCurvesFace import IntCurvesFace_ShapeIntersector
from OCC.Core.Adaptor3d import Adaptor3d_Curve, Adaptor3d_HCurve
from OCC.Core.Adaptor3d import Adaptor3d_Curve
from OCC.Core.Geom import Geom_Line
from OCC.Display.SimpleGui import init_display
from OCC.Core.BRepGProp import (brepgprop_LinearProperties,
Expand All @@ -48,8 +48,8 @@
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeCylinder
from OCC.Core.GeomLProp import GeomLProp_SLProps
from OCC.Core.GCPnts import GCPnts_AbscissaPoint
from OCC.Core.BRepAdaptor import (BRepAdaptor_Curve, BRepAdaptor_HCurve,
BRepAdaptor_CompCurve, BRepAdaptor_HCompCurve)
from OCC.Core.BRepAdaptor import (BRepAdaptor_Curve,
BRepAdaptor_CompCurve)
from OCC.Core.GCPnts import GCPnts_UniformDeflection
from OCC.Core.GeomAPI import GeomAPI_PointsToBSpline
from OCC.Core.GeomAbs import (GeomAbs_C0, GeomAbs_G1, GeomAbs_C1, GeomAbs_G2,
Expand Down
60 changes: 60 additions & 0 deletions tests/test_blade.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,3 +835,63 @@ def test_blade_str_method(self):
string += '\nInduced rake from skew (in unit length)'\
' for the sections = {}'.format(blade.induced_rake)
assert blade.__str__() == string

def test_blade_parameters_unchanged_after_transformations_and_rotation(self):
"""Test that blade parameters remain unchanged after transformations and rotation."""
# Create the original parameters
sections = np.asarray([NacaProfile(digits='0012') for i in range(10)])
radii = np.arange(0.4, 1.31, 0.1)
chord_lengths = np.concatenate((np.arange(0.55, 1.1, 0.15),
np.arange(1.03, 0.9, -0.03),
np.array([0.3])))
pitch = np.append(np.arange(3.0, 4., 0.2), np.arange(4.1, 3.2, -0.2))
rake = np.append(np.arange(5e-3, 0.08, 1e-2), np.arange(0.075, 0.02, -3e-2))
skew_angles = np.append(np.arange(-4., -9., -3.), np.arange(-7., 15., 3.))

# Store original section data
original_sections = []
for section in sections:
original_sections.append({
'xup': section.xup_coordinates.copy(),
'yup': section.yup_coordinates.copy(),
'xdown': section.xdown_coordinates.copy(),
'ydown': section.ydown_coordinates.copy()
})

# Build the blade
blade = bl.Blade(
sections=sections,
radii=radii,
chord_lengths=chord_lengths,
pitch=pitch,
rake=rake,
skew_angles=skew_angles)

# Apply transformations and rotate 180 degrees around z-axis
blade.apply_transformations()
blade.rotate(deg_angle=180)

# Verify parameters are unchanged
for i, section in enumerate(blade.sections):
np.testing.assert_array_equal(
section.xup_coordinates,
original_sections[i]['xup']
)
np.testing.assert_array_equal(
section.yup_coordinates,
original_sections[i]['yup']
)
np.testing.assert_array_equal(
section.xdown_coordinates,
original_sections[i]['xdown']
)
np.testing.assert_array_equal(
section.ydown_coordinates,
original_sections[i]['ydown']
)

np.testing.assert_array_equal(blade.radii, radii)
np.testing.assert_array_equal(blade.chord_lengths, chord_lengths)
np.testing.assert_array_equal(blade.pitch, pitch)
np.testing.assert_array_equal(blade.rake, rake)
np.testing.assert_array_equal(blade.skew_angles, skew_angles)
Loading