- Grid: B06 사면이 반폭 안에서 안 닫히면 끝 기울기로 원지형까지 늘림(자료 끝이면 끝 값 이어 감) — 띠 끝 7 m 가짜 절벽 없앰 - Grid: δ = 설계 − 원 DEM(다시 뜬 1 m 격자의 cm 잡음이 끝 판정을 흔들던 것) - Search: 타원체는 구 탐색 임계 노드에서만(1 km 822 s → 287 s) · 다듬기 문턱 = max(2.0, 전역 최소 × 1.2) - 시험: 안 닫힌 사면 연장 · Scoops3D 원뿔 r_u 0.4(1.626) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
5.2 KiB
Python
124 lines
5.2 KiB
Python
"""533-1 E04 합성 DEM · 지층 격자 — 측점 위 단면 = B06 설계선 · 띠 밖 = 원지형 · 보간 · 지층."""
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from E04_Stab3D.E04_Stab3D_Engine_Grid import _segment_map, build_grid, strata_from_grid
|
|
|
|
|
|
def _dem(dx=0.5):
|
|
x = np.arange(-30, 90 + 1e-9, dx)
|
|
y = np.arange(-40, 40 + 1e-9, dx)
|
|
xx, yy = np.meshgrid(x, y)
|
|
z = 100.0 + 0.3 * yy + 0.02 * xx # 왼쪽(+y)이 오르막
|
|
return {"x": x, "y": y, "z": z, "valid_mask": np.ones(z.shape, bool)}, xx, yy
|
|
|
|
|
|
def _station(dem, cx, depth_cut, ground_type="soil", rock=None):
|
|
"""x=cx · 왼쪽 = +y · 노면 ±2 m 를 depth_cut 낮춘 설계선(사면 1:1 로 지반에 닿음)."""
|
|
offs = np.arange(-20, 20.01, 0.5)
|
|
g = 100.0 + 0.3 * offs + 0.02 * cx
|
|
design = g.copy()
|
|
road = np.abs(offs) <= 2.0
|
|
design[road] = 100.0 + 0.02 * cx - depth_cut
|
|
for k in np.flatnonzero(~road):
|
|
edge = 2.0 * np.sign(offs[k])
|
|
design[k] = (
|
|
min(g[k], 100.0 + 0.02 * cx - depth_cut + abs(offs[k] - edge))
|
|
if depth_cut > 0
|
|
else g[k]
|
|
)
|
|
return {
|
|
"chainage_m": float(cx),
|
|
"center": [float(cx), 0.0],
|
|
"left": [0.0, 1.0],
|
|
"ground": np.column_stack([offs, g]).tolist(),
|
|
"design": np.column_stack([offs, design]).tolist(),
|
|
"ground_type": ground_type,
|
|
"rock_depth_m": rock,
|
|
"road": [-2.0, 2.0],
|
|
}
|
|
|
|
|
|
def _grid():
|
|
dem, xx, yy = _dem()
|
|
sts = [
|
|
_station(dem, 0, 1.0),
|
|
_station(dem, 20, 3.0, "blasting_rock", 0.5),
|
|
_station(dem, 40, 3.0, "ripping_rock", 1.0),
|
|
]
|
|
return dem, build_grid(dem, sts, cell_m=0.5), xx, yy, sts
|
|
|
|
|
|
def test_station_section_equals_design_and_outside_unchanged():
|
|
dem, g, xx, yy, sts = _grid()
|
|
for st in sts:
|
|
col = np.isclose(g["x"], st["center"][0])
|
|
d = np.asarray(st["design"])
|
|
z_line = np.interp(g["y"], d[:, 0], d[:, 1])
|
|
inside = np.abs(g["y"]) <= 20
|
|
assert np.abs(g["z"][inside, col][:, None].ravel() - z_line[inside]).max() < 1e-9
|
|
out = ~g["in_band"]
|
|
assert np.array_equal(g["z"][out], g["z_orig"][out])
|
|
assert np.array_equal(g["z"][:, g["x"] < 0], dem["z"][:, g["x"] < 0]) # 첫 측점 앞 = 원지형
|
|
|
|
|
|
def test_between_stations_linear_delta():
|
|
_, g, xx, yy, sts = _grid()
|
|
j = np.flatnonzero(np.isclose(g["x"], 10.0))[0]
|
|
i = np.flatnonzero(np.isclose(g["y"], 0.0))[0]
|
|
# 노면 가운데 δ: 측점 0 −1 m · 측점 20 −3 m → 중간 −2 m
|
|
assert g["dz"][i, j] == pytest.approx(-2.0, abs=1e-9)
|
|
assert g["q"][i, j] == pytest.approx(10.0)
|
|
|
|
|
|
def test_curved_segment_map_round_trip():
|
|
c0, c1 = np.array([0.0, 0.0]), np.array([19.0, 6.0])
|
|
l0 = np.array([0.0, 1.0])
|
|
a = np.radians(35.0)
|
|
l1 = np.array([-np.sin(a), np.cos(a)])
|
|
t_true = np.array([0.0, 0.3, 0.75, 1.0])
|
|
o_true = np.array([-15.0, 4.0, 12.0, -7.0])
|
|
lt = l0[None] + t_true[:, None] * (l1 - l0)[None]
|
|
lt /= np.hypot(lt[:, 0], lt[:, 1])[:, None]
|
|
p = c0[None] + t_true[:, None] * (c1 - c0)[None] + o_true[:, None] * lt
|
|
t, o = _segment_map(p[:, 0], p[:, 1], c0, c1, l0, l1)
|
|
assert np.allclose(t, t_true, atol=1e-9) and np.allclose(o, o_true, atol=1e-9)
|
|
|
|
|
|
def test_rock_boundary_and_layers():
|
|
_, g, xx, yy, sts = _grid()
|
|
j0 = np.flatnonzero(np.isclose(g["x"], 20.0))[0]
|
|
jm = np.flatnonzero(np.isclose(g["x"], 30.0))[0]
|
|
i = np.flatnonzero(np.isclose(g["y"], 10.0))[0]
|
|
assert g["soil_thickness"][i, j0] == pytest.approx(0.5)
|
|
assert g["soil_thickness"][i, jm] == pytest.approx(0.75) # 0.5 → 1.0 가운데
|
|
far = np.flatnonzero(np.isclose(g["y"], 35.0))[0]
|
|
assert not g["in_band"][far, j0]
|
|
assert g["soil_thickness"][far, j0] == pytest.approx(0.5) # 띠 밖 = 가까운 측점
|
|
assert g["soil_thickness"][i, 0] == pytest.approx(3.0) # 토사 측점 = 기본 두께
|
|
st = strata_from_grid(g)
|
|
assert g["rock_kind"][i, j0] == 1 # 발파암 → 못 뚫는 층 윗면 = 암 윗면(깎기로 드러나면 지표)
|
|
assert st.hard_top[i, j0] == pytest.approx(min(g["rock_top"][i, j0], g["z"][i, j0]))
|
|
assert np.isneginf(st.hard_top[i, jm + 20]) or st.hard_top[i, jm + 20] < -1e5 # 리핑암 = 뚫림
|
|
ii = np.flatnonzero(np.isclose(g["y"], 0.0))[0]
|
|
assert st.bottoms[0, ii, j0] == pytest.approx(g["z"][ii, j0]) # 깎기 = 성토 두께 0
|
|
|
|
|
|
def test_unclosed_slope_extended_to_ground():
|
|
"""B06 사면이 반폭 안에서 안 닫힘(끝 δ ≠ 0) → 끝 기울기로 원지형까지 늘려 띠 끝 절벽 없앰."""
|
|
dem, xx, yy = _dem()
|
|
sts = [_station(dem, cx, 1.0) for cx in (0, 20)]
|
|
for st in sts: # 오른쪽(−y) 끝 5 m 를 1:1 성토로 바꿔 −20 m 에서 2 m 떠 있게 함
|
|
d = np.asarray(st["design"])
|
|
g = np.asarray(st["ground"])
|
|
tail = d[:, 0] <= -15
|
|
d[tail, 1] = g[tail, 1] + 2.0 - (d[tail, 0] + 20) * 0.4
|
|
st["design"] = d.tolist()
|
|
g = build_grid(dem, sts, cell_m=0.5)
|
|
j = np.flatnonzero(np.isclose(g["x"], 10.0))[0]
|
|
col = g["dz"][:, j]
|
|
# 늘린 사면이 원지형과 만나는 곳 너머는 0 · 이웃 칸 사이 단차 1 m 아래
|
|
assert np.abs(np.diff(col[np.isfinite(col)])).max() < 1.0
|
|
assert g["in_band"][np.flatnonzero(np.isclose(g["y"], -21.0))[0], j]
|