Skip to content

Commit 232146a

Browse files
committed
Add test for new circumcenter()
1 parent 305ffbe commit 232146a

1 file changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import collections
2+
3+
import numpy as np
4+
5+
from mpas_tools.mesh.creation.util import circumcenter
6+
7+
point = collections.namedtuple('Point', ['x', 'y', 'z'])
8+
9+
10+
def test_circumcenter_planar():
11+
# List of test cases: (cell0, cell1, cell2, expected)
12+
test_cases = [
13+
# From a regular hex mesh
14+
(
15+
(0.0, 17320.50807569, 0.0),
16+
(10000.0, 17320.50807569, 0.0),
17+
(5000.0, 25980.76211353, 0.0),
18+
(5000.0, 20207.25942164, 0.0),
19+
),
20+
# From an Antarctic mesh
21+
(
22+
(2194652.05105505, 1144816.29348609, 0.0),
23+
(2198726.62725077, 1145337.65538494, 0.0),
24+
(2195474.22967839, 1148983.95766333, 0.0),
25+
(2196492.12915069, 1146618.22089598, 0.0),
26+
),
27+
# From an Antarctic mesh
28+
(
29+
(2195474.22967839, 1148983.95766333, 0.0),
30+
(2198726.62725077, 1145337.65538494, 0.0),
31+
(2199279.33328257, 1149240.68069275, 0.0),
32+
(2197485.28573298, 1147504.08822421, 0.0),
33+
),
34+
# From an Antarctic mesh
35+
(
36+
(2150629.6663189, 1441451.4899963, 0.0),
37+
(2149349.39244275, 1445285.59533973, 0.0),
38+
(2146912.55160507, 1442187.732174, 0.0),
39+
(2149013.33940856, 1443042.57600619, 0.0),
40+
),
41+
# From an Antarctic mesh
42+
(
43+
(2570868.91779026, -97880.09977304, 0.0),
44+
(2575332.71614832, -97980.66509542, 0.0),
45+
(2573585.71564672, -95138.63592864, 0.0),
46+
(2573113.05578246, -97387.13758518, 0.0),
47+
),
48+
]
49+
50+
for i, (cell0, cell1, cell2, expected) in enumerate(test_cases):
51+
x1, y1, z1 = cell0
52+
x2, y2, z2 = cell1
53+
x3, y3, z3 = cell2
54+
55+
xv, yv, zv = circumcenter(False, x1, y1, z1, x2, y2, z2, x3, y3, z3)
56+
assert np.allclose([xv, yv, zv], expected, atol=1e-8), (
57+
f'Planar circumcenter failed for case {i}: got {[xv, yv, zv]}, '
58+
f'expected {expected}'
59+
)
60+
61+
xv_old, yv_old, zv_old = _old_circumcenter(
62+
False, x1, y1, z1, x2, y2, z2, x3, y3, z3
63+
)
64+
print(f'Planar circumcenter (case {i}):')
65+
print(' circumcenter: ', (xv, yv, zv))
66+
print(' _old_circumcenter: ', (xv_old, yv_old, zv_old))
67+
print(' difference: ', (xv - xv_old, yv - yv_old, zv - zv_old))
68+
assert np.array_equal([xv, yv, zv], [xv_old, yv_old, zv_old]), (
69+
f'circumcenter and _old_circumcenter results differ in case {i}: '
70+
f'({xv}, {yv}, {zv}) != ({xv_old}, {yv_old}, {zv_old})'
71+
)
72+
73+
74+
def test_circumcenter_sphere():
75+
# Use provided cell coordinates and vertex as expected output
76+
x1, y1, z1 = -3590.95704026, -684.52527287, -6371218.95125671
77+
x2, y2, z2 = -9926.94769501, 18734.26792821, -6371184.72274307
78+
x3, y3, z3 = 12921.92507847, 11340.69768405, -6371196.8028643
79+
expected = (296.83807146, 11327.05862805, -6371209.92418473)
80+
81+
xv, yv, zv = circumcenter(True, x1, y1, z1, x2, y2, z2, x3, y3, z3)
82+
assert np.allclose([xv, yv, zv], expected, atol=1e-8), (
83+
f'Spherical circumcenter failed: got {[xv, yv, zv]}, expected '
84+
f'{expected}'
85+
)
86+
87+
xv_old, yv_old, zv_old = _old_circumcenter(
88+
True, x1, y1, z1, x2, y2, z2, x3, y3, z3
89+
)
90+
# Print both sets and their difference
91+
print('Spherical circumcenter:')
92+
print(' circumcenter: ', (xv, yv, zv))
93+
print(' _old_circumcenter: ', (xv_old, yv_old, zv_old))
94+
print(' difference: ', (xv - xv_old, yv - yv_old, zv - zv_old))
95+
# Check for exact equality
96+
assert np.array_equal([xv, yv, zv], [xv_old, yv_old, zv_old]), (
97+
f'circumcenter and _old_circumcenter results differ: '
98+
f'({xv}, {yv}, {zv}) != ({xv_old}, {yv_old}, {zv_old})'
99+
)
100+
101+
102+
def _old_circumcenter(on_sphere, x1, y1, z1, x2, y2, z2, x3, y3, z3):
103+
"""
104+
Compute the circumcenter of the triangle (possibly on a sphere)
105+
with the three given vertices in Cartesian coordinates.
106+
107+
Returns
108+
-------
109+
center : point
110+
The circumcenter of the triangle with x, y and z attributes
111+
112+
"""
113+
p1 = point(x1, y1, z1)
114+
p2 = point(x2, y2, z2)
115+
p3 = point(x3, y3, z3)
116+
if on_sphere:
117+
a = (p2.x - p3.x) ** 2 + (p2.y - p3.y) ** 2 + (p2.z - p3.z) ** 2
118+
b = (p3.x - p1.x) ** 2 + (p3.y - p1.y) ** 2 + (p3.z - p1.z) ** 2
119+
c = (p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2 + (p1.z - p2.z) ** 2
120+
121+
pbc = a * (-a + b + c)
122+
apc = b * (a - b + c)
123+
abp = c * (a + b - c)
124+
125+
xv = (pbc * p1.x + apc * p2.x + abp * p3.x) / (pbc + apc + abp)
126+
yv = (pbc * p1.y + apc * p2.y + abp * p3.y) / (pbc + apc + abp)
127+
zv = (pbc * p1.z + apc * p2.z + abp * p3.z) / (pbc + apc + abp)
128+
else:
129+
d = 2 * (
130+
p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)
131+
)
132+
133+
xv = (
134+
(p1.x**2 + p1.y**2) * (p2.y - p3.y)
135+
+ (p2.x**2 + p2.y**2) * (p3.y - p1.y)
136+
+ (p3.x**2 + p3.y**2) * (p1.y - p2.y)
137+
) / d
138+
yv = (
139+
(p1.x**2 + p1.y**2) * (p3.x - p2.x)
140+
+ (p2.x**2 + p2.y**2) * (p1.x - p3.x)
141+
+ (p3.x**2 + p3.y**2) * (p2.x - p1.x)
142+
) / d
143+
zv = 0.0
144+
145+
# Optional method to use barycenter instead.
146+
# xv = p1.x + p2.x + p3.x
147+
# xv = xv / 3.0
148+
# yv = p1.y + p2.y + p3.y
149+
# yv = yv / 3.0
150+
return point(xv, yv, zv)

0 commit comments

Comments
 (0)