Skip to content

Commit cd5b2e5

Browse files
committed
Unit testing iges/stl methods
Pretty format
1 parent 6c86b6d commit cd5b2e5

File tree

3 files changed

+100
-35
lines changed

3 files changed

+100
-35
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ See the [**Examples**](#examples) section below and the [**Tutorials**](tutorial
4343
**BladeX** requires `numpy`, `scipy`, `matplotlib`, `sphinx` (for the documentation), and `nose` (for the local test). They can be easily installed using `pip`.
4444
**BladeX** is compatible with Python 2.7 and Python 3.6. Moreover, some of the modules require `OCC` to be installed for the `.iges` or `.stl` CAD generation. Please see the table below for instructions on how to satisfy the `OCC` requirements. You can also refer to `pythonocc.org` or `github.com/tpaviot/pythonocc-core` for further instructions.
4545

46-
| Package | Version | How to install (precompiled binaries via conda) |
47-
|---------|-------------|----------------------------------------------------------------------------------------------------------|
48-
| OCC | ==0.18.1 | Python2.7 `conda install -c conda-forge -c dlr-sc -c pythonocc -c oce pythonocc-core==0.18.1 python=2.7` |
49-
| OCC | ==0.18.1 | Python3.6 `conda install -c conda-forge -c dlr-sc -c pythonocc -c oce pythonocc-core==0.18.1 python=3.6` |
46+
| Package | Version | How to install (precompiled binaries via conda) |
47+
|---------|-------------|----------------------------------------------------------------------------------------------------------------------|
48+
| OCC | ==0.18.1 | Python2.7 `conda install -c conda-forge -c dlr-sc -c pythonocc -c oce pythonocc-core==0.18.1 smesh=6.7.6 python=2.7` |
49+
| OCC | ==0.18.1 | Python3.6 `conda install -c conda-forge -c dlr-sc -c pythonocc -c oce pythonocc-core==0.18.1 smesh=6.7.6 python=3.6` |
5050

51+
conda install -c pythonocc -c dlr-sc -c oce smesh=6.7.6
5152

5253
### Installing from source
5354
The official distribution is on GitHub, and you can clone the repository using

bladex/blade.py

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -662,19 +662,18 @@ def _write_blade_errors(self, upper_face, lower_face, errors):
662662
vertex = BRepBuilderAPI_MakeVertex(
663663
gp_Pnt(
664664
1000 * self.blade_coordinates_up[i][0][j],
665-
1000 * self.blade_coordinates_up[i][1][j],
666-
1000 * self.blade_coordinates_up[i][2][
667-
j])).Vertex()
665+
1000 * self.blade_coordinates_up[i][1][j], 1000
666+
* self.blade_coordinates_up[i][2][j])).Vertex()
668667
projection = BRepExtrema_DistShapeShape(
669668
self.generated_upper_face, vertex)
670669
projection.Perform()
671670
output_string += str(
672671
i) + '\t\t\t' + str(j) + '\t\t\t' + str(
673-
1000 * self.blade_coordinates_up[i][0]
674-
[j]) + '\t\t\t'
672+
1000 *
673+
self.blade_coordinates_up[i][0][j]) + '\t\t\t'
675674
output_string += str(
676-
1000 * self.blade_coordinates_up[i]
677-
[1][j]) + '\t\t\t' + str(
675+
1000 * self.blade_coordinates_up[i][1]
676+
[j]) + '\t\t\t' + str(
678677
1000 * self.blade_coordinates_up[i][2]
679678
[j]) + '\t\t\t' + str(projection.Value())
680679
output_string += '\n'
@@ -691,24 +690,23 @@ def _write_blade_errors(self, upper_face, lower_face, errors):
691690
gp_Pnt(
692691
1000 * self.blade_coordinates_down[i][0][j],
693692
1000 * self.blade_coordinates_down[i][1][j],
694-
1000 * self.blade_coordinates_down[i][2][
695-
j])).Vertex()
693+
1000 *
694+
self.blade_coordinates_down[i][2][j])).Vertex()
696695
projection = BRepExtrema_DistShapeShape(
697696
self.generated_lower_face, vertex)
698697
projection.Perform()
699698
output_string += str(
700699
i) + '\t\t\t' + str(j) + '\t\t\t' + str(
701-
1000 * self.blade_coordinates_down[i][0]
702-
[j]) + '\t\t\t'
700+
1000 *
701+
self.blade_coordinates_down[i][0][j]) + '\t\t\t'
703702
output_string += str(
704-
1000 * self.blade_coordinates_down[i]
705-
[1][j]) + '\t\t\t' + str(
703+
1000 * self.blade_coordinates_down[i][1]
704+
[j]) + '\t\t\t' + str(
706705
1000 * self.blade_coordinates_down[i][2]
707706
[j]) + '\t\t\t' + str(projection.Value())
708707
output_string += '\n'
709708
f.write(output_string)
710709

