Skip to content

Commit d92175a

Browse files
committed
Improve speed and quality of optimization
1 parent 687e663 commit d92175a

File tree

11 files changed

+60
-71
lines changed

11 files changed

+60
-71
lines changed

.vscode/settings.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
"source.organizeImports.ruff": "always"
88
},
99
},
10-
"python.analysis.typeCheckingMode": "basic",
11-
"python.analysis.diagnosticMode": "openFilesOnly",
1210
"python.testing.pytestEnabled": false,
1311
"files.exclude": {
1412
"**/.git": true,

classy_blocks.code-workspace

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@
1313
"scipy"
1414
],
1515
"editor.autoClosingBrackets": "never",
16-
"isort.importStrategy": "fromEnvironment",
17-
"editor.defaultFormatter": null,
16+
"editor.defaultFormatter": "charliermarsh.ruff",
1817
"ruff.organizeImports": true
1918
},
2019
"extensions": {
2120
"recommendations": [
2221
"ms-python.vscode-pylance",
2322
"ms-python.python",
24-
"charliermarsh.ruff",
25-
"ms-python.black-formatter"
23+
"charliermarsh.ruff"
2624
]
2725
},
2826
}
-5.57 KB
Binary file not shown.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license = "MIT"
77
keywords = ["classy_blocks", "OpenFOAM", "blockMesh"]
88
authors = [{ name = "Nejc Jurkovic", email = "kandelabr@gmail.com" }]
99
requires-python = ">=3.8"
10-
dependencies = ["numpy", "scipy", "nptyping", "numba"]
10+
dependencies = ["numpy", "scipy", "nptyping", "numba", "parameterized"]
1111

1212
[project.urls]
1313
"Homepage" = "https://github.com/damogranlabs/classy_blocks"

src/classy_blocks/optimize/optimizer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def fquality(params):
4949
return self.grid.update(junction.index, clamp.position)
5050

5151
try:
52-
scipy.optimize.minimize(fquality, clamp.params, bounds=clamp.bounds, method=method)
52+
scipy.optimize.minimize(fquality, clamp.params, bounds=clamp.bounds, method=method, tol=1e-2)
5353

5454
reporter.junction_final = junction.quality
5555
reporter.grid_final = self.grid.quality

src/classy_blocks/optimize/quality.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ def scale_quality(base: float, exponent: float, factor: float, value: float) ->
1717

1818
@numba.jit(nopython=True, cache=True)
1919
def scale_non_ortho(angle: float) -> float:
20-
return scale_quality(1.25, 0.35, 0.8, angle)
20+
return scale_quality(1.5, 0.25, 0.5, angle)
2121

2222

2323
@numba.jit(nopython=True, cache=True)
2424
def scale_inner_angle(angle: float) -> float:
25-
return scale_quality(1.5, 0.25, 0.15, np.abs(angle))
25+
return scale_quality(1.5, 0.25, 0.5, np.abs(angle))
2626

2727

2828
@numba.jit(nopython=True, cache=True)
@@ -146,12 +146,12 @@ def get_hex_quality(grid_points: NPPointListType, cell_indexes: NPIndexType) ->
146146
quality = 0
147147

148148
for side in side_indexes:
149-
# Non-ortho angle is measured between two lines;
150-
# neighbour_center-to-this_center and face_center-to-this_center,
151-
# but for cells on the boundary simply face center is taken.
152-
# For this kind of optimization it is quite sufficient to take
153-
# only the latter as it's not much different and we'll optimize
154-
# other cells too.
149+
# Non-ortho angle in a hexahedron is measured between two vectors:
150+
# <neighbour_center-this_center> and <face_normal>
151+
# but since cells on the boundary don't have a neighbour
152+
# simply <face_center> is taken.
153+
# For this kind of optimization it is quite sufficient to
154+
# take <face_center> for all cells.
155155
side_points = take(cell_points, side)
156156
side_center, side_normal, side_aspect = get_quad_normal(side_points)
157157
center_vector = cell_center - side_center

src/classy_blocks/util/functions.py

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -61,27 +61,40 @@ def angle_between(vect_1: VectorType, vect_2: VectorType) -> float:
6161
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
6262

6363

