"""533-2 E04 활동면 탐색 — Scoops3D 검증 표 전역 최소 ±3 %. Reid 외(2015) Scoops3D 표 6.8 · 6.11(CLARA-W 와 같은 구): H 10 m 제방 - 30° · λ 0.125(γ 19 · φ 20° · c 8.64 kPa) → Bishop 1.32 · 부피 1,124 m³ - 60° · λ 0.125 → 0.75 — 발끝에 거의 닿는 면(발끝 앞 접시는 따로 덩이 · 마지막 다듬기) ±1 % - Donald & Giam 1a 2:1 · γ 20 · φ 19.6° · c 3 kPa → 1.04 - 표 6.7 원뿔 30° · r_u 0.4 → 1.626 탐색 격자는 시험 시간 때문에 문헌 임계 중심 둘레로 좁힘. """ import numpy as np import pytest from E04_Stab3D.E04_Stab3D_Engine_Column import Strata from E04_Stab3D.E04_Stab3D_Engine_Search import SearchParams, search def _embankment(run, gamma, phi, c, dx=1.0): x = np.arange(-25, run + 35 + 1e-9, dx) y = np.arange(-40, 40 + 1e-9, dx) xx, _ = np.meshgrid(x, y) z = np.clip(xx / run * 10.0, 0, 10.0) mat = {"id": "m", "name": "m", "gamma_t": gamma, "gamma_sat": gamma, "c": c, "phi": phi} return Strata(x, y, z, np.zeros((0,) + z.shape), [mat]) def test_embankment_30deg_global_minimum(): st = _embankment(10 / np.tan(np.radians(30)), 19, 20, 8.64) p = SearchParams( box=(-2, 10, -1, 1), h_min=4, h_max=24, spacing=1.0, coarse=2, max_depth=20, v_min=50, v_max=2e4, ratios=(1.0,), ) r = search(st, p) assert r.best["governing"] == pytest.approx(1.32, rel=0.03) assert r.best["volume"] == pytest.approx(1124, rel=0.15) f = r.f_min assert np.nanmin(f) == pytest.approx(r.best["governing"]) # 셀별 최소 지도: 임계 활동체 안 셀은 전역 최소 · 부피 래스터가 같이 채워짐 assert np.isfinite(r.volume[np.isfinite(f)]).all() def test_embankment_60deg_toe_minimum(): st = _embankment(10 / np.tan(np.radians(60)), 19, 20, 8.64) p = SearchParams( box=(-9, -5, -1, 1), h_min=13, h_max=17, h_step=1, spacing=1.0, coarse=2, dr=0.5, max_depth=20, v_min=50, v_max=2e4, ratios=(1.0,), ) r = search(st, p) assert r.best["governing"] == pytest.approx(0.75, rel=0.01) assert np.nanmin(r.f_min) == pytest.approx(r.best["governing"]) def test_donald_giam_1a(): st = _embankment(20, 20, 19.6, 3.0) p = SearchParams( box=(-4, 6, -1, 1), h_min=16, h_max=30, spacing=1.0, coarse=2, max_depth=20, v_min=50, v_max=3e4, ratios=(1.0,), ) assert search(st, p).best["governing"] == pytest.approx(1.04, rel=0.03) def test_wet_cone_pore_pressure_ratio(): """Scoops3D 표 6.7 · 원뿔 30° · λ 0.125(γ 20 · φ 30°) · r_u 0.4 → Bishop 1.626(도표 1.644).""" h = 10.0 r0 = h / np.tan(np.radians(30)) x = np.arange(-r0 - 15, r0 + 15 + 1e-9, 0.5) xx, yy = np.meshgrid(x, x) z = np.clip(h - np.hypot(xx, yy) * np.tan(np.radians(30)), 0, h) c = 0.125 * 20 * h * np.tan(np.radians(30)) mat = {"id": "m", "name": "m", "gamma_t": 20, "gamma_sat": 20, "c": c, "phi": 30} st = Strata(x, x, z, np.zeros((0,) + z.shape), [mat]) p = SearchParams( box=(8, r0 + 4, -1, 1), spacing=1.0, h_min=1, h_max=21, h_step=2, coarse=4, max_depth=15, v_min=20, v_max=3e4, ratios=(1.0,), groundwater={"mode": "ru", "ru": 0.4}, ) assert search(st, p).best["governing"] == pytest.approx(1.626, rel=0.03) def test_workers_same_result(): """프로세스 나눔 — 일꾼 수와 무관하게 셀별 최소 F · 전역 최소가 같음.""" st = _embankment(10 / np.tan(np.radians(30)), 19, 20, 8.64) kw = dict(box=(-2, 10, -4, 4), h_min=4, h_max=20, spacing=2.0, coarse=2, max_depth=16) kw.update(v_min=50, v_max=2e4, ratios=(1.0, 2.0)) one = search(st, SearchParams(workers=1, **kw)) two = search(st, SearchParams(workers=2, **kw)) np.testing.assert_array_equal(one.f_min, two.f_min) assert one.best["governing"] == two.best["governing"] assert one.evaluated == two.evaluated