| """ |
| Geo-Lab AI Engine: ์นจ์ ๋ก์ง |
| Stream Power Law ๊ธฐ๋ฐ ํ๋ฐฉ/์ธก๋ฐฉ ์นจ์ ๊ตฌํ |
| """ |
| import numpy as np |
| from typing import TYPE_CHECKING |
|
|
| if TYPE_CHECKING: |
| from .base import Terrain, Water |
|
|
|
|
| def vertical_erosion(terrain: 'Terrain', water: 'Water', |
| k_erosion: float = 0.0001, |
| m_exponent: float = 0.5, |
| n_exponent: float = 1.0, |
| dt: float = 1.0) -> np.ndarray: |
| """ |
| ํ๋ฐฉ ์นจ์ (Vertical/Downcutting Erosion) |
| Stream Power Law: E = K * A^m * S^n |
| |
| Parameters: |
| ----------- |
| terrain : Terrain |
| ์งํ ๊ฐ์ฒด |
| water : Water |
| ์๋ฌธ ๊ฐ์ฒด |
| k_erosion : float |
| ์นจ์ ๊ณ์ (์์ ๊ฒฝ๋์ ์ญ์) |
| m_exponent : float |
| ์ ๋ ์ง์ (๋ณดํต 0.4-0.6) |
| n_exponent : float |
| ๊ฒฝ์ฌ ์ง์ (๋ณดํต 1.0) |
| dt : float |
| ์๊ฐ ๋จ์ (๋
) |
| |
| Returns: |
| -------- |
| erosion_amount : np.ndarray |
| ๊ฐ ์
์ ์นจ์๋ (m) |
| """ |
| |
| slope = terrain.get_slope() |
| |
| |
| |
| |
| effective_k = k_erosion * (1 - terrain.rock_hardness * 0.9) |
| |
| erosion_rate = effective_k * np.power(water.discharge + 0.1, m_exponent) * np.power(slope + 0.001, n_exponent) |
| |
| erosion_amount = erosion_rate * dt |
| |
| |
| max_erosion = 10.0 |
| erosion_amount = np.clip(erosion_amount, 0, max_erosion) |
| |
| return erosion_amount |
|
|
|
|
| def lateral_erosion(terrain: 'Terrain', water: 'Water', |
| k_lateral: float = 0.00005, |
| curvature_factor: float = 1.0, |
| dt: float = 1.0) -> np.ndarray: |
| """ |
| ์ธก๋ฐฉ ์นจ์ (Lateral Erosion) |
| ๊ณก๋ฅ ํ์ฒ์์ ๋ฐ๊นฅ์ชฝ(๊ณต๊ฒฉ์ฌ๋ฉด)์ ๊น์ |
| |
| Parameters: |
| ----------- |
| terrain : Terrain |
| ์งํ ๊ฐ์ฒด |
| water : Water |
| ์๋ฌธ ๊ฐ์ฒด |
| k_lateral : float |
| ์ธก๋ฐฉ ์นจ์ ๊ณ์ |
| curvature_factor : float |
| ๊ณก๋ฅ ๊ฐ์กฐ ๊ณ์ |
| dt : float |
| ์๊ฐ ๋จ์ (๋
) |
| |
| Returns: |
| -------- |
| erosion_amount : np.ndarray |
| ๊ฐ ์
์ ์นจ์๋ (m) |
| """ |
| h, w = terrain.height, terrain.width |
| erosion = np.zeros((h, w)) |
| |
| |
| flow_x, flow_y = water.flow_x, water.flow_y |
| |
| |
| curvature_x = np.gradient(flow_x, axis=1) |
| curvature_y = np.gradient(flow_y, axis=0) |
| curvature = np.sqrt(curvature_x**2 + curvature_y**2) |
| |
| |
| |
| erosion = k_lateral * water.discharge * water.velocity * curvature * curvature_factor * dt |
| |
| |
| channel_mask = water.discharge > 0.1 |
| erosion = erosion * channel_mask |
| |
| return np.clip(erosion, 0, 5.0) |
|
|
|
|
| def headward_erosion(terrain: 'Terrain', water: 'Water', |
| k_headward: float = 0.0002, |
| dt: float = 1.0) -> np.ndarray: |
| """ |
| ๋๋ถ ์นจ์ (Headward Erosion) |
| ํ์ฒ์ ์๋ฅ ๋์ด ์ ์ ๋ค๋ก ๋ฌผ๋ฌ๋จ |
| ํญํฌ, ํ๊ณก ํ์ฑ์ ํต์ฌ ๋ฉ์ปค๋์ฆ |
| |
| Returns: |
| -------- |
| erosion_amount : np.ndarray |
| ๊ฐ ์
์ ์นจ์๋ (m) |
| """ |
| h, w = terrain.height, terrain.width |
| erosion = np.zeros((h, w)) |
| |
| |
| slope = terrain.get_slope() |
| steep_mask = slope > np.percentile(slope[slope > 0], 90) |
| |
| |
| channel_mask = water.discharge > 0.5 |
| knickpoint_mask = steep_mask & channel_mask |
| |
| |
| erosion[knickpoint_mask] = k_headward * water.discharge[knickpoint_mask] * dt |
| |
| return np.clip(erosion, 0, 2.0) |
|
|
|
|
| def apply_erosion(terrain: 'Terrain', erosion_amount: np.ndarray, |
| min_elevation: float = 0.0): |
| """ |
| ์งํ์ ์นจ์ ์ ์ฉ |
| |
| Parameters: |
| ----------- |
| terrain : Terrain |
| ์์ ํ ์งํ ๊ฐ์ฒด |
| erosion_amount : np.ndarray |
| ์นจ์๋ ๋ฐฐ์ด |
| min_elevation : float |
| ์ต์ ๊ณ ๋ (ํด์๋ฉด) |
| """ |
| terrain.elevation -= erosion_amount |
| terrain.elevation = np.maximum(terrain.elevation, min_elevation) |
|
|
|
|
| def mass_wasting(terrain: 'Terrain', |
| critical_slope: float = 0.7, |
| transfer_rate: float = 0.3) -> np.ndarray: |
| """ |
| ์ฌ๋ฉด ๋ถ๊ดด (Mass Wasting) |
| V์๊ณก ํ์ฑ ์ ์์ ์ฌ๋ฉด์ด ๋ฌด๋์ง๋ ๊ณผ์ |
| |
| Returns: |
| -------- |
| elevation_change : np.ndarray |
| ๊ณ ๋ ๋ณํ๋ (๋์ ๊ณณ -, ๋ฎ์ ๊ณณ +) |
| """ |
| h, w = terrain.height, terrain.width |
| change = np.zeros((h, w)) |
| |
| slope = terrain.get_slope() |
| |
| |
| unstable = slope > critical_slope |
| |
| for y in range(1, h-1): |
| for x in range(1, w-1): |
| if unstable[y, x]: |
| |
| elev = terrain.elevation[y, x] |
| neighbors = [ |
| (y-1, x), (y+1, x), (y, x-1), (y, x+1) |
| ] |
| |
| for ny, nx in neighbors: |
| if terrain.elevation[ny, nx] < elev: |
| transfer = (elev - terrain.elevation[ny, nx]) * transfer_rate * 0.25 |
| change[y, x] -= transfer |
| change[ny, nx] += transfer |
| |
| return change |
|
|