Merge remote-tracking branch 'origin/sub_laptop_7' into sub_laptop_5
E04 Search 충돌 풂 — 점 단위 덩이 나눔 · 전역 최소 다듬기(_set_best) 유지 + sub7 best.cells 를 _set_best 에 넣어 탐색 · 다듬기 두 길 모두 셀 전달 (요구 351) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,7 +82,7 @@ class SearchResult:
|
|||||||
f_min: np.ndarray # 셀별 최소 안전율(다스리는 값) · 닿지 않은 셀 = nan
|
f_min: np.ndarray # 셀별 최소 안전율(다스리는 값) · 닿지 않은 셀 = nan
|
||||||
volume: np.ndarray # 그 임계 활동체 부피
|
volume: np.ndarray # 그 임계 활동체 부피
|
||||||
method: np.ndarray # 0 = Bishop · 1 = Janbu 보정
|
method: np.ndarray # 0 = Bishop · 1 = Janbu 보정
|
||||||
best: dict # 전체 최소 활동체(중심 · 반지름 · 비 · 방향 · 방법별 F · 부피)
|
best: dict # 전체 최소 활동체(중심 · 반지름 · 비 · 방향 · 방법별 F · 부피 · 셀)
|
||||||
trials: int = 0
|
trials: int = 0
|
||||||
evaluated: int = 0
|
evaluated: int = 0
|
||||||
seconds: float = 0.0
|
seconds: float = 0.0
|
||||||
@@ -476,7 +476,7 @@ def search(strata: Strata, params: SearchParams | None = None) -> SearchResult:
|
|||||||
thr,
|
thr,
|
||||||
ref,
|
ref,
|
||||||
)
|
)
|
||||||
k = _set_best(best, res, f, meth, xyz[part][trial], r, ratio)
|
k = _set_best(best, res, f, meth, xyz[part][trial], r, ratio, cols, nx)
|
||||||
if k >= 0:
|
if k >= 0:
|
||||||
at_best.update(
|
at_best.update(
|
||||||
node=int(ids[part][trial[k]]),
|
node=int(ids[part][trial[k]]),
|
||||||
@@ -553,7 +553,9 @@ def search(strata: Strata, params: SearchParams | None = None) -> SearchResult:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _set_best(best: dict, res: dict, f, meth, centers, r: float, ratio: float) -> int:
|
def _set_best(
|
||||||
|
best: dict, res: dict, f, meth, centers, r: float, ratio: float, cols, nx: int
|
||||||
|
) -> int:
|
||||||
"""덩이들 가운데 best 보다 낮은 것이 있으면 best 를 바꾸고 그 번호 · 없으면 −1."""
|
"""덩이들 가운데 best 보다 낮은 것이 있으면 best 를 바꾸고 그 번호 · 없으면 −1."""
|
||||||
if not np.isfinite(f).any():
|
if not np.isfinite(f).any():
|
||||||
return -1
|
return -1
|
||||||
@@ -570,6 +572,8 @@ def _set_best(best: dict, res: dict, f, meth, centers, r: float, ratio: float) -
|
|||||||
center=np.asarray(centers[k], float).tolist(),
|
center=np.asarray(centers[k], float).tolist(),
|
||||||
radius=float(r),
|
radius=float(r),
|
||||||
ratio=float(ratio),
|
ratio=float(ratio),
|
||||||
|
# 활동체 셀(iy·nx+ix) — 한 구가 자른 여러 덩이 중 이 덩이만
|
||||||
|
cells=np.unique(cols.iy[cols.group == k] * nx + cols.ix[cols.group == k]).tolist(),
|
||||||
)
|
)
|
||||||
return k
|
return k
|
||||||
|
|
||||||
@@ -627,7 +631,17 @@ def _polish(strata, p, best, at_best, f_min, volume, method, node_map) -> None:
|
|||||||
return
|
return
|
||||||
cols, tr, res, f, meth = trial(o.x)
|
cols, tr, res, f, meth = trial(o.x)
|
||||||
_update(f, meth, res, cols, tr, np.array([at_best["node"]]), f_min, volume, method, node_map)
|
_update(f, meth, res, cols, tr, np.array([at_best["node"]]), f_min, volume, method, node_map)
|
||||||
_set_best(best, res, f, meth, np.repeat(o.x[None, :3], len(tr), 0), float(o.x[3]), ratio)
|
_set_best(
|
||||||
|
best,
|
||||||
|
res,
|
||||||
|
f,
|
||||||
|
meth,
|
||||||
|
np.repeat(o.x[None, :3], len(tr), 0),
|
||||||
|
float(o.x[3]),
|
||||||
|
ratio,
|
||||||
|
cols,
|
||||||
|
strata.z_top.shape[1],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _update(
|
def _update(
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
z = 합성 지표(원지형 + 노선면) · f = 셀별 최소 안전율 · v = 그 임계 체적 m³ · m = 방법(0 Bishop
|
z = 합성 지표(원지형 + 노선면) · f = 셀별 최소 안전율 · v = 그 임계 체적 m³ · m = 방법(0 Bishop
|
||||||
· 1 Janbu 보정) · 닿지 않은 셀 = null
|
· 1 Janbu 보정) · 닿지 않은 셀 = null
|
||||||
best = 전체 최소 활동면 {x, y, z(중심), r, ratio, azimuth_deg(활동 방향), f, volume, method, ix, iy,
|
best = 전체 최소 활동면 {x, y, z(중심), r, ratio, azimuth_deg(활동 방향), f, volume, method, ix, iy,
|
||||||
hovland · bishop · janbu_corrected(같은 면 방법별 F) · truncated_ratio · floor([셀, 못 뚫는 층 윗면])}
|
hovland · bishop · janbu_corrected(같은 면 방법별 F) · truncated_ratio · floor([셀, 못 뚫는 층 윗면])
|
||||||
|
· cells(활동체 덩이 셀 번호 — 한 구가 자른 여러 덩이 중 이 덩이)}
|
||||||
타원체 = 회전축(활동 방향에 수직) 반축만 ratio·r — 533-2 `trial_columns` 와 같은 꼴.
|
타원체 = 회전축(활동 방향에 수직) 반축만 ratio·r — 533-2 `trial_columns` 와 같은 꼴.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ from E03_StabSection.E03_StabSection_Types import DEFAULT_ROAD_SURCHARGE_KPA
|
|||||||
from E04_Stab3D import E04_Stab3D_Engine_Grid as grid_engine
|
from E04_Stab3D import E04_Stab3D_Engine_Grid as grid_engine
|
||||||
from E04_Stab3D import E04_Stab3D_Engine_Search as search_engine
|
from E04_Stab3D import E04_Stab3D_Engine_Search as search_engine
|
||||||
|
|
||||||
ENGINE_VERSION = "e04-3" # 엔진 판 — 바뀌면 지문이 달라져 다시 셈
|
ENGINE_VERSION = "e04-4" # 엔진 판 — 바뀌면 지문이 달라져 다시 셈
|
||||||
STAGE_DIR = "E04_Stab3D"
|
STAGE_DIR = "E04_Stab3D"
|
||||||
SECTION_LENGTH_M = 100.0 # 자동 구간 길이
|
SECTION_LENGTH_M = 100.0 # 자동 구간 길이
|
||||||
MAX_SECTION_M = 500.0 # 한 번에 셀 구간 상한(시간 · 메모리)
|
MAX_SECTION_M = 500.0 # 한 번에 셀 구간 상한(시간 · 메모리)
|
||||||
@@ -167,6 +168,8 @@ def _floor(strata: Any, best: dict[str, Any]) -> list[list[float]]:
|
|||||||
slip = np.where(rest >= 0, cz - np.sqrt(np.maximum(rest, 0)), np.nan)
|
slip = np.where(rest >= 0, cz - np.sqrt(np.maximum(rest, 0)), np.nan)
|
||||||
hard = strata.hard_top
|
hard = strata.hard_top
|
||||||
hit = np.isfinite(slip) & (slip < strata.z_top) & (hard > slip) & (hard < strata.z_top)
|
hit = np.isfinite(slip) & (slip < strata.z_top) & (hard > slip) & (hard < strata.z_top)
|
||||||
|
if "cells" in best: # 활동체 덩이 셀만
|
||||||
|
hit &= np.isin(np.arange(hit.size), best["cells"]).reshape(hit.shape)
|
||||||
return [[int(i), round(float(hard.ravel()[i]), 2)] for i in np.flatnonzero(hit.ravel())]
|
return [[int(i), round(float(hard.ravel()[i]), 2)] for i in np.flatnonzero(hit.ravel())]
|
||||||
|
|
||||||
|
|
||||||
@@ -258,6 +261,7 @@ def run_engine(
|
|||||||
"janbu_corrected": float(best["janbu_corrected"]),
|
"janbu_corrected": float(best["janbu_corrected"]),
|
||||||
"truncated_ratio": float(best["truncated_ratio"]),
|
"truncated_ratio": float(best["truncated_ratio"]),
|
||||||
"floor": _floor(strata, best),
|
"floor": _floor(strata, best),
|
||||||
|
"cells": [int(i) for i in best["cells"]],
|
||||||
},
|
},
|
||||||
"trials": int(res.trials),
|
"trials": int(res.trials),
|
||||||
"reference_fs": reference_fs(ground, settings["condition"]),
|
"reference_fs": reference_fs(ground, settings["condition"]),
|
||||||
@@ -292,6 +296,16 @@ def pack(
|
|||||||
best["ix"] = int(round((best["x"] - grid["x0"]) / grid["dx"]))
|
best["ix"] = int(round((best["x"] - grid["x0"]) / grid["dx"]))
|
||||||
best["iy"] = int(round((best["y"] - grid["y0"]) / grid["dx"]))
|
best["iy"] = int(round((best["y"] - grid["y0"]) / grid["dx"]))
|
||||||
f = np.asarray(raw["f"], dtype=float)
|
f = np.asarray(raw["f"], dtype=float)
|
||||||
|
cells = np.asarray(best.get("cells") or [], int)
|
||||||
|
# 가까운 측점 = 활동체 평면 중심(없으면 구 중심) — 화면 최소 표시와 같은 자리
|
||||||
|
where = (
|
||||||
|
(
|
||||||
|
grid["x0"] + (cells % grid["nx"]).mean() * grid["dx"],
|
||||||
|
grid["y0"] + (cells // grid["nx"]).mean() * grid["dx"],
|
||||||
|
)
|
||||||
|
if len(cells)
|
||||||
|
else (best["x"], best["y"])
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"fingerprint": finger,
|
"fingerprint": finger,
|
||||||
"engine": ENGINE_VERSION,
|
"engine": ENGINE_VERSION,
|
||||||
@@ -316,7 +330,7 @@ def pack(
|
|||||||
"by_method": {
|
"by_method": {
|
||||||
k: round(float(best[k]), 3) for k in ("hovland", "bishop", "janbu_corrected")
|
k: round(float(best[k]), 3) for k in ("hovland", "bishop", "janbu_corrected")
|
||||||
},
|
},
|
||||||
"chainage_m": float(_nearest_chainage(stations, best["x"], best["y"])),
|
"chainage_m": float(_nearest_chainage(stations, *where)),
|
||||||
"cells": int(np.isfinite(f).sum()),
|
"cells": int(np.isfinite(f).sum()),
|
||||||
"trials": int(raw.get("trials") or 0),
|
"trials": int(raw.get("trials") or 0),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -70,22 +70,24 @@ export function slipHeight(best: SlipBest, x: number, y: number): number | null
|
|||||||
return rest < 0 ? null : best.z - Math.sqrt(rest);
|
return rest < 0 ? null : best.z - Math.sqrt(rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 잘린 셀 → 활동면 높이(활동면이 지표보다 낮은 셀만 · 못 뚫는 층 윗면 아래로는 안 내려감) */
|
/** 잘린 셀 → 활동면 높이(활동면이 지표보다 낮은 셀만 · 못 뚫는 층 윗면 아래로는 안 내려감 · 엔진 덩이 셀만) */
|
||||||
export function cutCells(
|
export function cutCells(
|
||||||
grid: Grid,
|
grid: Grid,
|
||||||
z: readonly (number | null)[],
|
z: readonly (number | null)[],
|
||||||
best: SlipBest,
|
best: SlipBest,
|
||||||
): Map<number, number> {
|
): Map<number, number> {
|
||||||
const floor = new Map(best.floor ?? []);
|
const floor = new Map(best.floor ?? []);
|
||||||
|
const mass = best.cells ? new Set(best.cells) : null;
|
||||||
const out = new Map<number, number>();
|
const out = new Map<number, number>();
|
||||||
z.forEach((top, index) => {
|
z.forEach((top, index) => {
|
||||||
if (top === null) return;
|
if (top === null || (mass && !mass.has(index))) return;
|
||||||
const x = grid.x0 + (index % grid.nx) * grid.dx;
|
const x = grid.x0 + (index % grid.nx) * grid.dx;
|
||||||
const y = grid.y0 + Math.floor(index / grid.nx) * grid.dx;
|
const y = grid.y0 + Math.floor(index / grid.nx) * grid.dx;
|
||||||
const slip = slipHeight(best, x, y);
|
const slip = slipHeight(best, x, y);
|
||||||
if (slip === null) return;
|
const base = Math.max(slip ?? top, floor.get(index) ?? -Infinity);
|
||||||
const base = Math.max(slip, floor.get(index) ?? -Infinity);
|
// 엔진 덩이 셀 = 그대로(셀 안 일부만 잘린 가장자리 = 두께 0) · 옛 저장본 = 셀 중심이 잘린 칸만
|
||||||
if (base < top) out.set(index, base);
|
if (mass) out.set(index, Math.min(base, top));
|
||||||
|
else if (base < top) out.set(index, base);
|
||||||
});
|
});
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ export interface SlipBest {
|
|||||||
iy: number;
|
iy: number;
|
||||||
/** 못 뚫는 층에 잘린 셀 — [셀 번호, 층 윗면] · 활동면을 그 높이에서 자름 */
|
/** 못 뚫는 층에 잘린 셀 — [셀 번호, 층 윗면] · 활동면을 그 높이에서 자름 */
|
||||||
floor?: [number, number][];
|
floor?: [number, number][];
|
||||||
|
/** 활동체 덩이 셀 번호(엔진 · 한 구가 자른 여러 덩이 중 이 덩이) — 없으면(옛 저장본) 잘린 셀 전부 */
|
||||||
|
cells?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Summary3d {
|
export interface Summary3d {
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ def test_engine_raster_and_best(root):
|
|||||||
assert best["r"] > 0 and best["ratio"] in (1.0, 2.0) and 0 <= best["azimuth_deg"] < 360
|
assert best["r"] > 0 and best["ratio"] in (1.0, 2.0) and 0 <= best["azimuth_deg"] < 360
|
||||||
assert best["f"] == best["bishop"] or best["method"] == 1 # 다스림 = Bishop(잘림 없음)
|
assert best["f"] == best["bishop"] or best["method"] == 1 # 다스림 = Bishop(잘림 없음)
|
||||||
assert raw["reference_fs"] == 1.5 # 마스터 기본 건기
|
assert raw["reference_fs"] == 1.5 # 마스터 기본 건기
|
||||||
|
# 활동체 셀 = 엔진 덩이 — 모두 그 셀 최소(= 전체 최소)
|
||||||
|
cells = np.asarray(best["cells"])
|
||||||
|
assert len(cells) and np.allclose(f.ravel()[cells], best["f"])
|
||||||
# 합성 지표 — 노면 위(측점 40 · o=0) = 설계 100 · 깎기 끝 밖(o=+20 · 원지형) = 지반
|
# 합성 지표 — 노면 위(측점 40 · o=0) = 설계 100 · 깎기 끝 밖(o=+20 · 원지형) = 지반
|
||||||
z = raw["z"]
|
z = raw["z"]
|
||||||
jy0 = round((0.0 - grid["y0"]) / grid["dx"])
|
jy0 = round((0.0 - grid["y0"]) / grid["dx"])
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ writeFileSync(process.argv[3], JSON.stringify({
|
|||||||
sphere: mesh(M.slipMesh(c.grid, c.z, c.sphere)),
|
sphere: mesh(M.slipMesh(c.grid, c.z, c.sphere)),
|
||||||
ellipsoid: mesh(M.slipMesh(c.grid, c.z, c.ellipsoid)),
|
ellipsoid: mesh(M.slipMesh(c.grid, c.z, c.ellipsoid)),
|
||||||
cut: Array.from(M.cutCells(c.grid, c.z, c.sphere).keys()),
|
cut: Array.from(M.cutCells(c.grid, c.z, c.sphere).keys()),
|
||||||
|
massCut: Array.from(M.cutCells(c.grid, c.z, { ...c.sphere, cells: [7 * 21 + 10, 7 * 21 + 11, 0] }).keys()),
|
||||||
|
massBase0: M.cutCells(c.grid, c.z, { ...c.sphere, cells: [0] }).get(0),
|
||||||
center: M.footprintCenter(c.grid, M.cutCells(c.grid, c.z, c.sphere), c.sphere),
|
center: M.footprintCenter(c.grid, M.cutCells(c.grid, c.z, c.sphere), c.sphere),
|
||||||
centerEmpty: M.footprintCenter(c.grid, new Map(), c.sphere),
|
centerEmpty: M.footprintCenter(c.grid, new Map(), c.sphere),
|
||||||
floored: Object.fromEntries(M.cutCells(c.grid, c.z,
|
floored: Object.fromEntries(M.cutCells(c.grid, c.z,
|
||||||
@@ -207,6 +209,8 @@ def test_terrain_hole_matches_cut(ts):
|
|||||||
"""활동체를 걷어낸 지표 = 지표 꼭짓점 − 잘린 셀 · 구멍 테두리는 활동면 메쉬와 같은 꼭짓점."""
|
"""활동체를 걷어낸 지표 = 지표 꼭짓점 − 잘린 셀 · 구멍 테두리는 활동면 메쉬와 같은 꼭짓점."""
|
||||||
cut = [i for i, _ in _py_cut(SPHERE)]
|
cut = [i for i, _ in _py_cut(SPHERE)]
|
||||||
assert ts["cut"] == cut
|
assert ts["cut"] == cut
|
||||||
|
assert ts["massCut"] == [0, 7 * 21 + 10, 7 * 21 + 11] # 엔진 덩이 셀 그대로(0 = 안 잘림 · 두께 0)
|
||||||
|
assert ts["massBase0"] == Z[0]
|
||||||
assert ts["holed"]["n"] == ts["terrain"]["n"] - len(cut)
|
assert ts["holed"]["n"] == ts["terrain"]["n"] - len(cut)
|
||||||
assert not set(ts["holed"]["cells"]) & set(cut)
|
assert not set(ts["holed"]["cells"]) & set(cut)
|
||||||
assert _py_rim(set(cut)) <= set(ts["holed"]["cells"])
|
assert _py_rim(set(cut)) <= set(ts["holed"]["cells"])
|
||||||
|
|||||||
Reference in New Issue
Block a user