Skip to content

Commit 2f7cc5e

Browse files
committed
coord_system3d: test r_h = 0
1 parent d49fcca commit 2f7cc5e

File tree

1 file changed

+55
-37
lines changed

1 file changed

+55
-37
lines changed

fluidsim/operators/test/test_coord_system3d.py

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
from fluidsim.operators.coord_system3d import CoordSystem3DConverter
55

66
# Use a 3D grid of points in Cartesian coordinates.
7-
# Avoid x=y=0 (the z-axis) to keep cylindrical/spherical angles well-defined.
7+
# warning: x=y=0 (the z-axis) is particular, cylindrical/spherical not well-defined.
88
_n = 4
9-
_x1d = np.linspace(1.0, 2.0, _n)
10-
_y1d = np.linspace(0.5, 1.5, _n)
9+
_x1d = np.linspace(0.0, 1.0, _n)
10+
_y1d = np.linspace(0.0, 1.0, _n)
1111
_z1d = np.linspace(-1.0, 1.0, _n)
1212
_z, _y, _x = np.meshgrid(_z1d, _y1d, _x1d, indexing="ij")
1313
shape = _x.shape
14+
EPSILON = 1e-12
1415

1516

1617
@pytest.fixture(scope="module")
@@ -25,9 +26,10 @@ def r_h():
2526

2627

2728
@pytest.fixture(scope="module")
28-
def r_sph():
29+
def r_sph_not0():
2930
"""Spherical radius sqrt(x^2 + y^2 + z^2)."""
30-
return np.sqrt(_x**2 + _y**2 + _z**2)
31+
r_sph = np.sqrt(_x**2 + _y**2 + _z**2)
32+
return np.where(r_sph != 0, r_sph, EPSILON)
3133

3234

3335
# ---------------------------------------------------------------------------
@@ -72,25 +74,29 @@ def test_compute_r_theta_origin():
7274
["pure-radial-h", "pure-azimuthal", "pure-vertical", "pure-spherical-radial"],
7375
)
7476
def test_compute_cylindrical_components(
75-
vector_kind, converter, r_h, r_sph, allclose
77+
vector_kind, converter, r_h, r_sph_not0, allclose
7678
):
79+
r_h_not0 = np.where(r_h != 0, r_h, EPSILON)
80+
7781
match vector_kind:
7882
case "pure-radial-h":
7983
# Unit vector in the horizontal radial direction
80-
vx = _x / r_h
81-
vy = _y / r_h
84+
vx = _x / r_h_not0
85+
vy = _y / r_h_not0
8286
vz = np.zeros(shape)
8387
vh_exp = np.ones(shape)
88+
vh_exp[r_h == 0] = 0
8489
vt_exp = np.zeros(shape)
8590
vz_exp = np.zeros(shape)
8691

8792
case "pure-azimuthal":
8893
# Unit vector in the azimuthal direction: (-y, x, 0) / r_h
89-
vx = -_y / r_h
90-
vy = _x / r_h
94+
vx = -_y / r_h_not0
95+
vy = _x / r_h_not0
9196
vz = np.zeros(shape)
9297
vh_exp = np.zeros(shape)
9398
vt_exp = np.ones(shape)
99+
vt_exp[r_h == 0] = 0
94100
vz_exp = np.zeros(shape)
95101

