Skip to content

Commit 8382263

Browse files
committed
Merge branch 'development' of https://github.com/damogranlabs/classy_blocks into development
2 parents 7fc2d1c + d7dc183 commit 8382263

File tree

6 files changed

+115
-101
lines changed

6 files changed

+115
-101
lines changed

examples/complex/cyclone/cyclone.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
from typing import List
44

5+
import numpy as np
56
import parameters as params
67
from geometry import geometry
78
from regions.body import ChainSketch, Cone, LowerBody, UpperBody
@@ -14,6 +15,7 @@
1415
from regions.skirt import Skirt
1516

1617
import classy_blocks as cb
18+
from classy_blocks.util import functions as f
1719

1820
mesh = cb.Mesh()
1921

@@ -47,9 +49,30 @@ def add_regions(regions: List[Region]) -> None:
4749
# This bad blocking needs to be improved before
4850
# making further blocks. It is done on a semi-finished mesh:
4951
mesh.assemble()
50-
# Also, when Mesh is assembled, vertices are created that have
51-
# consistent indexes; those are used in Region.get_clamps() to quickly
52-
# fetch points (no need for cb.*Finders)
52+
53+
# Now coincident points have been merged into Vertices and each got its own index.
54+
# We do mesh.write(..., debug_path="debug.vtk") and view debug.vtk's points in ParaView.
55+
# We'll redistribute (and fix) inner ring points evenly or optimization
56+
# will find a better solution with a thin block in the
57+
vindexes = [68, 69, 70, 84, 82, 80, 78, 76, 74, 71, 67, 66]
58+
current_angles = [f.to_polar(mesh.vertices[i].position, axis="z")[1] for i in vindexes]
59+
angle_offset = current_angles[0]
60+
uniform_angles = np.linspace(2 * np.pi, 0, num=len(current_angles), endpoint=False) + angle_offset
61+
62+
for i, vindex in enumerate(vindexes):
63+
position = mesh.vertices[vindex].position
64+
polar = f.to_polar(position, axis="z")
65+
polar[1] = uniform_angles[i]
66+
mesh.vertices[vindex].move_to(f.to_cartesian(polar, axis="z"))
67+
68+
# correct points that created invalid cells
69+
neighbours = [mesh.vertices[i].position for i in (30, 57, 84, 70)]
70+
mesh.vertices[31].move_to(np.average(neighbours, axis=0))
71+
72+
neighbours = [mesh.vertices[i].position for i in (25, 67, 71, 33)]
73+
mesh.vertices[22].move_to(np.average(neighbours, axis=0))
74+
75+
# Now, optimize the bad blocks in the inlet.
5376
optimizer = cb.MeshOptimizer(mesh)
5477

5578
clamps = []
@@ -84,7 +107,7 @@ def add_regions(regions: List[Region]) -> None:
84107

85108
add_regions([upper, lower, pipe])
86109

87-
# Core is a 12-block cylinder, created from 12 points of pipe ring.
110+
# Core is a 9-block-core cylinder, created from 12 points of pipe ring.
88111
# Again, instead of finding those 12 points (which are difficult to find and sort)
89112
# it is easier to re-assemble the mesh and get the points from debug.vtk in ParaView.
90113
mesh.assemble()

examples/complex/cyclone/regions/core.py

Lines changed: 62 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,104 @@
1-
from typing import List, Sequence
1+
from typing import ClassVar, List, Sequence
22

33
import numpy as np
44
import parameters as params
55
from regions.region import Region
66