711-
712710
def generate_iges(self,
713711
upper_face=None,
714712
lower_face=None,
@@ -781,7 +779,8 @@ def generate_iges(self,
781779
self._check_string(filename=errors)
782780
self._check_errors(upper_face=upper_face, lower_face=lower_face)
783781

784-
self. _write_blade_errors(upper_face=upper_face, lower_face=lower_face, errors=errors)
782+
self._write_blade_errors(
783+
upper_face=upper_face, lower_face=lower_face, errors=errors)
785784

786785
if display:
787786
display, start_display, add_menu, add_function_to_menu = init_display(
@@ -805,7 +804,7 @@ def generate_stl(self, min_length=None, max_length=None, outfile_stl=None):
805804
and http://docs.salome-platform.org/7/gui/SMESH/index.html for
806805
further details.
807806
808-
This method requires PythonOCC and SMESH to be installed.
807+
This method requires PythonOCC and SMESH to be installed.
809808
810809
:param double min_length: smallest distance between two nodes. Default
811810
value is None
@@ -825,24 +824,32 @@ def generate_stl(self, min_length=None, max_length=None, outfile_stl=None):
825824
manual mesh healing is recommended by the user (e.g. see
826825
"Repair > Sewing" in SALOME GUI) for a proper mesh closure.
827826
"""
828-
from OCC.SMESH import SMESH_Gen, SMESH_MeshVSLink
829-
from OCC.StdMeshers import (StdMeshers_Arithmetic1D, StdMeshers_TrianglePreference,
830-
StdMeshers_Regular_1D, StdMeshers_Quadrangle_2D,
831-
StdMeshers_MEFISTO_2D)
827+
from OCC.SMESH import SMESH_Gen
828+
from OCC.StdMeshers import (
829+
StdMeshers_Arithmetic1D, StdMeshers_TrianglePreference,
830+
StdMeshers_Regular_1D, StdMeshers_MEFISTO_2D)
832831
from OCC.BRep import BRep_Builder
833832
from OCC.TopoDS import TopoDS_Shape, TopoDS_Compound
834833

834+
if min_length <= 0 or max_length <= 0:
835+
raise ValueError('min_length and max_length must be positive.')
836+
if min_length >= max_length:
837+
raise ValueError('min_length can not be greater than max_length')
838+
835839
# First we check that blade shapes are generated, otherwise we generate
836840
# them. After that we combine the generated_upper_face,
837841
# generated_lower_face, and generated_tip into a topological compound
838842
# that we use to compute the surface mesh
839-
if (self.generated_upper_face is None) or not isinstance(self.generated_upper_face, TopoDS_Shape):
843+
if (self.generated_upper_face is None) or not isinstance(
844+
self.generated_upper_face, TopoDS_Shape):
840845
# Upper face is generated with a maximal U degree = 1
841846
self._generate_upper_face(maxDeg=1)
842-
if (self.generated_lower_face is None) or not isinstance(self.generated_lower_face, TopoDS_Shape):
847+
if (self.generated_lower_face is None) or not isinstance(
848+
self.generated_lower_face, TopoDS_Shape):
843849
# Upper face is generated with a maximal U degree = 1
844850
self._generate_lower_face(maxDeg=1)
845-
if (self.generated_tip is None) or not isinstance(self.generated_tip, TopoDS_Shape):
851+
if (self.generated_tip is None) or not isinstance(
852+
self.generated_tip, TopoDS_Shape):
846853
# Upper face is generated with a maximal U degree = 1
847854
self._generate_tip(maxDeg=1)
848855

@@ -851,9 +858,9 @@ def generate_stl(self, min_length=None, max_length=None, outfile_stl=None):
851858
aBuilder = BRep_Builder()
852859
aBuilder.MakeCompound(aCompound)
853860
# Add shapes
854-
aBuilder.Add(aCompound,self.generated_upper_face)
855-
aBuilder.Add(aCompound,self.generated_lower_face)
856-
aBuilder.Add(aCompound,self.generated_tip)
861+
aBuilder.Add(aCompound, self.generated_upper_face)
862+
aBuilder.Add(aCompound, self.generated_lower_face)
863+
aBuilder.Add(aCompound, self.generated_tip)
857864

858865
# In the following we build the surface mesh according to the given
859866
# hypotheses
@@ -887,12 +894,14 @@ def generate_stl(self, min_length=None, max_length=None, outfile_stl=None):
887894
aMesh.AddHypothesis(aCompound, 2)
888895
aMesh.AddHypothesis(aCompound, 3)
889896

890-
#Compute the data
891-
aMeshGen.Compute(aMesh, aMesh.GetShapeToMesh())
892-
893897
if outfile_stl is not None:
894-
assert isinstance(outfile_stl, str), "outfile_stl must be a valid string."
895-
aMesh.ExportSTL(outfile_stl+'.stl', False)
898+
if not isinstance(outfile_stl, str):
899+
raise ValueError('outfile_stl must be a valid string.')
900+
901+
#Compute the data
902+
aMeshGen.Compute(aMesh, aMesh.GetShapeToMesh())
903+
# Export STL
904+
aMesh.ExportSTL(outfile_stl + '.stl', False)
896905

897906
@staticmethod
898907
def _check_string(filename):

tests/test_blade.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import matplotlib.pyplot as plt
77
from mpl_toolkits.mplot3d import Axes3D
8+
from OCC.TopoDS import TopoDS_Shape
89

910

1011
def create_sample_blade_NACA():
@@ -265,6 +266,18 @@ def test_blade_coordinates_down_init(self):
265266
blade = create_sample_blade_NACA()
266267
assert len(blade.blade_coordinates_down) == 0
267268

269+
def test_blade_generated_upper_face_init(self):
270+
blade = create_sample_blade_NACA()
271+
assert blade.generated_upper_face == None
272+
273+
def test_blade_generated_lower_face_init(self):
274+
blade = create_sample_blade_NACA()
275+
assert blade.generated_lower_face == None
276+
277+
def test_blade_generated_tip_init(self):
278+
blade = create_sample_blade_NACA()
279+
assert blade.generated_tip == None
280+
268281
def test_planar_to_cylindrical_blade_up(self):
269282
blade = create_sample_blade_NACA()
270283
blade._planar_to_cylindrical()
@@ -550,6 +563,48 @@ def test_iges_generate_errors_lower(self):
550563
self.assertTrue(os.path.isfile('tests/test_datasets/errors.txt'))
551564
self.addCleanup(os.remove, 'tests/test_datasets/errors.txt')
552565

566+
def test_stl_exception_1(self):
567+
blade = create_sample_blade_NACA()
568+
blade.apply_transformations()
569+
with self.assertRaises(ValueError):
570+
blade.generate_stl(min_length=-1, max_length=1, outfile_stl=None)
571+
572+
def test_stl_exception_2(self):
573+
blade = create_sample_blade_NACA()
574+
blade.apply_transformations()
575+
with self.assertRaises(ValueError):
576+
blade.generate_stl(min_length=2, max_length=1, outfile_stl=None)
577+
578+
def test_stl_generated_upper(self):
579+
# Requires OCC to be installed
580+
blade = create_sample_blade_NACA()
581+
blade.apply_transformations()
582+
blade.generated_upper_face = 5
583+
blade.generate_stl(min_length=1, max_length=10, outfile_stl=None)
584+
self.assertIsInstance(blade.generated_upper_face, TopoDS_Shape)
585+
586+
def test_stl_generated_lower(self):
587+
# Requires OCC to be installed
588+
blade = create_sample_blade_NACA()
589+
blade.apply_transformations()
590+
blade.generated_lower_face = None
591+
blade.generate_stl(min_length=1, max_length=10, outfile_stl=None)
592+
self.assertIsInstance(blade.generated_lower_face, TopoDS_Shape)
593+
594+
def test_stl_generated_tip(self):
595+
# Requires OCC to be installed
596+
blade = create_sample_blade_NACA()
597+
blade.apply_transformations()
598+
blade.generated_tip = 0
599+
blade.generate_stl(min_length=1, max_length=10, outfile_stl=None)
600+
self.assertIsInstance(blade.generated_tip, TopoDS_Shape)
601+
602+
def test_stl_export_exception(self):
603+
blade = create_sample_blade_NACA()
604+
blade.apply_transformations()
605+
with self.assertRaises(ValueError):
606+
blade.generate_stl(min_length=1, max_length=10, outfile_stl=55)
607+
553608
def test_abs_to_norm_radii(self):
554609
blade = create_sample_blade_NACA()
555610
blade.radii[0] = 1.

0 commit comments

Comments
 (0)