64-
def rotation_matrix(axis: VectorType, theta: float):
65-
"""
66-
Return the rotation matrix associated with counterclockwise rotation about
67-
the given axis by theta radians.
68-
# Kudos to
64+
@jit(nopython=True, cache=True)
65+
def _rotation_matrix(axis: NPVectorType, angle: float):
66+
axis = axis / np.sqrt(np.dot(axis, axis))
67+
a = np.cos(angle / 2.0)
68+
b, c, d = -axis * np.sin(angle / 2.0)
69+
aa, bb, cc, dd = a * a, b * b, c * c, d * d
70+
bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
71+
72+
return np.array(
73+
[
74+
[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
75+
[2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
76+
[2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc],
77+
]
78+
)
79+
80+
81+
def rotation_matrix(axis: VectorType, angle: float):
82+
"""Returns the rotation matrix associated with counterclockwise rotation about
83+
the given axis by theta radians."""
84+
# Kudos to https://stackoverflow.com/questions/6802577/rotation-of-3d-vector
85+
axis = np.asarray(axis, dtype=constants.DTYPE)
86+
87+
return _rotation_matrix(axis, angle)
88+
89+
90+
@jit(nopython=True, cache=True)
91+
def _rotate(point: NPPointType, angle: float, axis: NPVectorType, origin: NPPointType) -> NPPointType:
92+
# Rotation matrix associated with counterclockwise rotation about
93+
# the given axis by theta radians. Kudos to
6994
# https://stackoverflow.com/questions/6802577/rotation-of-3d-vector
70-
#import math
71-
#
72-
# axis = np.asarray(axis)
73-
# axis = axis / math.sqrt(np.dot(axis, axis))
74-
# a = math.cos(theta / 2.0)
75-
# b, c, d = -axis * math.sin(theta / 2.0)
76-
# aa, bb, cc, dd = a * a, b * b, c * c, d * d
77-
# bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
78-
# return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
79-
# [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
80-
# [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])
81-
82-
# Also Kudos to the guy with another answer for the same question (used here):"""
83-
axis = np.asarray(axis)
84-
return scipy.linalg.expm(np.cross(np.eye(3), axis / norm(axis) * theta))
95+
96+
rotated_point = np.dot(_rotation_matrix(axis, angle), point - origin)
97+
return rotated_point + origin
8598

8699

87100
def rotate(point: PointType, angle: float, axis: VectorType, origin: PointType) -> NPPointType:
@@ -90,8 +103,7 @@ def rotate(point: PointType, angle: float, axis: VectorType, origin: PointType)
90103
axis = np.asarray(axis, dtype=constants.DTYPE)
91104
origin = np.asarray(origin, dtype=constants.DTYPE)
92105

93-
rotated_point = np.dot(rotation_matrix(axis, angle), point - origin)
94-
return rotated_point + origin
106+
return _rotate(point, angle, axis, origin)
95107

96108

