diff --git a/E03_StabSection/E03_StabSection_Engine_LEM.py b/E03_StabSection/E03_StabSection_Engine_LEM.py index 79e755295..b3b05c057 100644 --- a/E03_StabSection/E03_StabSection_Engine_LEM.py +++ b/E03_StabSection/E03_StabSection_Engine_LEM.py @@ -28,6 +28,7 @@ TOL = 1e-6 #: Janbu 보정 f0 식의 d/L 상한(지식DB 02 §2.3) JANBU_DL_MAX = 0.35 MAX_ITER = 100 +WALL_ID = "wall" # Engine_Model.WALL_MATERIAL id class Prepared: @@ -44,6 +45,8 @@ class Prepared: self.gamma_sat = np.array([m["gamma_sat"] for m in mats] + [0.0]) self.c = np.array([m["c"] for m in mats] + [np.nan]) self.tanphi = np.array([np.tan(np.radians(m["phi"])) for m in mats] + [np.nan]) + # 벽(옹벽 · 석축) — 바닥이 벽 안을 지나는 활동면은 버림(벽 밑 · 뒤로 돌아가는 면만 후보) + self.blocked = np.array([m["id"] == WALL_ID for m in mats] + [False]) polys = [] for region in model["regions"]: rings = [region["polygon"], *region.get("holes", [])] @@ -209,6 +212,7 @@ def slices(prep: Prepared, xe: np.ndarray, ze: np.ndarray) -> dict: xw = np.where(W > 0, (Ws * xm + Mq) / np.maximum(W, _TINY), xm) valid = np.all(found & (zb >= prep.bottom_z - 1e-6) & np.isfinite(zt), axis=1) valid &= np.all(b > 0, axis=1) + valid &= ~np.any(prep.blocked[base_mat] & found, axis=1) return { "xe": xe, "ze": ze, diff --git a/E03_StabSection/E03_StabSection_Types.py b/E03_StabSection/E03_StabSection_Types.py index adfa50720..e4763828c 100644 --- a/E03_StabSection/E03_StabSection_Types.py +++ b/E03_StabSection/E03_StabSection_Types.py @@ -4,6 +4,8 @@ E04 가 이 파일을 그대로 import — 이름 · 열쇠를 바꾸지 않음. """ +import json +from pathlib import Path from typing import Literal, TypedDict GAMMA_W = 9.81 # 물 단위중량 kN/m³ @@ -30,54 +32,6 @@ class Surcharge(TypedDict): q: float # 연직 등분포 kPa -# 요구서 345 6장 기본값(사용자가 E02 에서 바꿈) — 사전조사 후보의 보수 쪽. -DEFAULT_MATERIALS: list[Material] = [ - { - "id": "weathered_soil", - "name": "풍화토", - "gamma_t": 18.0, - "gamma_sat": 19.0, - "c": 15.0, - "phi": 30.0, - }, - { - "id": "weathered_rock", - "name": "풍화암", - "gamma_t": 20.0, - "gamma_sat": 21.0, - "c": 30.0, - "phi": 33.0, - }, - { - "id": "soft_rock", - "name": "연암", - "gamma_t": 24.0, - "gamma_sat": 24.5, - "c": 150.0, - "phi": 35.0, - }, - { - "id": "hard_rock", - "name": "경암", - "gamma_t": 26.0, - "gamma_sat": 26.0, - "c": 300.0, - "phi": 40.0, - }, - {"id": "fill", "name": "성토", "gamma_t": 18.0, "gamma_sat": 19.0, "c": 10.0, "phi": 25.0}, - { - "id": "colluvium", - "name": "붕적토", - "gamma_t": 17.5, - "gamma_sat": 18.5, - "c": 5.0, - "phi": 27.0, - }, -] - -DEFAULT_ROAD_SURCHARGE_KPA = 10.0 # 노면 하중 기본 - - def material(raw: dict) -> Material: """밖에서 온 물성 한 줄을 꼴에 맞춤 — 빠진 γsat 는 γt · 음수 · 범위 밖은 오류.""" gamma_t = float(raw["gamma_t"]) @@ -122,3 +76,14 @@ def surcharges(raw: list[dict] | None) -> list[Surcharge]: if x1 > x0: out.append({"x0": x0, "x1": x1, "q": float(item["q"])}) return out + + +# 기본 물성 · 노면 하중 = M02 마스터 층 `ground_defaults.json`(E02 초기값과 한 벌 · 사용자가 E02 에서 바꿈). +_DEFAULTS = json.loads( + ( + Path(__file__).resolve().parents[1] + / "resources/master_template/stability/ground_defaults.json" + ).read_text(encoding="utf-8") +) +DEFAULT_MATERIALS: list[Material] = [material(m) for m in _DEFAULTS["materials"]] +DEFAULT_ROAD_SURCHARGE_KPA = float(_DEFAULTS["road_surcharge_kpa"]) # 노면 하중 기본 diff --git a/E06_StabRock/E06_StabRock_Model.py b/E06_StabRock/E06_StabRock_Model.py index 2cfe7d1ae..95e57df69 100644 --- a/E06_StabRock/E06_StabRock_Model.py +++ b/E06_StabRock/E06_StabRock_Model.py @@ -40,7 +40,7 @@ DEFAULT_SETTINGS: dict[str, Any] = { "lateral_planar_deg": engine.LATERAL_PLANAR_DEG, "lateral_toppling_deg": engine.LATERAL_TOPPLING_DEG, "gamma_r": None, # None = E02 측점 암 물성 γt - "water_fill_wet": 0.5, # 우기 수위 = 비탈 높이 ½(KDS 11 70 05 표 4.3-3 암반) + "water_fill_wet": 0.5, # 우기 = 균열 깊이 · 활동면 높이 ½(KDS 11 70 05 표 4.3-3 암반) "crack": {"mode": "none", "z": None}, "bolt": {"force": 0.0, "plunge": 15.0}, } diff --git a/resources/tester/test_532_4_stab_walls_water.py b/resources/tester/test_532_4_stab_walls_water.py index 86bb68eb5..c15bd00f6 100644 --- a/resources/tester/test_532_4_stab_walls_water.py +++ b/resources/tester/test_532_4_stab_walls_water.py @@ -30,7 +30,7 @@ from E02_StabGround import E02_StabGround_Model as ground_model # noqa: E402 from E02_StabGround import E02_StabGround_Router as router # noqa: E402 from E03_StabSection import E03_StabSection_Types as types # noqa: E402 from E03_StabSection.E03_StabSection_Engine_Model import section_model_from_b06 # noqa: E402 -from E03_StabSection.E03_StabSection_Engine_Search import analyze # noqa: E402 +from E03_StabSection.E03_StabSection_Engine_Search import analyze, evaluate_specified # noqa: E402 from E03_StabSection.E03_StabSection_Engine_Station import analyze_station # noqa: E402 from E03_StabSection.E03_StabSection_Walls import ( # noqa: E402 design_with_walls, @@ -122,6 +122,19 @@ def test_임계면이_벽을_안_지남(found): assert a["min_F"]["bishop"] != pytest.approx(b["min_F"]["bishop"], abs=1e-3) +def test_벽을_지나는_면은_버림(found): + entry = found["100.00"] + model = section_model_from_b06(CROSS, design_with_walls(DESIGN, entry), walls=walls_of(entry)) + side = next(s for s in model["sides"] if s["role"] == "fill") + through = [[-0.5, 10.0], [2.0, 8.5], [6.0, 6.4]] # 벽(x 1.4~3.2 · z 7~10) 한가운데 + under = [[-0.5, 10.0], [2.0, 6.0], [8.0, 5.2]] # 벽 바닥 아래 + for line, ok in ((through, False), (under, True)): + got = evaluate_specified( + model, polyline=line, direction=side["direction"], methods=("janbu", "spencer") + ) + assert all((got[m]["F"] is not None) == ok for m in got) + + def test_벽_없는_측점_모형_그대로(): assert design_with_walls(DESIGN, None) is DESIGN and walls_of(None) is None a = section_model_from_b06(CROSS, DESIGN) diff --git a/resources/tester/test_536_1_e06_rock.py b/resources/tester/test_536_1_e06_rock.py index 35fe8869b..44e91c4fe 100644 --- a/resources/tester/test_536_1_e06_rock.py +++ b/resources/tester/test_536_1_e06_rock.py @@ -48,6 +48,25 @@ def test_plane_hoek_tension_crack(): close(r["crack_b"], 15.3357, 1e-4) # 마루 뒤 거리(RocPlane 검증 #1) +def test_wet_default_kds_half_depth(): + """KDS 11 70 05 표 4.3-3 암반 우기 = 균열 · 활동면 높이 ½ 심도 포화 · 기준 1.2.""" + import json + from pathlib import Path + + fill = model.DEFAULT_SETTINGS["water_fill_wet"] + assert fill == 0.5 + z = 10.0 + r = eng.plane_fs(**SAU_MAU, water_fill=fill, crack={"mode": "depth", "z": z}) + close(r["V"], 0.5 * 10 * (z / 2) ** 2, 1e-9) # 균열 안 수위 = z/2 + dry = eng.plane_fs(**SAU_MAU) # 균열 없음 = 활동면 오름 60 · 가운데 수두 = (½ · 60)/2 + close(eng.plane_fs(**SAU_MAU, water_fill=fill)["U"], 0.5 * 10 * 15 * dry["A"], 1e-9) + root = Path(__file__).resolve().parents[2] + ground = json.loads( + (root / "resources/master_template/stability/ground_defaults.json").read_text("utf-8") + ) + assert ground["safety_factors"]["wet"]["cut"] == 1.2 + + def test_plane_critical_crack_search_matches_hoek(): """건조 · 윗면 수평 — 수치 탐색 임계 자리 = Hoek-Bray 식 z · b.""" r = eng.plane_fs( diff --git a/ui_template/ui_template_locale_e06.ts b/ui_template/ui_template_locale_e06.ts index e604dca01..8eeed1d8d 100644 --- a/ui_template/ui_template_locale_e06.ts +++ b/ui_template/ui_template_locale_e06.ts @@ -36,7 +36,10 @@ export const ui_locales_e06 = { E06_Set_LateralPlanar: ["평면 측방 한계 (°)", "Planar lateral limit (°)"], E06_Set_LateralTopple: ["전도 측방 한계 (°)", "Toppling lateral limit (°)"], E06_Set_Gamma: ["암 단위중량 (kN/m³ · 비우면 E02)", "Rock unit weight (kN/m³, blank = E02)"], - E06_Set_WaterWet: ["우기 수위 비 (0~1)", "Wet water ratio (0–1)"], + E06_Set_WaterWet: [ + "우기 수위 비 (0~1 · 균열 · 활동면 높이 대비)", + "Wet water ratio (0–1, of crack / sliding-plane height)", + ], E06_Set_Crack: ["인장균열", "Tension crack"], E06_Crack_None: ["없음", "None"], E06_Crack_Critical: ["임계(최소 안전율 자리)", "Critical (minimum FS)"], @@ -45,8 +48,8 @@ export const ui_locales_e06 = { E06_Set_BoltForce: ["볼트 힘 (평면 kN/m · 쐐기 kN)", "Bolt force (plane kN/m · wedge kN)"], E06_Set_BoltPlunge: ["볼트 경사 (수평 아래 °)", "Bolt plunge (below horizontal °)"], E06_SettingsHint: [ - "측방 한계 기본 20° (Dips 기본 · 지식DB 03 전도는 30°) · 우기 = 비탈 높이 ½ 수위(KDS 11 70 05) · 볼트 = 수동 지지력.", - "Lateral limit default 20° (Dips default; knowledge DB uses 30° for toppling) · wet = water at half height (KDS 11 70 05) · bolts = passive support.", + "측방 한계 기본 20° (Dips 기본 · 지식DB 03 전도는 30°) · 우기 = 인장균열 깊이 또는 활동면 높이의 ½ 까지 지하수 · 기준 안전율 1.2(KDS 11 70 05 표 4.3-3 암반) · 볼트 = 수동 지지력.", + "Lateral limit default 20° (Dips default; knowledge DB uses 30° for toppling) · wet = groundwater to ½ of tension-crack depth or sliding-plane height, required FS 1.2 (KDS 11 70 05 Table 4.3-3, rock) · bolts = passive support.", ], E06_Analyze: ["해석", "Analyze"], E06_Running: ["해석 중…", "Analyzing…"],