77
import classy_blocks as cb
88
from classy_blocks.base.transforms import Transformation, Translation
9-
from classy_blocks.construct.flat.sketch import Sketch
9+
from classy_blocks.construct.point import Point
1010
from classy_blocks.construct.shapes.round import RoundSolidShape
11-
from classy_blocks.types import NPPointListType, NPVectorType, PointListType, PointType
12-
from classy_blocks.util import functions as f
13-
14-
15-
class DozenBlockDisk(Sketch):
16-
"""A disk that has 3x3 inside quads and 12 outer
17-
See sketch for explanations"""
18-
19-
layer_1_ratio = 0.25
20-
layer_2_ratio = 0.55
21-
22-
face_map = [ # for creating blocks from points-by-layer
23-
[[0, 0], [0, 1], [0, 2], [0, 3]], # core: 0
24-
[[0, 0], [1, 11], [1, 0], [1, 1]], # layer 1: 1
25-
[[0, 0], [1, 1], [1, 2], [0, 1]], # 2
26-
[[0, 1], [1, 2], [1, 3], [1, 4]], # 3
27-
[[0, 1], [1, 4], [1, 5], [0, 2]], # 4
28-
[[0, 2], [1, 5], [1, 6], [1, 7]], # 5
29-
[[0, 2], [1, 7], [1, 8], [0, 3]], # 6
30-
[[0, 3], [1, 8], [1, 9], [1, 10]], # 7
31-
[[0, 3], [1, 10], [1, 11], [0, 0]], # 8
32-
[[1, 0], [2, 0], [2, 1], [1, 1]], # layer 3: 9
33-
[[1, 1], [2, 1], [2, 2], [1, 2]], # 10
34-
[[1, 2], [2, 2], [2, 3], [1, 3]], # 11
35-
[[1, 3], [2, 3], [2, 4], [1, 4]], # 12
36-
[[1, 4], [2, 4], [2, 5], [1, 5]], # 13
37-
[[1, 5], [2, 5], [2, 6], [1, 6]], # 14
38-
[[1, 6], [2, 6], [2, 7], [1, 7]], # 15
39-
[[1, 7], [2, 7], [2, 8], [1, 8]], # 16
40-
[[1, 8], [2, 8], [2, 9], [1, 9]], # 17
41-
[[1, 9], [2, 9], [2, 10], [1, 10]], # 18
42-
[[1, 10], [2, 10], [2, 11], [1, 11]], # 19
43-
[[1, 11], [2, 11], [2, 0], [1, 0]], # 20
11+
from classy_blocks.types import NPVectorType, PointListType, PointType
12+
13+
14+
class NineCoreDisk(cb.MappedSketch):
15+
"""A disk that has 3x3 inside quads and 12 outer;
16+
see docs/cylinder.svg for a sketch"""
17+
18+
quads: ClassVar = [
19+
# core
20+
[0, 1, 2, 3], # 0
21+
# layer 1
22+
[0, 15, 4, 5], # 1
23+
[0, 5, 6, 1], # 2
24+
[1, 6, 7, 8], # 3
25+
[1, 8, 9, 2], # 4
26+
[2, 9, 10, 11], # 5
27+
[2, 11, 12, 3], # 6
28+
[3, 12, 13, 14], # 7
29+
[3, 14, 15, 0], # 8
30+
# layer 2
31+
[4, 16, 17, 5], # 9
32+
[5, 17, 18, 6], # 10
33+
[6, 18, 19, 7], # 11
34+
[7, 19, 20, 8], # 12
35+
[8, 20, 21, 9], # 13
36+
[9, 21, 22, 10], # 14
37+
[10, 22, 23, 11], # 15
38+
[11, 23, 24, 12], # 16
39+
[12, 24, 25, 13], # 17
40+
[13, 25, 26, 14], # 18
41+
[14, 26, 27, 15], # 19
42+
[15, 27, 16, 4], # 20
4443
]
4544

46-
# TODO: drop this and inherit from MappedSketch
47-
neighbours = [ # for laplacian smoothing of the inside
48-
[15, 5, 1, 3], # 0
49-
[0, 6, 8, 2], # 1
50-
[9, 1, 11, 3], # 2
51-
[14, 0, 2, 12], # 3
52-
[16, 5, 15], # 4
53-
[4, 17, 6, 0], # 5
54-
[18, 7, 1, 5], # 6
55-
[6, 19, 8], # 7
56-
[1, 7, 20, 9], # 8
57-
[2, 8, 21, 10], # 9
58-
[22, 11, 9], # 10
59-
[23, 10, 2, 12], # 11
60-
[11, 24, 13, 3], # 12
61-
[25, 12, 14], # 13
62-
[13, 3, 15, 26], # 14
63-
[14, 27, 4, 0], # 15
64-
]
65-
66-
def _smooth_points(self, points: NPPointListType):
67-
# A very rudimentary 2D laplacian smoothing;
68-
# to be replaced with automatic neighbour search,
69-
# removing the need for this 'neighbours' map
70-
for i, nei_indexes in enumerate(self.neighbours):
71-
nei_points = np.take(points, nei_indexes, axis=0)
72-
points[i] = np.average(nei_points, axis=0)
73-
7445
def __init__(self, perimeter: PointListType, center_point: PointType):
75-
# TODO: add a class method that creates this kind of shapes from perimeter
76-
self.perimeter = np.array(perimeter)
7746
center_point = np.asarray(center_point)
7847

79-
# calculate 3 layers of points;
80-
# 1st layer, square, 4 points
81-
# 2nd layer, 3x3 squares, 9 points
82-
# 3rd layer, 12 shell faces, 12 points
83-
layer_2 = np.array([center_point + self.layer_2_ratio * (p - center_point) for p in self.perimeter])
84-
layer_1 = np.array([center_point + self.layer_1_ratio * (layer_2[i] - center_point) for i in (0, 3, 6, 9)])
48+
# inner points will be determined with smoothing;
49+
# a good enough starting estimate is the center
50+
# (anything in the same plane as other points)
51+
outer_points = np.asarray(perimeter)
52+
inner_points = np.ones((16, 3)) * np.average(outer_points, axis=0)
8553

86-
# Assemble a full list for smoothing:
87-
points_by_index = np.concatenate((layer_1, layer_2, self.perimeter))
54+
positions = np.concatenate((inner_points, outer_points))
8855

89-
for _ in range(5):
90-
self._smooth_points(points_by_index)
56+
self.center_point = Point(center_point)
9157

92-
# reconstruct the list back to layer_1, layer_2, layer_3 for face creation
93-
points_by_layer = [points_by_index[:4], points_by_index[4:16], points_by_index[16:]]
58+
super().__init__(positions, self.quads)
9459

95-
# the first point is layer index, the second is the point within layer
96-
self._faces: List[cb.Face] = []
60+
# smooth the inner_points (which are all invalid) into position
61+
smoother = cb.SketchSmoother(self)
62+
smoother.smooth()
9763

98-
for face_indexes in self.face_map:
99-
face = cb.Face([points_by_layer[i[0]][i[1]] for i in face_indexes])
100-
self._faces.append(face)
64+
@property
65+
def core(self) -> List[cb.Face]:
66+
return self.faces[:9]
10167

102-
self.core = self._faces[:10]
103-
self.shell = self._faces[10:]
68+
@property
69+
def shell(self) -> List[cb.Face]:
70+
return self.faces[9:]
10471

72+
def add_edges(self):
10573
for face in self.shell:
106-
face.add_edge(1, cb.Origin(center_point))
74+
face.add_edge(1, cb.Origin(self.center_point.position))
10775

10876
@property
109-
def faces(self):
110-
return self._faces
77+
def parts(self):
78+
return [*super().parts, self.center_point]
11179

11280
@property
113-
def grid(self):
114-
return [self.faces]
81+
def center(self):
82+
return self.center_point.position
11583

11684
@property
117-
def center(self):
118-
return self.faces[0].center
85+
def grid(self):
86+
return [[self.faces[0]], self.faces[1:9], self.faces[9:]]
11987

12088
@property
12189
def normal(self) -> NPVectorType:
122-
return f.unit_vector(np.cross(self.perimeter[0] - self.center, self.perimeter[1] - self.center))
90+
return self.faces[0].normal
12391

12492
@property
12593
def n_segments(self):
12694
return 12
12795

12896

12997
class DozenBlockCylinder(RoundSolidShape):
130-
sketch_class = DozenBlockDisk
98+
sketch_class = NineCoreDisk
13199

132100
def __init__(self, perimeter: PointListType, center_point: PointType, length: float):
133-
sketch = DozenBlockDisk(perimeter, center_point)
101+
sketch = NineCoreDisk(perimeter, center_point)
134102
transforms: Sequence[Transformation] = [Translation(sketch.normal * length)]
135103
super().__init__(sketch, transforms)
136104

examples/complex/cyclone/regions/inlet.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from regions.region import Region
44

55
import classy_blocks as cb
6-
from classy_blocks.construct.edges import Line
76
from classy_blocks.construct.point import Point
87
from classy_blocks.util import functions as f
98

9+
# Helps with optimization of this tricky part
10+
cb.HalfDisk.core_ratio = 0.6
11+
1012

1113
class InletPipe(Region):
1214
line_clamps = {20, 18, 16, 13, 12}

examples/complex/cyclone/regions/inner_ring.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@
1111
class InnerRing(Region):
1212
"""A ring, created by extruding innermost faces of given shapes"""
1313

14-
radial_clamps = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70}
14+
radial_clamps = {60, 61, 62, 63, 64, 65}
1515

