Geo-Lab / engine /base.py
HANSOL
Geo-Lab v4.1 - Clean deployment
2afa69c
"""
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 # X ๋ฐฉํ–ฅ ์…€ ์ˆ˜
height: int = 100 # Y ๋ฐฉํ–ฅ ์…€ ์ˆ˜
cell_size: float = 10.0 # ์…€๋‹น ์‹ค์ œ ๊ฑฐ๋ฆฌ (m)
# ๋†’์ด๋งต (m)
elevation: np.ndarray = field(default=None)
# ์•”์„ ์†์„ฑ
rock_hardness: np.ndarray = field(default=None) # 0-1, 1์ด ๊ฐ€์žฅ ๋‹จ๋‹จํ•จ
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
# ๊ฐ ์…€์˜ ์ˆ˜๋Ÿ‰ (mยณ/s)
discharge: np.ndarray = field(default=None)
# ์œ ์† (m/s)
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
# ์œ ์† ๊ณ„์‚ฐ (Manning ๋ฐฉ์ •์‹ ๋‹จ์ˆœํ™”)
slope = self.terrain.get_slope() + 0.001 # 0 ๋ฐฉ์ง€
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 # ์‹œ๋ฎฌ๋ ˆ์ด์…˜ 1์Šคํ… = 1๋…„
current_time: float = 0.0
# ์ „์—ญ ๋ณ€์ˆ˜ (Master Plan์˜ Global Controllers)
climate_level: float = 1.0 # ๊ธฐํ›„ (๊ฐ•์ˆ˜๋Ÿ‰ ๊ณ„์ˆ˜)
sea_level: float = 0.0 # ํ•ด์ˆ˜๋ฉด (m)
tectonic_energy: float = 0.0 # ์ง€๊ฐ ์—๋„ˆ์ง€ (์œต๊ธฐ์œจ m/year)
def step(self):
"""1 ํƒ€์ž„์Šคํ… ์ง„ํ–‰"""
self.current_time += self.time_step
# ๊ฐ•์ˆ˜ ์ถ”๊ฐ€
self.water.add_precipitation(rate=0.001 * self.climate_level)
# ํ๋ฆ„ ๋ˆ„์ 
self.water.accumulate_flow()