97109
def scale(point: PointType, ratio: float, origin: Optional[PointType]) -> NPPointType:
@@ -201,14 +213,6 @@ def arc_length_3point(p_start: NPPointType, p_btw: NPPointType, p_end: NPPointTy
201213
return angle * np.linalg.norm(radius)
202214

203215

204-
# def arc_length_3point(p_start: NPPointType, p_btw: NPPointType, p_end: NPPointType) -> float:
205-
# p_start = np.asarray(p_start, dtype=np.float64)
206-
# p_btw = np.asarray(p_btw, dtype=np.float64)
207-
# p_end = np.asarray(p_end, dtype=np.float64)
208-
209-
# return _arc_length_3point(p_start, p_btw, p_end)
210-
211-
212216
@jit(nopython=True, cache=True)
213217
def divide_arc(center: NPPointType, point_1: NPPointType, point_2: NPPointType, count: int) -> NPPointListType:
214218
radius = np.linalg.norm(center - point_1)
@@ -225,17 +229,6 @@ def divide_arc(center: NPPointType, point_1: NPPointType, point_2: NPPointType,
225229
return result
226230

227231

228-
# def divide_arc(center: PointType, point_1: PointType, point_2: PointType, count: int) -> NPPointListType:
229-
# # Kudos to this guy for his shrewd solution
230-
# # https://math.stackexchange.com/questions/3717427
231-
# # (extended here to create more than 1 point)
232-
# center = np.asarray(center, dtype=np.float64)
233-
# point_1 = np.asarray(point_1, dtype=np.float64)
234-
# point_2 = np.asarray(point_2, dtype=np.float64)
235-
236-
# return _divide_arc(center, point_1, point_2, count)
237-
238-
239232
def arc_mid(center: PointType, point_1: PointType, point_2: PointType) -> PointType:
240233
"""Returns the midpoint of the specified arc in 3D space"""
241234
return divide_arc(center, point_1, point_2, 1)[0]

tests/test_construct/test_chaining.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def check_success(self, chained_shape, end_center):
4545
self.assertEqual(len(self.mesh.blocks), 24)
4646
self.assertEqual(len(self.mesh.vertices), 3 * 17)
4747

48-
np.testing.assert_allclose(chained_shape.sketch_2.center, end_center)
48+
np.testing.assert_allclose(chained_shape.sketch_2.center, end_center, atol=1e-7)
4949

5050
def test_to_elbow_end(self):
5151
"""Chain an elbow to an elbow on an end sketch"""

tests/test_items/test_edge.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ def test_arc_edge_translate(self):
2828
arc_edge = ArcEdge(self.vertex_1, self.vertex_2, edges.Arc([0.5, 0, 0]))
2929

3030
arc_edge.translate([1, 1, 1])
31-
np.testing.assert_array_equal(arc_edge.data.point.position, [1.5, 1, 1])
31+
np.testing.assert_almost_equal(arc_edge.data.point.position, [1.5, 1, 1])
3232

3333
def test_angle_edge_rotate_1(self):
3434
angle_edge = AngleEdge(self.vertex_1, self.vertex_2, edges.Angle(np.pi / 2, [0, 0, 1]))
3535

3636
angle_edge.rotate(np.pi / 2, [0, 0, 1], [0, 0, 0])
3737

38-
np.testing.assert_array_equal(angle_edge.data.axis.components, [0, 0, 1])
38+
np.testing.assert_almost_equal(angle_edge.data.axis.components, [0, 0, 1])
3939

4040
self.assertEqual(angle_edge.data.angle, np.pi / 2)
4141

@@ -44,7 +44,7 @@ def test_angle_edge_rotate_2(self):
4444

4545
angle_edge.rotate(np.pi / 2, [1, 0, 0], [0, 0, 0])
4646

47-
np.testing.assert_array_equal(angle_edge.data.axis.components, [0, -1, 0])
47+
np.testing.assert_almost_equal(angle_edge.data.axis.components, [0, -1, 0])
4848

4949
self.assertEqual(angle_edge.data.angle, np.pi / 2)
5050

@@ -63,7 +63,7 @@ def test_spline_edge_translate(self):
6363

6464
spline_edge.translate([1, 1, 1])
6565

66-
np.testing.assert_array_equal(
66+
np.testing.assert_almost_equal(
6767
spline_edge.point_array,
6868
[
6969
[1.25, 1.1, 1],
@@ -119,21 +119,21 @@ def test_angle(self):
119119

120120
self.assertIsInstance(edge, AngleEdge)
121121
self.assertEqual(angle, edge.data.angle)
122-
np.testing.assert_array_equal(axis, edge.data.axis.components)
122+
np.testing.assert_almost_equal(axis, edge.data.axis.components)
123123

124124
def test_spline(self):
125125
points = [[0.3, 0.25, 0], [0.6, 0.1, 0], [0.3, 0.25, 0]]
126126
edg = factory.create(self.vertex_1, self.vertex_2, edges.Spline(points))
127127

128128
self.assertIsInstance(edg, SplineEdge)
129-
np.testing.assert_array_equal(points, edg.point_array)
129+
np.testing.assert_almost_equal(points, edg.point_array)
130130

131131
def test_polyline(self):
132132
points = [[0.3, 0.25, 0], [0.6, 0.1, 0], [0.3, 0.25, 0]]
133133
edg = factory.create(self.vertex_1, self.vertex_2, edges.PolyLine(points))
134134

135135
self.assertIsInstance(edg, SplineEdge)
136-
np.testing.assert_array_equal(points, edg.point_array)
136+
np.testing.assert_almost_equal(points, edg.point_array)
137137

138138
def test_project_edge_single(self):
139139
label = "terrain"

tests/test_optimize/test_links.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def test_translate(self):
1414
link.leader = np.array([3, 3, 3])
1515
link.update()
1616

17-
np.testing.assert_equal(link.follower, [4, 4, 4])
17+
np.testing.assert_almost_equal(link.follower, [4, 4, 4])
1818

1919

2020
class RotationLinkTests(unittest.TestCase):
@@ -36,14 +36,14 @@ def test_radius(self, origin):
3636

3737
link = RotationLink(leader, follower, [0, 0, 1], origin)
3838

39-
np.testing.assert_equal(link._get_radius(link.leader), [1, 0, 0])
39+
np.testing.assert_almost_equal(link._get_radius(link.leader), [1, 0, 0])
4040

4141
def test_rotate(self):
4242
link = RotationLink(self.leader, self.follower, [0, 0, 1], [0, 0, 0])
4343
link.leader = np.array([0, 1, 0])
4444
link.update()
4545

46-
np.testing.assert_equal(link.follower, [-1, 0, 0])
46+
np.testing.assert_almost_equal(link.follower, [-1, 0, 0])
4747

4848
def test_rotate_negative(self):
4949
"""Rotate in negative direction"""
@@ -52,7 +52,7 @@ def test_rotate_negative(self):
5252
link.leader = np.array([0, -1, 0])
5353
link.update()
5454

55-
np.testing.assert_equal(link.follower, [1, 0, 0])
55+
np.testing.assert_almost_equal(link.follower, [1, 0, 0])
5656

5757
@parameterized.expand(
5858
(
@@ -108,4 +108,4 @@ def test_move(self):
108108
link.leader = np.array([0, 0, 2])
109109
link.update()
110110

111-
np.testing.assert_equal(link.follower, [0, 0, -2])
111+
np.testing.assert_almost_equal(link.follower, [0, 0, -2])

0 commit comments

Comments
 (0)