1616
def _move_to_radius(self, point):
1717
polar = f.to_polar(point, axis="z")
1818
polar[0] = self.geo.r["pipe"]
1919

2020
return f.to_cartesian(polar, axis="z")
2121

22+
def _move_to_angle(self, point, angle):
23+
polar = f.to_polar(point, axis="z")
24+
polar[1] = angle
25+
26+
return f.to_cartesian(polar, axis="z")
27+
2228
def _reorient_face(self, face: cb.Face) -> None:
2329
# find the point with lowest z and lowest angle and
2430
# reorient face so that it starts with it

src/classy_blocks/construct/flat/sketches/mapped.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def merge_two_sketches(sketch_1: MappedSketch, sketch_2: MappedSketch) -> None:
9292
for i, face in enumerate(sketch_2.faces):
9393
for j, pos in enumerate(face.point_array):
9494
sketch_2_ind[i, j] = np.argwhere(
95-
np.linalg.norm(all_pos - pos.reshape((1, 3)), axis=1 < constants.TOL)
95+
np.linalg.norm(all_pos - pos.reshape((1, 3)), axis=1) < constants.TOL
9696
)[0][0]
9797

9898
# Append indexes and faces to sketch_1

tests/test_construct/test_flat/test_sketch.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ def test_update(self):
4949

5050
np.testing.assert_equal(sketch.faces[0].point_array[0], [0.1, 0.1, 0.1])
5151

52+
def test_merge(self):
53+
sketch_1_pos = self.positions[:6]
54+
sketch_2_pos = self.positions[3:]
55+
sketch_2_quad = (np.asarray(self.quads[2:]) - 3).tolist()
56+
57+
sketch_1 = MappedSketch(sketch_1_pos, self.quads[:2])
58+
sketch_2 = MappedSketch(sketch_2_pos, sketch_2_quad)
59+
sketch_1.merge(sketch_2)
60+
61+
np.testing.assert_equal(
62+
np.asarray([face.point_array for face in sketch_1.faces]),
63+
np.asarray([face.point_array for face in self.sketch.faces]),
64+
)
65+
self.assertEqual(sketch_1.indexes, self.sketch.indexes)
66+
5267

5368
class GridSketchTests(unittest.TestCase):
5469
def test_construct(self):

0 commit comments

Comments
 (0)