| """ |
| Geo-Lab AI Engine: ๊ธฐ๋ณธ ์งํ ํด๋์ค |
| """ |
| import numpy as np |
| from dataclasses import dataclass, field |
| from typing import Optional, Tuple |
|
|
|
|
| @dataclass |
| class Terrain: |
| """2D ๋์ด๋งต ๊ธฐ๋ฐ ์งํ ๋ฐ์ดํฐ ๊ตฌ์กฐ""" |
| |
| width: int = 100 |
| height: int = 100 |
| cell_size: float = 10.0 |
| |
| |
| elevation: np.ndarray = field(default=None) |
| |
| |
| rock_hardness: np.ndarray = field(default=None) |
| |
| def __post_init__(self): |
| if self.elevation is None: |
| self.elevation = np.zeros((self.height, self.width)) |
| if self.rock_hardness is None: |
| self.rock_hardness = np.ones((self.height, self.width)) * 0.5 |
| |
| @classmethod |
| def create_slope(cls, width: int, height: int, |
| max_elevation: float = 1000.0, |
| slope_direction: str = 'south') -> 'Terrain': |
| """๊ฒฝ์ฌ๋ฉด ์งํ ์์ฑ""" |
| terrain = cls(width=width, height=height) |
| |
| if slope_direction == 'south': |
| |
| for y in range(height): |
| terrain.elevation[y, :] = max_elevation * (1 - y / height) |
| elif slope_direction == 'east': |
| for x in range(width): |
| terrain.elevation[:, x] = max_elevation * (1 - x / width) |
| |
| return terrain |
| |
| @classmethod |
| def create_v_valley_initial(cls, width: int = 100, height: int = 100, |
| valley_depth: float = 50.0) -> 'Terrain': |
| """V์๊ณก ์๋ฎฌ๋ ์ด์
์ด๊ธฐ ์งํ""" |
| terrain = cls(width=width, height=height) |
| |
| |
| for y in range(height): |
| terrain.elevation[y, :] = 500 * (1 - y / height) |
| |
| |
| center = width // 2 |
| for x in range(width): |
| dist = abs(x - center) |
| if dist < 5: |
| terrain.elevation[:, x] -= valley_depth * (1 - dist / 5) |
| |
| return terrain |
| |
| def get_slope(self) -> np.ndarray: |
| """๊ฐ ์
์ ๊ฒฝ์ฌ๋ ๊ณ์ฐ (๋ผ๋์)""" |
| dy, dx = np.gradient(self.elevation, self.cell_size) |
| return np.arctan(np.sqrt(dx**2 + dy**2)) |
| |
| def get_flow_direction(self) -> Tuple[np.ndarray, np.ndarray]: |
| """๋ฌผ ํ๋ฆ ๋ฐฉํฅ ๋ฒกํฐ (๊ฐ์ฅ ๊ฐํ๋ฅธ ํ๊ฐ ๋ฐฉํฅ)""" |
| dy, dx = np.gradient(self.elevation, self.cell_size) |
| magnitude = np.sqrt(dx**2 + dy**2) + 1e-10 |
| return -dx / magnitude, -dy / magnitude |
|
|
|
|
| @dataclass |
| class Water: |
| """ํ์ฒ ์๋ฌธ ๋ฐ์ดํฐ""" |
| |
| terrain: Terrain |
| |
| |
| discharge: np.ndarray = field(default=None) |
| |
| |
| velocity: np.ndarray = field(default=None) |
| |
| |
| flow_x: np.ndarray = field(default=None) |
| flow_y: np.ndarray = field(default=None) |
| |
| def __post_init__(self): |
| shape = (self.terrain.height, self.terrain.width) |
| if self.discharge is None: |
| self.discharge = np.zeros(shape) |
| if self.velocity is None: |
| self.velocity = np.zeros(shape) |
| if self.flow_x is None: |
| self.flow_x, self.flow_y = self.terrain.get_flow_direction() |
| |
| def add_precipitation(self, rate: float = 0.001): |
| """๊ฐ์ ์ถ๊ฐ (m/s per cell)""" |
| self.discharge += rate |
| |
| def accumulate_flow(self): |
| """ํ๋ฆ ๋์ ๊ณ์ฐ (๊ฐ๋จํ D8 ์๊ณ ๋ฆฌ์ฆ)""" |
| h, w = self.terrain.height, self.terrain.width |
| accumulated = self.discharge.copy() |
| |
| |
| indices = np.argsort(self.terrain.elevation.ravel())[::-1] |
| |
| for idx in indices: |
| y, x = idx // w, idx % w |
| if accumulated[y, x] <= 0: |
| continue |
| |
| |
| min_elev = self.terrain.elevation[y, x] |
| min_neighbor = None |
| |
| for dy, dx in [(-1,0), (1,0), (0,-1), (0,1)]: |
| ny, nx = y + dy, x + dx |
| if 0 <= ny < h and 0 <= nx < w: |
| if self.terrain.elevation[ny, nx] < min_elev: |
| min_elev = self.terrain.elevation[ny, nx] |
| min_neighbor = (ny, nx) |
| |
| if min_neighbor: |
| accumulated[min_neighbor] += accumulated[y, x] |
| |
| self.discharge = accumulated |
| |
| |
| slope = self.terrain.get_slope() + 0.001 |
| self.velocity = 2.0 * np.sqrt(slope) * np.power(self.discharge + 0.1, 0.4) |
|
|
|
|
| @dataclass |
| class SimulationState: |
| """์๋ฎฌ๋ ์ด์
์ํ ๊ด๋ฆฌ""" |
| |
| terrain: Terrain |
| water: Water |
| |
| time_step: float = 1.0 |
| current_time: float = 0.0 |
| |
| |
| climate_level: float = 1.0 |
| sea_level: float = 0.0 |
| tectonic_energy: float = 0.0 |
| |
| def step(self): |
| """1 ํ์์คํ
์งํ""" |
| self.current_time += self.time_step |
| |
| |
| self.water.add_precipitation(rate=0.001 * self.climate_level) |
| |
| |
| self.water.accumulate_flow() |
|
|