142 lines
6.2 KiB
Python
142 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""532-2 2차 E03 2D — Morgenstern-Price(반정현) 대조 · 결과 자료(자유물체도 · F–λ 곡선 · Safety Map).
|
||
|
||
기대값 = Rocscience Slide2 Slope Stability Verification Manual(지식DB 참고문헌) 원문 GLE · M-P 값.
|
||
- #1 ACADS 1a GLE 0.986 · #3 ACADS 1c GLE 1.374 · #8 ACADS 3b 지정 비원호 GLE 1.262 ·
|
||
#9 ACADS 4 블록 + 최적화 GLE 0.683 · #21 F&K 지정 원 M-P 건기 2.075 · r_u 1.760(Slide 열).
|
||
- 뺀 것: #7 · #15 — 원문 GLE(1.275 · 0.437)가 같은 표 Spencer(1.246 · 0.409)보다 높음(Slide 탐색이 GLE
|
||
임계면을 놓친 값 · 같은 문제 #8 지정면 GLE 는 Spencer 보다 낮음) → 대조 기준으로 못 씀.
|
||
|
||
./venv/Scripts/python.exe -m pytest resources/tester/test_532_2_stab_mp_outputs.py -q
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import numpy as np
|
||
import pytest
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
sys.path.insert(0, str(ROOT))
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
|
||
from E03_StabSection import E03_StabSection_Engine_GLE as gle # noqa: E402
|
||
from E03_StabSection import E03_StabSection_Engine_LEM as lem # noqa: E402
|
||
from E03_StabSection.E03_StabSection_Engine_Search import ( # noqa: E402
|
||
analyze,
|
||
evaluate_specified,
|
||
)
|
||
from E03_StabSection.E03_StabSection_Engine_Surface import ( # noqa: E402
|
||
poly_slices,
|
||
three_point_center,
|
||
)
|
||
from test_532_2_stab_lem_benchmarks import ( # noqa: E402
|
||
ACADS1C_MATS,
|
||
ACADS1C_REGIONS,
|
||
ACADS_EXT,
|
||
FK_EXT,
|
||
_acads3_model,
|
||
_acads4_model,
|
||
circ,
|
||
mats,
|
||
near,
|
||
nonc,
|
||
)
|
||
|
||
MP = "morgenstern_price"
|
||
SEAM_3B = [[41.85, 27.75], [44.0, 26.5], [63.5, 27.0], [73.31, 40.0]]
|
||
|
||
|
||
def test_mp_constant_function_equals_spencer():
|
||
# f(x) ≡ 1 이면 M-P 식 = Spencer 식 — 같은 F · λ
|
||
prep = lem.Prepared(_acads3_model(), -1)
|
||
pts = sorted((-x, z) for x, z in SEAM_3B)
|
||
vx, vz = np.array([[p[0] for p in pts]]), np.array([[p[1] for p in pts]])
|
||
sl = poly_slices(prep, vx, vz, 50)
|
||
center = three_point_center(vx, vz)
|
||
sp = lem.spencer(sl, center, np.array([1.3]))
|
||
mp = gle.morgenstern_price(sl, center, "constant", F0=np.array([1.3]))
|
||
assert abs(mp["F"][0] - sp["F"][0]) < 1e-6
|
||
assert abs(mp["lambda"][0] - sp["lambda"][0]) < 1e-5
|
||
|
||
|
||
def test_mp_specified_surfaces_slide2():
|
||
# #8 지정 비원호 GLE 1.262 · #21 지정 원 M-P 건기 2.075 · r_u 0.25 1.760
|
||
r = evaluate_specified(_acads3_model(), polyline=SEAM_3B, direction=-1, methods=(MP,))
|
||
near(r[MP]["F"], 1.262, 0.01)
|
||
m = {"materials": mats(("s", 120, 600, 20)), "regions": [{"material_id": "s", "polygon": FK_EXT}]}
|
||
near(evaluate_specified(m, circle=[120, 90, 80], direction=1, methods=(MP,))[MP]["F"], 2.075, 0.01)
|
||
m["groundwater"] = {"mode": "ru", "ru": 0.25}
|
||
near(evaluate_specified(m, circle=[120, 90, 80], direction=1, methods=(MP,))[MP]["F"], 1.760, 0.01)
|
||
|
||
|
||
def test_mp_search_slide2():
|
||
# #1 원호 GLE 0.986 · #3 원호 GLE 1.374
|
||
m = {"materials": mats(("s", 20, 3, 19.6)), "regions": [{"material_id": "s", "polygon": ACADS_EXT}]}
|
||
near(circ(analyze(m, direction=-1, noncircular=False), MP), 0.986, 0.01)
|
||
r = analyze({"materials": ACADS1C_MATS, "regions": ACADS1C_REGIONS}, direction=-1, noncircular=False)
|
||
near(circ(r, MP), 1.374, 0.01)
|
||
|
||
|
||
def test_mp_acads4_noncircular():
|
||
# #9 블록 + 최적화 GLE 0.683 (약층 좌표 = 그림 판독 → ±2 %)
|
||
r = analyze(_acads4_model(), direction=-1, methods=("janbu", "spencer", MP))
|
||
near(nonc(r, MP), 0.683, 0.02)
|
||
|
||
|
||
def _closure(rows, lam, f_last=1.0):
|
||
weight = sum(r["weight"] for r in rows)
|
||
for a, b in zip(rows, rows[1:]):
|
||
assert abs(a["E_right"] - b["E_left"]) < 1e-9 and abs(a["X_right"] - b["X_left"]) < 1e-9
|
||
assert abs(rows[-1]["E_right"]) < 1e-3 * weight # 끝 절편에서 힘 평형이 닫힘
|
||
assert abs(rows[0]["X_left"]) < 1e-12
|
||
for r in rows:
|
||
# 연직 평형 — N·cosα + S·sinα = W + (X_right − X_left) (지진 · 균열 없음)
|
||
a = np.radians(r["alpha_deg"])
|
||
lhs = r["normal_total"] * np.cos(a) + r["shear_mobilized"] * np.sin(a)
|
||
assert abs(lhs - (r["weight"] + r["X_right"] - r["X_left"])) < 1e-6 * weight
|
||
|
||
|
||
def test_free_body_and_lambda_curve_outputs():
|
||
m = {"materials": mats(("s", 20, 3, 19.6)), "regions": [{"material_id": "s", "polygon": ACADS_EXT}]}
|
||
r = evaluate_specified(m, circle=[29.104, 55.527, 30.527], direction=-1, methods=("spencer", MP))
|
||
for key in ("spencer", MP):
|
||
out = r[key]
|
||
rows = out["slices"]
|
||
assert {"normal_total", "pore_force", "shear_strength", "E_right", "X_right", "x_mid"} <= rows[0].keys()
|
||
_closure(rows, out["lambda"])
|
||
curve = out["lambda_curve"]
|
||
lam, fm, ff = (np.array(curve[k], float) for k in ("lambda", "Fm", "Ff"))
|
||
assert len(lam) == gle.CURVE_POINTS and np.isfinite(fm).sum() > 10
|
||
# 곡선 교점 = 해 (F · λ)
|
||
d = fm - ff
|
||
k = np.where(np.isfinite(d[:-1]) & np.isfinite(d[1:]) & (np.sign(d[:-1]) != np.sign(d[1:])))[0]
|
||
assert len(k)
|
||
j = k[np.argmin(np.abs(lam[k] - out["lambda"]))]
|
||
t = d[j] / (d[j] - d[j + 1])
|
||
assert abs(lam[j] + t * (lam[j + 1] - lam[j]) - out["lambda"]) < 0.02
|
||
assert abs(fm[j] + t * (fm[j + 1] - fm[j]) - out["F"]) < 0.01 * out["F"]
|
||
assert abs(r[MP]["slices"][-1]["X_right"]) < 1e-9 # 반정현 f = 0 (끝)
|
||
|
||
|
||
def test_safety_map_and_reference_only():
|
||
m = {"materials": mats(("s", 17.64, 9.8, 10)), "regions": [{"material_id": "s", "polygon": [[0, 0], [25, 0], [25, 10], [15, 10], [5, 5], [0, 5]]}]}
|
||
r = analyze(m, direction=-1)
|
||
smap = r["safety_map"]["surfaces"]
|
||
assert 0 < len(smap) <= 50
|
||
F = [s["F"] for s in smap]
|
||
assert F == sorted(F) and F[0] >= 0.97 * circ(r, "bishop")
|
||
assert all(len(s["points"]) == 25 and s["points"][0][0] < s["points"][-1][0] for s in smap)
|
||
# 비원호 Fellenius · Bishop = 참고 칸 · 최소 F 는 원호에서만
|
||
for k in ("ordinary", "bishop"):
|
||
res = r["methods"][k]
|
||
assert res["noncircular"]["reference_only"] is True
|
||
assert res["min"]["kind"] == "circle"
|
||
assert r["methods"]["spencer"]["min"]["F"] <= circ(r, "spencer") + 1e-12
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(pytest.main([__file__, "-q"]))
|