| """ |
| Mass Movement Kernel (๋งค์ค๋ฌด๋ธ๋จผํธ) |
| |
| ์ค๋ ฅ์ ์ํ ์ฌ๋ฉด ์ด๋ ํ๋ก์ธ์ค |
| - ์ฐ์ฌํ (Landslide) |
| - ์ฌ๋ผํ (Slump) |
| - ๋์ (Rockfall) |
| |
| ํต์ฌ ์๋ฆฌ: |
| ๊ฒฝ์ฌ(Slope) > ์๊ณ ๊ฒฝ์ฌ(Critical Angle) โ ๋ฌผ์ง ์ด๋ |
| """ |
|
|
| import numpy as np |
| from .grid import WorldGrid |
|
|
|
|
| class MassMovementKernel: |
| """ |
| ๋งค์ค๋ฌด๋ธ๋จผํธ ์ปค๋ |
| |
| ๊ฒฝ์ฌ ์์ ์ฑ์ ๊ฒ์ฌํ๊ณ , ๋ถ์์ ํ ๊ณณ์์ ๋ฌผ์ง ์ด๋์ ์๋ฎฌ๋ ์ด์
. |
| """ |
| |
| def __init__(self, grid: WorldGrid, |
| friction_angle: float = 35.0, |
| cohesion: float = 0.0): |
| self.grid = grid |
| self.friction_angle = friction_angle |
| self.cohesion = cohesion |
| |
| |
| self.critical_slope = np.tan(np.radians(friction_angle)) |
| |
| def check_stability(self) -> np.ndarray: |
| """ |
| ๊ฒฝ์ฌ ์์ ์ฑ ๊ฒ์ฌ |
| |
| Returns: |
| unstable_mask: ๋ถ์์ ํ ์
๋ง์คํฌ (True = ๋ถ์์ ) |
| """ |
| slope, _ = self.grid.get_gradient() |
| |
| |
| unstable = slope > self.critical_slope |
| |
| return unstable |
| |
| def trigger_landslide(self, unstable_mask: np.ndarray, |
| efficiency: float = 0.5) -> np.ndarray: |
| """ |
| ์ฐ์ฌํ ๋ฐ์ |
| |
| ๋ถ์์ ํ ์
์์ ๋ฌผ์ง์ ๋ฎ์ ๊ณณ์ผ๋ก ์ด๋. |
| |
| Args: |
| unstable_mask: ๋ถ์์ ๋ง์คํฌ |
| efficiency: ์ด๋ ํจ์จ (0.0~1.0, 1.0์ด๋ฉด ์์ ์ด๋) |
| |
| Returns: |
| change: ์งํ ๋ณํ๋ |
| """ |
| h, w = self.grid.height, self.grid.width |
| change = np.zeros((h, w), dtype=np.float64) |
| |
| if not np.any(unstable_mask): |
| return change |
| |
| |
| dr = np.array([-1, -1, -1, 0, 0, 1, 1, 1]) |
| dc = np.array([-1, 0, 1, -1, 1, -1, 0, 1]) |
| |
| elev = self.grid.elevation |
| slope, _ = self.grid.get_gradient() |
| |
| |
| unstable_coords = np.argwhere(unstable_mask) |
| |
| for r, c in unstable_coords: |
| |
| excess_slope = slope[r, c] - self.critical_slope |
| if excess_slope <= 0: |
| continue |
| |
| |
| |
| available = self.grid.sediment[r, c] + self.grid.bedrock[r, c] * 0.1 |
| move_amount = min(excess_slope * efficiency * 5.0, available) |
| |
| if move_amount <= 0: |
| continue |
| |
| |
| min_z = elev[r, c] |
| target = None |
| |
| for k in range(8): |
| nr, nc = r + dr[k], c + dc[k] |
| if 0 <= nr < h and 0 <= nc < w: |
| if elev[nr, nc] < min_z: |
| min_z = elev[nr, nc] |
| target = (nr, nc) |
| |
| if target is None: |
| continue |
| |
| tr, tc = target |
| |
| |
| change[r, c] -= move_amount |
| change[tr, tc] += move_amount * 0.9 |
| |
| |
| |
| loss_mask = change < 0 |
| loss = -change[loss_mask] |
| |
| sed_loss = np.minimum(loss, self.grid.sediment[loss_mask]) |
| rock_loss = loss - sed_loss |
| |
| self.grid.sediment[loss_mask] -= sed_loss |
| self.grid.bedrock[loss_mask] -= rock_loss |
| |
| |
| self.grid.sediment += np.maximum(change, 0) |
| |
| self.grid.update_elevation() |
| |
| return change |
| |
| def step(self, dt: float = 1.0) -> np.ndarray: |
| """ |
| 1๋จ๊ณ ๋งค์ค๋ฌด๋ธ๋จผํธ ์คํ |
| |
| Args: |
| dt: ์๊ฐ ๊ฐ๊ฒฉ (์ฌ์ฉํ์ง ์์ง๋ง ์ธํฐํ์ด์ค ์ผ๊ด์ฑ) |
| |
| Returns: |
| change: ์งํ ๋ณํ๋ |
| """ |
| |
| unstable = self.check_stability() |
| |
| |
| change = self.trigger_landslide(unstable) |
| |
| return change |
|
|