feat(567-1): E03 2D 비원호 출발 셋 최솟값 · 판정 = 원호 Bishop · 비원호 Spencer · M-P 중 작은 값 · 면 확인 · 모형 끝 면 · 탐색 한계 모형 끝

- 비원호 꼭짓점 최적화를 난수 씨앗 셋으로 돌려 찾은 면 모두에서 방법별 최소(NONCIRC_STARTS 3)
- 판정 면 = 원호 Bishop · 비원호 Spencer · M-P 중 작은 값 · 비원호 < 원호 Bishop × 0.85 = surface_check · 판정 면 진입 · 진출이 모형 끝 1 m 안 = edge
- 탐색 한계 바깥 끝 = 모형 끝(요구 392 · Slide2 기본) · 설정 search_reach(model_end · 2h · m)
- 새 지킴 시험 test_567_1(원호 기본 대 촘촘 ≤ 0.5 % · 비원호 ≤ 3 % · 다출발 최소 · 한계 · 판정 칸)
- 낡은 시험 고침: ACADS 3a Spencer · ACADS 4 · M-P ACADS 4 비원호 탐색 허용 ±5 % — 다출발이 원문보다 낮은 면(약층 바닥 따라감 · 3a Spencer 1.217)을 찾음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 20:25:21 +09:00
co-authored by Claude Opus 5.5
parent 6d4964ae55
commit a843aa3ad3
6 changed files with 266 additions and 29 deletions
@@ -36,6 +36,8 @@ WALL_MATERIAL = {
"phi": 45.0,
}
DEFAULT_HALF_WIDTH_M = 40.0
#: 탐색 한계 바깥 끝 — "model_end"(모형 끝 · Slide2 기본) · "2h"(비탈 끝 + max(5 m, 2H)) · 수(비탈 끝 + m)
SEARCH_REACH = "model_end"
def _polys(geom) -> list[Polygon]:
@@ -180,11 +182,13 @@ def widen_ground(cross: dict, sampler, half_width: float = DEFAULT_HALF_WIDTH_M,
return sorted(ground + added)
def side_cases(design: dict, ground: list, line: list) -> list[dict]:
def side_cases(
design: dict, ground: list, line: list, reach: str | float = SEARCH_REACH
) -> list[dict]:
"""좌 · 우 비탈마다 역할(절토 · 성토) · 활동 방향 · 탐색 한계. 좌 = +offset.
탐색 한계 = 노면 반대쪽 끝 ~ 비탈 끝(설계선이 원지반과 만나는 점) 바깥 max(5 m, 2H) — 설계 비탈을
지나는 면만 봄(바깥 자연 비탈 전체 붕괴는 E 광역 · 2차 몫). H = 노면 끝 ~ 비탈 끝 높이차.
탐색 한계 = 노면 반대쪽 끝 ~ reach(`SEARCH_REACH` · 기본 모형 끝 = 깎기 위 · 쌓기 아래 자연 비탈 큰 면까지).
H = 노면 끝 ~ 비탈 끝(설계선이 원지반과 만나는 점) 높이차.
"""
mode = design.get("section_mode") or "right_cut"
roles = {
@@ -215,10 +219,13 @@ def side_cases(design: dict, ground: list, line: list) -> list[dict]:
else:
day = edge
H = abs(float(np.interp(day, lx, lz)) - float(np.interp(edge, lx, lz)))
reach = day + sign * max(5.0, 2.0 * H)
reach = min(max(reach, gx[0]), gx[-1])
if reach == "model_end":
far = float(gx[-1] if sign > 0 else gx[0])
else:
far = day + sign * (max(5.0, 2.0 * H) if reach == "2h" else float(reach))
far = min(max(far, gx[0]), gx[-1])
inner = right_edge if side == "left" else left_edge
limits = tuple(sorted((inner, reach)))
limits = tuple(sorted((inner, far)))
direction = int(-sign) if inward else int(sign)
out.append(
{"side": side, "role": role, "direction": direction, "limits": limits, "height_m": H}
@@ -237,6 +244,7 @@ def section_model_from_b06(
seismic_kh: float = 0.0,
road_surcharge_kpa: float = DEFAULT_ROAD_SURCHARGE_KPA,
walls: list[dict] | None = None,
search_reach: str | float = SEARCH_REACH,
) -> dict:
"""B06 횡단 파일(samples · frame) + 설계(design) → 해석 모형 + 비탈 목록(`sides`).
@@ -244,6 +252,7 @@ def section_model_from_b06(
layers 기본 = 토사 측점 풍화토 한 층 · 암 측점 풍화토(암 경계까지) + 풍화암 / 연암.
wet = 우기 — 지하수위 = 공사 뒤 지표(KDS 11 70 05 표 4.3-3 · 토사 · 풍화암 지표 포화).
노면 하중 = 노면 끝 사이 등분포. 벽 도형(walls)은 B06 화면 산식(TS)이 그리므로 부르는 쪽이 넘김.
search_reach = 탐색 한계 바깥 끝(`side_cases` reach).
"""
ground = ground or ground_from_cross(cross)
line = [
@@ -281,7 +290,7 @@ def section_model_from_b06(
surcharge=loads,
area_range=(line[0][0], line[-1][0]) if line else None,
)
model["sides"] = side_cases(design, ground, line or ground)
model["sides"] = side_cases(design, ground, line or ground, search_reach)
model["station"] = {"chainage_m": cross.get("chainage_m"), "label": cross.get("label")}
return model