- Search: 활동면이 지표를 자른 점을 셀 안 점 단위로 덩이 나눔 — 발끝을 살짝 비켜 지나는 구의 사면 몸통 · 발끝 앞 접시가 따로(셀 중심 줄 꺾임만 검사 · 지표 쌍선형 + 활동면 볼록) - Search: 전역 최소 면을 찾은 노드 · 반지름 한 눈금 안에서 Nelder-Mead 로 다듬음(box · node_mask 안) — 셀 지도도 그 면으로 고침 - Scoops3D 60° 0.771~0.776 → 0.754(0.75 · +0.5 %) · 30° 1.325 → 1.3197(1.32) - 검증_sub5 1 km 띠: 전역 최소 1.185 → 1.174(−0.9 %) · p1/p5/p50 같음 · 셀 9.7만 중 168 이 1 % 넘게 바뀜 · 12 프로세스 28 s → 45 s - 시험: 60° 제방 ±1 % · Zhang 경우 3(약층 + 수위선 · FK 1977 그림 8) Bishop 1.524 = Lam & Fredlund 1.481~1.54 · Gharti 1.49 ±3 % · 경우3/2 비 0.943 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
122 lines
5.4 KiB
Python
122 lines
5.4 KiB
Python
"""533-2 E04 3D 기둥 솔버 — 문헌 기준 문제 ±3 %.
|
||
|
||
- Gens 외(1988) · Hungr 외(1989) · Scoops3D 6.1: 2:1 평면 사면 · 구 R=1 · 중심-지표 0.5
|
||
· c=0.1 γ=1 φ=0 → 해석해 1.402 · 부피 0.654.
|
||
- Zhang(1988) 경우 1 · 2(PLoS ONE 18(7) e0287998 그림 5 · 표 2): 1:2 사면 H 12.2 m
|
||
· 타원체 중심 (6.1, 0, 21.3)
|
||
Rx=Rz=24.4 · Ry=78.2 · c 29 kPa φ 20° γ 18.8 → Hungr 3D Bishop 2.167 · Zhang 2.122 /
|
||
약층(c 0 · φ 10°, 발끝 아래 0.94 m — 그림 화소로 잼) → Hungr 1.620 · Zhang 1.533.
|
||
- 경우 3 = 경우 2 + 수위선(Fredlund & Krahn 1977 그림 8 · Slide3 2D Extruded #16 그림 16:
|
||
뒤 경계 42.7 m 에서 발끝 위 6.1 m → 발끝 → 발끝 앞 지표) → Lam & Fredlund 1993 1.481~1.54
|
||
· Gharti 2011 1.49 · Zhang 1.441(경우 2 처럼 Zhang 은 다른 풀이보다 4~6 % 낮음).
|
||
"""
|
||
|
||
import numpy as np
|
||
import pytest
|
||
|
||
from E04_Stab3D.E04_Stab3D_Engine_Column import Strata
|
||
from E04_Stab3D.E04_Stab3D_Engine_Search import evaluate_surface, governing
|
||
|
||
|
||
def _mat(gamma, c, phi, mid="m"):
|
||
return {"id": mid, "name": mid, "gamma_t": gamma, "gamma_sat": gamma, "c": c, "phi": phi}
|
||
|
||
|
||
def _gens(dx=0.02, angle=0.0):
|
||
"""2:1 평면 사면 — 아래로 향하는 방향을 angle(rad) 만큼 돌림."""
|
||
beta = np.arctan(0.5)
|
||
x = np.arange(-2, 2 + 1e-9, dx)
|
||
xx, yy = np.meshgrid(x, x)
|
||
down = np.array([np.cos(angle), np.sin(angle)])
|
||
z = -(xx * down[0] + yy * down[1]) * np.tan(beta)
|
||
st = Strata(x, x, z, np.zeros((0,) + z.shape), [_mat(1.0, 0.1, 0.0)])
|
||
c = np.r_[0.5 * np.sin(beta) * down, 0.5 * np.cos(beta)]
|
||
return st, c
|
||
|
||
|
||
def test_gens_sphere_analytic():
|
||
st, c = _gens()
|
||
r = evaluate_surface(st, c, 1.0)
|
||
assert r["bishop"] == pytest.approx(1.402, rel=0.01)
|
||
assert r["hovland"] == pytest.approx(1.402, rel=0.01)
|
||
assert r["volume"] == pytest.approx(np.pi * 0.25 * 2.5 / 3, rel=0.01) # 구 모자 부피 0.6545
|
||
|
||
|
||
def test_rotated_slope_same_factor():
|
||
"""Scoops3D 6.7 대칭 — 낙하 방향이 격자 축과 어긋나도 같은 F."""
|
||
f0 = evaluate_surface(*_gens(0.02), 1.0)["bishop"]
|
||
f1 = evaluate_surface(*_gens(0.02, np.radians(33.0)), 1.0)["bishop"]
|
||
assert f1 == pytest.approx(f0, rel=0.01)
|
||
|
||
|
||
def _zhang(weak: bool, dx=0.5):
|
||
x = np.arange(-15, 45 + 1e-9, dx)
|
||
y = np.arange(-90, 90 + 1e-9, dx)
|
||
xx, _ = np.meshgrid(x, y)
|
||
z = np.clip(xx * 0.5, 0, 12.2)
|
||
soil = _mat(18.8, 29.0, 20.0, "soil")
|
||
if not weak:
|
||
return Strata(x, y, z, np.zeros((0,) + z.shape), [soil])
|
||
zw = -0.94
|
||
bottoms = np.stack([np.full(z.shape, zw + 0.3), np.full(z.shape, zw)])
|
||
mats = [soil, _mat(18.8, 0.0, 10.0, "weak"), _mat(18.8, 1000.0, 45.0, "hard")]
|
||
return Strata(x, y, z, bottoms, mats, hard=[False, False, True])
|
||
|
||
|
||
def test_zhang_case1_ellipsoid():
|
||
r = evaluate_surface(_zhang(False), [6.1, 0.0, 21.3], 24.4, 78.2 / 24.4, [-1.0, 0.0])
|
||
assert r["bishop"] == pytest.approx(2.167, rel=0.03) # Hungr 외 1989
|
||
assert r["bishop"] == pytest.approx(2.122, rel=0.03) # Zhang 1988
|
||
assert r["hovland"] < r["bishop"] # Hovland 은 보수적
|
||
|
||
|
||
def test_zhang_case2_weak_layer_truncated():
|
||
r = evaluate_surface(_zhang(True), [6.1, 0.0, 21.3], 24.4, 78.2 / 24.4, [-1.0, 0.0])
|
||
assert r["bishop"] == pytest.approx(1.620, rel=0.03) # Hungr 외 1989
|
||
assert r["truncated_ratio"] > 0.1
|
||
# 잘린 면 → Janbu 보정으로 전환하되 Bishop 이 낮으면 Bishop(작은 쪽)
|
||
assert r["governing"] == pytest.approx(min(r["bishop"], r["janbu_corrected"]))
|
||
assert 1.45 < r["janbu_corrected"] < 1.80 # 문헌 폭 1.53~1.67 둘레
|
||
|
||
|
||
def test_zhang_case3_weak_layer_piezometric_line():
|
||
st = _zhang(True)
|
||
xx, _ = np.meshgrid(st.x, st.y)
|
||
st.z_water = np.clip(xx * 6.1 / 42.67, 0.0, None)
|
||
args = ([6.1, 0.0, 21.3], 24.4, 78.2 / 24.4, [-1.0, 0.0])
|
||
r = evaluate_surface(st, *args, groundwater={"mode": "line"})
|
||
assert 1.481 * 0.99 < r["bishop"] < 1.54 * 1.01 # Lam & Fredlund 1993 네 값 폭
|
||
assert r["bishop"] == pytest.approx(1.49, rel=0.03) # Gharti 2011
|
||
# 수위선이 낮추는 비 = Lam & Fredlund 경우 3/경우 2(0.940~0.951)
|
||
dry = evaluate_surface(_zhang(True), *args)["bishop"]
|
||
assert 0.93 < r["bishop"] / dry < 0.96
|
||
|
||
|
||
def test_pore_pressure_and_seismic():
|
||
st, c = _gens(0.04)
|
||
dry = evaluate_surface(st, c, 1.0)
|
||
# φ=0 이면 간극수압은 저항에 안 들어감
|
||
assert evaluate_surface(st, c, 1.0, groundwater={"mode": "ru", "ru": 0.3})[
|
||
"bishop"
|
||
] == pytest.approx(dry["bishop"])
|
||
# c−φ 흙: r_u 가 F 를 낮춤 · 깊은 지하수위는 영향 없음 · kh 는 낮춤
|
||
st2 = Strata(st.x, st.y, st.z_top, st.bottoms, [_mat(1.0, 0.05, 25.0)], z_water=st.z_top - 5.0)
|
||
base = evaluate_surface(st2, c, 1.0)["bishop"]
|
||
assert evaluate_surface(st2, c, 1.0, groundwater={"mode": "ru", "ru": 0.3})["bishop"] < base
|
||
assert evaluate_surface(st2, c, 1.0, groundwater={"mode": "line"})["bishop"] == pytest.approx(
|
||
base
|
||
)
|
||
st2.z_water = st2.z_top + 0.0
|
||
assert evaluate_surface(st2, c, 1.0, groundwater={"mode": "line"})["bishop"] < base
|
||
assert evaluate_surface(st2, c, 1.0, seismic_kh=0.1)["bishop"] < base
|
||
|
||
|
||
def test_governing_picks_bishop_when_not_truncated():
|
||
res = {
|
||
"bishop": np.array([1.5, 1.5]),
|
||
"janbu_corrected": np.array([1.4, 1.4]),
|
||
"truncated_ratio": np.array([0.0, 0.5]),
|
||
}
|
||
f, meth = governing(res)
|
||
assert f.tolist() == [1.5, 1.4] and meth.tolist() == [0, 1]
|