Skip to content
This repository was archived by the owner on Nov 13, 2023. It is now read-only.

Commit 934d3ae

Browse files
author
Keith Roberts
authored
improvements (#171)
* performance improvements in parallel.
1 parent 2601be6 commit 934d3ae

14 files changed

+382
-177
lines changed

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
593593
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
594594

595595
## Unreleased
596-
- Adding basic periodic domains with the Repeat SDF.
596+
597+
## [3.2.0] -2020-12-14
598+
- Adding basic periodic domains with the `Repeat` SDF.
599+
- Reworking CPP code and bottlenecks...20-30% faster `generate_mesh` in parallel for 2D/3D from previous versions.
600+
- `sliver_removal` has optional variable step size when perturbing vertices. Helps to remove the "last sliver".
601+
- Faster rectangle and cube primitives.
597602

598603
## [3.1.7] - 2020-11-27
599604
### Improved

SeismicMesh/generation/mesh_generator.py

+29-9
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def sliver_removal(points, domain, edge_length, comm=None, **kwargs): # noqa: C
114114
"delta_t": 0.30,
115115
"geps_mult": 0.1,
116116
"subdomains": [],
117+
"gamma": 1.0,
117118
}
118119

119120
sliver_opts.update(kwargs)
@@ -170,6 +171,7 @@ def print_msg2(msg):
170171
)
171172

172173
geps = sliver_opts["geps_mult"] * h0
174+
173175
# deps = np.sqrt(np.finfo(np.double).eps) * h0
174176
min_dh_bound = sliver_opts["min_dh_angle_bound"] * math.pi / 180
175177
max_dh_bound = sliver_opts["max_dh_angle_bound"] * math.pi / 180
@@ -197,10 +199,12 @@ def print_msg2(msg):
197199

198200
count = 0
199201
pold = None
200-
push = 0.10
202+
step = 0.10
203+
gamma = sliver_opts["gamma"]
204+
num_old_bad = np.inf
201205

202206
dt = DT()
203-
dt.insert(p.flatten().tolist())
207+
dt.insert(p.ravel().tolist())
204208
while True:
205209

206210
start = time.time()
@@ -209,7 +213,7 @@ def print_msg2(msg):
209213
# Using CGAL's incremental Delaunay triangulation capabilities.
210214
if count > 0:
211215
to_move = np.where(_dist(p, pold) > 0)[0]
212-
dt.move(to_move.flatten().tolist(), p[to_move].flatten().tolist())
216+
dt.move(to_move.ravel().tolist(), p[to_move].ravel().tolist())
213217

214218
# Get the current topology of the triangulation
215219
p, t = _get_topology(dt)
@@ -224,7 +228,7 @@ def print_msg2(msg):
224228
# Number of iterations reached, stop.
225229
if count == (max_iter - 1):
226230
print_msg1(
227-
"FAILURE: Termination...maximum number of iterations reached.",
231+
"FAILURE: Termination...maximum number of iterations reached. Try increasing max_iter when generating the mesh",
228232
)
229233
p, t = _termination(p, t, sliver_opts, comm, sliver=True)
230234
break
@@ -266,11 +270,26 @@ def print_msg2(msg):
266270
perturb_norm = np.sum(np.abs(perturb) ** 2, axis=-1) ** (1.0 / 2)
267271
perturb /= perturb_norm[:, None]
268272

269-
# perturb push % of minimum mesh size
270-
p[move] += push * h0 * perturb
273+
num_bad = len(ele_nums)
274+
275+
if num_bad < num_old_bad:
276+
# increase step
277+
step /= gamma
278+
elif num_bad > num_old_bad:
279+
# decrease step
280+
step *= gamma
281+
elif num_bad == num_old_bad:
282+
# algorithm appears stuck
283+
# increase step
284+
step /= 0.8
285+
286+
# perturb step % of minimum mesh size
287+
p[move] += step * h0 * perturb
271288

272289
count += 1
273290

291+
num_old_bad = len(ele_nums)
292+
274293
end = time.time()
275294
if comm.rank == 0:
276295
print_msg2(" Elapsed wall-clock time %f : " % (end - start))
@@ -444,11 +463,11 @@ def print_msg2(msg):
444463
start = time.time()
445464

446465
# Remove non-unique points
447-
p = np.array(list(set(tuple(p) for p in p)))
466+
# p = np.array(list(set(tuple(p) for p in p)))
448467

449468
# (Re)-triangulation by the Delaunay algorithm
450469
dt = DT()
451-
dt.insert(p.flatten().tolist())
470+
dt.insert(p.ravel().tolist())
452471

