| """ |
| Wind Kernel (๋ฐ๋ ์ปค๋) |
| |
| ํ์ฑ ์งํ ํ์ฑ ํ๋ก์ธ์ค |
| - ํ์ (Deflation): ๋ฏธ์ธ ์
์ ์ ๊ฑฐ |
| - ๋ง์ (Abrasion): ๋ชจ๋ ์ถฉ๋์ ์ํ ์นจ์ |
| - ํ์ (Aeolian Deposition): ์ฌ๊ตฌ ํ์ฑ |
| |
| ํต์ฌ: |
| - ๋ฐ๋ ์๋์ ๋น๋กํ ์ด๋ฐ๋ ฅ |
| - ์
์ ํฌ๊ธฐ์ ๋ฐ๋ฅธ ์ ํ์ ์ด๋ฐ |
| """ |
|
|
| import numpy as np |
| from .grid import WorldGrid |
|
|
|
|
| class WindKernel: |
| """ |
| ๋ฐ๋ ์ปค๋ |
| |
| ๊ฑด์กฐ ์ง์ญ์์์ ํ์๊ณผ ํ์ ์ ์๋ฎฌ๋ ์ด์
. |
| """ |
| |
| def __init__(self, grid: WorldGrid, |
| wind_speed: float = 10.0, |
| wind_direction: float = 45.0, |
| K_erosion: float = 0.0001, |
| sand_threshold: float = 0.1): |
| self.grid = grid |
| self.wind_speed = wind_speed |
| self.wind_direction = np.radians(wind_direction) |
| self.K = K_erosion |
| self.sand_threshold = sand_threshold |
| |
| def get_wind_vector(self) -> tuple: |
| """ |
| ๋ฐ๋ ๋ฐฉํฅ ๋ฒกํฐ ๋ฐํ |
| |
| Returns: |
| (dy, dx): ๋ฐ๋ ๋ฐฉํฅ ๋จ์ ๋ฒกํฐ |
| """ |
| dx = np.sin(self.wind_direction) |
| dy = -np.cos(self.wind_direction) |
| return dy, dx |
| |
| def calculate_transport_capacity(self, |
| vegetation_cover: np.ndarray = None) -> np.ndarray: |
| """ |
| ๋ชจ๋ ์ด๋ฐ๋ ฅ ๊ณ์ฐ |
| |
| Args: |
| vegetation_cover: ์์ ํผ๋ณต๋ฅ (0~1, ๋์ผ๋ฉด ์ด๋ฐ๋ ฅ ๊ฐ์) |
| |
| Returns: |
| capacity: ์ด๋ฐ๋ ฅ ๋ฐฐ์ด |
| """ |
| h, w = self.grid.height, self.grid.width |
| |
| |
| base_capacity = (self.wind_speed ** 3) * self.K |
| |
| capacity = np.ones((h, w)) * base_capacity |
| |
| |
| if vegetation_cover is not None: |
| capacity *= (1 - vegetation_cover) |
| |
| |
| slope, aspect = self.grid.get_gradient() |
| |
| |
| |
| dy, dx = self.get_wind_vector() |
| |
| |
| exposure = dx * np.gradient(self.grid.elevation, axis=1) + \ |
| dy * np.gradient(self.grid.elevation, axis=0) |
| |
| |
| capacity *= (1 + 0.5 * np.clip(exposure, -1, 1)) |
| |
| |
| capacity[self.grid.is_underwater()] = 0 |
| |
| return np.maximum(capacity, 0) |
| |
| def deflation(self, capacity: np.ndarray, dt: float = 1.0) -> np.ndarray: |
| """ |
| ํ์ (Deflation) - ๋ฏธ์ธ ์
์ ์ ๊ฑฐ |
| |
| Args: |
| capacity: ์ด๋ฐ๋ ฅ ๋ฐฐ์ด |
| dt: ์๊ฐ ๊ฐ๊ฒฉ |
| |
| Returns: |
| erosion: ์นจ์๋ ๋ฐฐ์ด |
| """ |
| h, w = self.grid.height, self.grid.width |
| |
| |
| available = self.grid.sediment |
| erosion = np.minimum(capacity * dt, available) |
| |
| |
| self.grid.sediment -= erosion |
| |
| return erosion |
| |
| def transport_and_deposit(self, |
| eroded_material: np.ndarray, |
| capacity: np.ndarray, |
| dt: float = 1.0) -> np.ndarray: |
| """ |
| ํ์ (Aeolian Deposition) - ์ฌ๊ตฌ ํ์ฑ |
| |
| Args: |
| eroded_material: ์นจ์๋ ๋ฌผ์ง๋ |
| capacity: ์ด๋ฐ๋ ฅ ๋ฐฐ์ด |
| dt: ์๊ฐ ๊ฐ๊ฒฉ |
| |
| Returns: |
| deposition: ํด์ ๋ ๋ฐฐ์ด |
| """ |
| h, w = self.grid.height, self.grid.width |
| |
| dy, dx = self.get_wind_vector() |
| |
| |
| deposition = np.zeros((h, w), dtype=np.float64) |
| |
| for r in range(h): |
| for c in range(w): |
| if eroded_material[r, c] <= 0: |
| continue |
| |
| |
| tr = int(r + dy * 2) |
| tc = int(c + dx * 2) |
| |
| if not (0 <= tr < h and 0 <= tc < w): |
| continue |
| |
| |
| if capacity[tr, tc] < capacity[r, c]: |
| |
| deposit_amount = eroded_material[r, c] * (1 - capacity[tr, tc] / capacity[r, c]) |
| deposition[tr, tc] += deposit_amount |
| else: |
| |
| |
| deposition[tr, tc] += eroded_material[r, c] * 0.1 |
| |
| |
| self.grid.add_sediment(deposition) |
| |
| return deposition |
| |
| def form_barchan(self, iteration: int = 5): |
| """ |
| ๋ฐ๋ฅดํ ์ฌ๊ตฌ ํ์ฑ (๋ฐ๋ณต ์๋ฎฌ๋ ์ด์
) |
| |
| ๋ฐ๋๋ฐ์ด: ์๊ฒฝ์ฌ, ๋ฐ๋๊ทธ๋: ๊ธ๊ฒฝ์ฌ (Slip Face) |
| |
| Args: |
| iteration: ํํ ๋ค๋ฌ๊ธฐ ๋ฐ๋ณต ํ์ |
| """ |
| h, w = self.grid.height, self.grid.width |
| dy, dx = self.get_wind_vector() |
| |
| |
| dune_mask = self.grid.sediment > self.sand_threshold |
| |
| for _ in range(iteration): |
| |
| for r in range(1, h - 1): |
| for c in range(1, w - 1): |
| if not dune_mask[r, c]: |
| continue |
| |
| |
| wr, wc = int(r - dy), int(c - dx) |
| if 0 <= wr < h and 0 <= wc < w: |
| |
| avg = (self.grid.sediment[r, c] + self.grid.sediment[wr, wc]) / 2 |
| self.grid.sediment[r, c] = self.grid.sediment[r, c] * 0.9 + avg * 0.1 |
| |
| self.grid.update_elevation() |
| |
| def step(self, vegetation_cover: np.ndarray = None, |
| dt: float = 1.0) -> dict: |
| """ |
| 1๋จ๊ณ ๋ฐ๋ ์์ฉ ์คํ |
| |
| Args: |
| vegetation_cover: ์์ ํผ๋ณต๋ฅ |
| dt: ์๊ฐ ๊ฐ๊ฒฉ |
| |
| Returns: |
| result: ์นจ์/ํด์ ๊ฒฐ๊ณผ |
| """ |
| |
| capacity = self.calculate_transport_capacity(vegetation_cover) |
| |
| |
| erosion = self.deflation(capacity, dt) |
| |
| |
| deposition = self.transport_and_deposit(erosion, capacity, dt) |
| |
| return { |
| 'erosion': erosion, |
| 'deposition': deposition, |
| 'capacity': capacity |
| } |
|
|