96102
case "pure-vertical":
@@ -103,14 +109,14 @@ def test_compute_cylindrical_components(
103109
vz_exp = np.ones(shape)
104110

105111
case "pure-spherical-radial":
106-
# Unit vector in the spherical radial direction: (x, y, z) / r_sph
107-
# Cylindrical decomposition: vh = r_h/r_sph, vt = 0, vz = z/r_sph
108-
vx = _x / r_sph
109-
vy = _y / r_sph
110-
vz = _z / r_sph
111-
vh_exp = r_h / r_sph
112+
# Unit vector in the spherical radial direction: (x, y, z) / r_sph_not0
113+
# Cylindrical decomposition: vh = r_h/r_sph_not0, vt = 0, vz = z/r_sph_not0
114+
vx = _x / r_sph_not0
115+
vy = _y / r_sph_not0
116+
vz = _z / r_sph_not0
117+
vh_exp = r_h / r_sph_not0
112118
vt_exp = np.zeros(shape)
113-
vz_exp = _z / r_sph
119+
vz_exp = _z / r_sph_not0
114120

115121
case _:
116122
raise ValueError(f"Unknown vector_kind: {vector_kind}")
@@ -126,12 +132,14 @@ def test_compute_cylindrical_components(
126132
# ---------------------------------------------------------------------------
127133

128134

129-
def test_cylindrical_preserves_norm(converter, allclose):
135+
def test_cylindrical_preserves_norm(converter, r_h, allclose):
130136
"""Cylindrical conversion is a rotation: it must preserve the vector norm."""
131137
rng = np.random.default_rng(0)
132138
vx = rng.standard_normal(shape)
133139
vy = rng.standard_normal(shape)
134140
vz = rng.standard_normal(shape)
141+
vx[r_h == 0] = 0
142+
vy[r_h == 0] = 0
135143

136144
norm2_cart = vx**2 + vy**2 + vz**2
137145
vh, vt, vz_out = converter.compute_cylindrical_components(vx, vy, vz)
@@ -145,19 +153,22 @@ def test_cylindrical_preserves_norm(converter, allclose):
145153
# ---------------------------------------------------------------------------
146154

147155

148-
def test_compute_radial_component_pure_radial(converter, r_sph, allclose):
156+
def test_compute_radial_component_pure_radial(converter, r_sph_not0, allclose):
149157
"""A pure horizontal-radial unit vector should have radial component 1."""
150-
vx = _x / r_sph
151-
vy = _y / r_sph
152-
vz = _z / r_sph
158+
vx = _x / r_sph_not0
159+
vy = _y / r_sph_not0
160+
vz = _z / r_sph_not0
153161
vr = converter.compute_radial_component(vx, vy, vz)
154162
assert allclose(vr, np.ones(shape))
155163

156164

157165
def test_compute_radial_component_pure_azimuthal(converter, r_h, allclose):
158166
"""A pure azimuthal unit vector is perpendicular to r_h → radial component 0."""
159-
vx = -_y / r_h
160-
vy = _x / r_h
167+
168+
r_h_not0 = np.where(r_h != 0, r_h, EPSILON)
169+
170+
vx = -_y / r_h_not0
171+
vy = _x / r_h_not0
161172
vz = np.zeros(shape)
162173
vr = converter.compute_radial_component(vx, vy, vz)
163174
assert allclose(vr, np.zeros(shape))
@@ -173,35 +184,39 @@ def test_compute_radial_component_pure_azimuthal(converter, r_h, allclose):
173184
["pure-spherical-radial", "pure-azimuthal", "pure-polar"],
174185
)
175186
def test_compute_spherical_components(
176-
vector_kind, converter, r_h, r_sph, allclose
187+
vector_kind, converter, r_h, r_sph_not0, allclose
177188
):
189+
r_h_not0 = np.where(r_h != 0, r_h, EPSILON)
190+
178191
match vector_kind:
179192
case "pure-spherical-radial":
180-
# Unit vector along spherical r: (x, y, z)/r_sph
181-
vx = _x / r_sph
182-
vy = _y / r_sph
183-
vz = _z / r_sph
193+
# Unit vector along spherical r: (x, y, z)/r_sph_not0
194+
vx = _x / r_sph_not0
195+
vy = _y / r_sph_not0
196+
vz = _z / r_sph_not0
184197
vr_exp = np.ones(shape)
185198
vt_exp = np.zeros(shape) # azimuthal
186199
vp_exp = np.zeros(shape) # polar
187200

188201
case "pure-azimuthal":
189202
# Unit vector along azimuthal phi: (-y, x, 0)/r_h
190-
vx = -_y / r_h
191-
vy = _x / r_h
203+
vx = -_y / r_h_not0
204+
vy = _x / r_h_not0
192205
vz = np.zeros(shape)
193206
vr_exp = np.zeros(shape)
194207
vt_exp = np.ones(shape)
208+
vt_exp[r_h == 0] = 0
195209
vp_exp = np.zeros(shape)
196210

197211
case "pure-polar":
198-
# Unit vector along polar theta (e_theta): (x*z, y*z, -r_h^2) / (r_sph * r_h)
199-
vx = _x * _z / (r_sph * r_h)
200-
vy = _y * _z / (r_sph * r_h)
201-
vz = -(r_h**2) / (r_sph * r_h)
212+
# Unit vector along polar theta (e_theta): (x*z, y*z, -r_h^2) / (r_sph_not0 * r_h)
213+
vx = _x * _z / (r_sph_not0 * r_h_not0)
214+
vy = _y * _z / (r_sph_not0 * r_h_not0)
215+
vz = -(r_h**2) / (r_sph_not0 * r_h_not0)
202216
vr_exp = np.zeros(shape)
203217
vt_exp = np.zeros(shape)
204218
vp_exp = np.ones(shape)
219+
vp_exp[r_h == 0] = 0
205220

206221
case _:
207222
raise ValueError(f"Unknown vector_kind: {vector_kind}")
@@ -212,21 +227,24 @@ def test_compute_spherical_components(
212227
assert allclose(vp, vp_exp), f"vp mismatch for {vector_kind}"
213228

214229

215-
def test_spherical_preserves_norm(converter, allclose):
230+
def test_spherical_preserves_norm(converter, r_h, allclose):
216231
"""Spherical conversion is a rotation: it must preserve the vector norm."""
217232
rng = np.random.default_rng(1)
218233
vx = rng.standard_normal(shape)
219234
vy = rng.standard_normal(shape)
220235
vz = rng.standard_normal(shape)
221236

237+
vx[r_h == 0] = 0
238+
vy[r_h == 0] = 0
239+
222240
norm2_cart = vx**2 + vy**2 + vz**2
223241
vr, vt, vp = converter.compute_spherical_components(vx, vy, vz)
224242
norm2_sph = vr**2 + vt**2 + vp**2
225243

226244
assert allclose(norm2_sph, norm2_cart)
227245

228246

229-
def test_spherical_radial_equals_radial_component(converter, allclose):
247+
def test_spherical_radial_equals_radial_component(converter, r_h, allclose):
230248
"""The spherical vr component must equal compute_radial_component."""
231249
rng = np.random.default_rng(2)
232250
vx = rng.standard_normal(shape)

0 commit comments

Comments
 (0)