453472
# Get the current topology of the triangulation
454473
p, t = _get_topology(dt)
@@ -621,6 +640,7 @@ def _parse_kwargs(kwargs):
621640
"h0",
622641
"geps_mult",
623642
"subdomains",
643+
"gamma",
624644
}:
625645
pass
626646
else:
@@ -696,7 +716,7 @@ def _add_ghost_vertices(p, t, dt, extents, comm):
696716
exports = migration.enqueue(extents, p, t, comm.rank, comm.size, dim=dim)
697717
recv = migration.exchange(comm, comm.rank, comm.size, exports, dim=dim)
698718
recv_ix = len(recv)
699-
dt.insert(recv.flatten().tolist())
719+
dt.insert(recv.ravel().tolist())
700720
p, t = _get_topology(dt)
701721
p, t, inv = geometry.remove_external_entities(
702722
p,

SeismicMesh/geometry/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
calc_dihedral_angles,
66
calc_volume_grad,
77
unique_edges,
8+
drectangle_fast,
9+
dblock_fast,
810
)
911
from .signed_distance_functions import (
1012
Rectangle,
@@ -56,6 +58,8 @@
5658
"Ball",
5759
"Cube",
5860
"Cylinder",
61+
"drectangle_fast",
62+
"dblock_fast",
5963
"Torus",
6064
"Prism",
6165
"Disk",

SeismicMesh/geometry/cpp/fast_geometry.cpp

+114
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,118 @@ double dot_product(const std::array<double, 3> &vect_A,
101101
return product;
102102
}
103103

104+
std::vector<double> c_dblock(std::vector<double> &points, double x1, double x2,
105+
double y1, double y2, double z1, double z2) {
106+
// Signed distance function for block with corners (x1,y1,z1), (x2,y2,z2)
107+
int sz = points.size() / 3;
108+
std::vector<double> dist;
109+
dist.resize(sz);
110+
// return -min(
111+
// min(
112+
// min(min(min(-z1 + p[:, 2], z2 - p[:, 2]), -y1 + p[:, 1]), y2 - p[:,
113+
// 1]), -x1 + p[:, 0],
114+
// ),
115+
// x2 - p[:, 0],
116+
//)
117+
for (std::size_t iv = 0; iv < sz; ++iv) {
118+
119+
double sum1 = -z1 + points[3 * iv + 2];
120+
double sum2 = z2 - points[3 * iv + 2];
121+
double sum3 = -y1 + points[3 * iv + 1];
122+
double sum4 = y2 - points[3 * iv + 1];
123+
double sum5 = -x1 + points[3 * iv];
124+
double sum6 = x2 - points[3 * iv];
125+
126+
double tmp1 = std::min(sum1, sum2);
127+
double tmp2 = std::min(tmp1, sum3);
128+
double tmp3 = std::min(tmp2, sum4);
129+
double tmp4 = std::min(tmp3, sum5);
130+
131+
dist[iv] = -std::min(tmp4, sum6);
132+
}
133+
return dist;
134+
}
135+
136+
py::array dblock_fast(
137+
py::array_t<double, py::array::c_style | py::array::forcecast> points,
138+
double x1, double x2, double y1, double y2, double z1, double z2) {
139+
140+
// check input dimensions
141+
int num_points = points.size() / 3;
142+
// allocate std::vector (to pass to the C++ function)
143+
std::vector<double> cppPts(num_points * 3);
144+
145+
// copy py::array -> std::vector
146+
std::memcpy(cppPts.data(), points.data(), 3 * num_points * sizeof(double));
147+
148+
std::vector<double> dist = c_dblock(cppPts, x1, x2, y1, y2, z1, z2);
149+
150+
ssize_t sodble = sizeof(double);
151+
std::vector<ssize_t> shape = {num_points};
152+
std::vector<ssize_t> strides = {sodble};
153+
154+
// return 1-D NumPy array
155+
return py::array(
156+
py::buffer_info(dist.data(), /* data as contiguous array */
157+
sizeof(double), /* size of one scalar */
158+
py::format_descriptor<double>::format(), /* data type */
159+
1, /* number of dimensions */
160+
shape, /* shape of the matrix */
161+
strides /* strides for each axis */
162+
));
163+
}
164+
165+
std::vector<double> c_drectangle(std::vector<double> &points, double x1,
166+
double x2, double y1, double y2) {
167+
// Signed distance function for rectangle with corners (x1,y1), (x2,y1)
168+
int sz = points.size() / 2;
169+
std::vector<double> dist;
170+
dist.resize(sz);
171+
// return -min(min(min(-y1 + p[:, 1], y2 - p[:, 1]), -x1 + p[:, 0]), x2 - p[:,
172+
// 0])
173+
for (std::size_t iv = 0; iv < sz; ++iv) {
174+
175+
double sum1 = -y1 + points[2 * iv + 1];
176+
double sum2 = y2 - points[2 * iv + 1];
177+
double sum3 = -x1 + points[2 * iv];
178+
double sum4 = x2 - points[2 * iv];
179+
180+
double tmp1 = std::min(sum1, sum2);
181+
double tmp2 = std::min(tmp1, sum3);
182+
dist[iv] = -std::min(tmp2, sum4);
183+
}
184+
return dist;
185+
}
186+
187+
py::array drectangle_fast(
188+
py::array_t<double, py::array::c_style | py::array::forcecast> points,
189+
double x1, double x2, double y1, double y2) {
190+
191+
// check input dimensions
192+
int num_points = points.size() / 2;
193+
// allocate std::vector (to pass to the C++ function)
194+
std::vector<double> cppPts(num_points * 2);
195+
196+
// copy py::array -> std::vector
197+
std::memcpy(cppPts.data(), points.data(), 2 * num_points * sizeof(double));
198+
199+
std::vector<double> dist = c_drectangle(cppPts, x1, x2, y1, y2);
200+
201+
ssize_t sodble = sizeof(double);
202+
std::vector<ssize_t> shape = {num_points};
203+
std::vector<ssize_t> strides = {sodble};
204+
205+
// return 1-D NumPy array
206+
return py::array(
207+
py::buffer_info(dist.data(), /* data as contiguous array */
208+
sizeof(double), /* size of one scalar */
209+
py::format_descriptor<double>::format(), /* data type */
210+
1, /* number of dimensions */
211+
shape, /* shape of the matrix */
212+
strides /* strides for each axis */
213+
));
214+
}
215+
104216
std::vector<double> c_calc_dihedral_angles(std::vector<double> &points,
105217
std::vector<int> &cells) {
106218
// compute the 6 dihedral angles of all tetrahedrons in the mesh
@@ -456,6 +568,8 @@ py::array calc_circumsphere_grad(
456568
}
457569

458570
PYBIND11_MODULE(fast_geometry, m) {
571+
m.def("drectangle_fast", &drectangle_fast);
572+
m.def("dblock_fast", &dblock_fast);
459573
m.def("unique_edges", &unique_edges);
460574
m.def("calc_volume_grad", &calc_volume_grad);
461575
m.def("calc_circumsphere_grad", &calc_circumsphere_grad);

SeismicMesh/geometry/signed_distance_functions.py

+6-36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import numpy as np
22
import itertools
33

4+
from .cpp.fast_geometry import drectangle_fast, dblock_fast
5+
46

57
def _length(x):
68
return np.sum(np.abs(x) ** 2, axis=-1) ** (1.0 / 2)
@@ -154,7 +156,9 @@ def __init__(self, bbox):
154156
self.bbox = bbox
155157

156158
def eval(self, x):
157-
return drectangle(x, self.bbox[0], self.bbox[1], self.bbox[2], self.bbox[3])
159+
return drectangle_fast(
160+
x, self.bbox[0], self.bbox[1], self.bbox[2], self.bbox[3]
161+
)
158162

159163

160164
class Cube:
@@ -164,7 +168,7 @@ def __init__(self, bbox):
164168
self.bbox = bbox
165169

166170
def eval(self, x):
167-
return dblock(
171+
return dblock_fast(
168172
x,
169173
self.bbox[0],
170174
self.bbox[1],
@@ -244,40 +248,6 @@ def drectangle(p, x1, x2, y1, y2):
244248
return -min(min(min(-y1 + p[:, 1], y2 - p[:, 1]), -x1 + p[:, 0]), x2 - p[:, 0])
245249

246250

247-
def dblock0(p, x1, x2, y1, y2, z1, z2):
248-
# adapted from:
249-
# https://github.com/nschloe/dmsh/blob/3305c417d373d509c78491b24e77409411aa18c2/dmsh/geometry/rectangle.py#L31
250-
# outside dist
251-
# https://gamedev.stackexchange.com/a/44496
252-
w = x2 - x1
253-
h = y2 - y1
254-
d = z2 - z1
255-
cx = (x1 + x2) / 2
256-
cy = (y1 + y2) / 2
257-
cz = (z1 + z2) / 2
258-
dx = np.abs(p[:, 0] - cx) - w / 2
259-
dy = np.abs(p[:, 1] - cy) - h / 2
260-
dz = np.abs(p[:, 2] - cz) - d / 2
261-
is_inside = (dx <= 0) & (dy <= 0) & (dz <= 0)
262-
dx[dx < 0.0] = 0.0
263-
dy[dy < 0.0] = 0.0
264-
dz[dz < 0.0] = 0.0
265-
dist = np.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
266-
# inside dist
267-
a = np.array(
268-
[
269-
p[is_inside, 0] - x1,
270-
x2 - p[is_inside, 0],
271-
p[is_inside, 1] - y1,
272-
y2 - p[is_inside, 1],
273-
p[is_inside, 2] - z1,
274-
z2 - p[is_inside, 2],
275-
]
276-
)
277-
dist[is_inside] = -np.min(a, axis=0)
278-
return dist
279-
280-
281251
def dblock(p, x1, x2, y1, y2, z1, z2):
282252
min = np.minimum
283253
return -min(

0 commit comments

Comments
 (0)