fish_farm_env / src /agentic_rl /constants.py
rahul24raj's picture
Upload folder using huggingface_hub
585cd37 verified
"""Biological, physical, and economic constants for the Fish Farm simulation.
ALL values sourced from the knowledge base:
- 01-BIOLOGY-AND-SCIENCE.md (species data, water quality thresholds)
- 02-REAL-WORLD-OPERATIONS.md (operational parameters, economics)
- 03-MATHEMATICAL-MODELS.md (equation parameters, MDP spec)
References inline as KB-XX-Section.
"""
import math
from dataclasses import dataclass
@dataclass(frozen=True)
class TilapiaParams:
"""Nile Tilapia (Oreochromis niloticus) biological parameters.
Source: FAO Annex 3 Fish Growth Model + KB-03 Section 15.6
"""
# Growth model (bioenergetic: dW/dt = H*W^m - k*W^n)
h: float = 0.4768 # consumption coefficient (KB-03 Sec 1.1)
m: float = 0.6277 # anabolism exponent (KB-03 Sec 1.1)
n: float = 0.8373 # catabolism exponent (KB-03 Sec 1.1)
b: float = 0.7108 # assimilation efficiency (KB-03 Sec 1.1)
a: float = 0.0559 # feeding catabolism fraction (KB-03 Sec 1.1)
k_min: float = 0.0104 # catabolism base rate g^(1-n)/d (KB-03 Sec 1.1)
s: float = 0.0288 # catabolism temp sensitivity 1/C (KB-03 Sec 1.1)
# Temperature (KB-01 Sec 6.2, KB-03 Sec 1.1)
T_min: float = 18.7 # minimum survivable temp (C)
T_opt: float = 32.4 # optimal growth temp (C)
T_max: float = 39.7 # maximum survivable temp (C)
T_lethal_low: float = 11.0 # lethal low (KB-01 Sec 16)
T_lethal_high: float = 42.0 # lethal high (KB-01 Sec 16)
# Stocking / lifecycle
w_initial: float = 5.0 # initial fingerling weight (g)
w_market: float = 500.0 # target market weight (g)
N_initial: int = 10000 # default stocking count
base_mortality: float = 0.0005 # natural daily mortality rate (KB-03 Sec 15.6)
# Feeding
fcr_target: float = 1.6 # target FCR (KB-02 Sec 3)
max_feeding_pct: float = 5.0 # max % body weight/day for fingerlings (KB-01 Sec 5.2)
protein_fraction: float = 0.40 # feed protein content (KB-02 Sec 3)
n_wasted_fraction: float = 0.50 # fraction of consumed N not retained (KB-03 Sec 2.2)
# DO requirements (KB-01 Sec 2.2)
DO_optimal: float = 5.0 # mg/L, growth reduction begins below
DO_stress: float = 3.0 # mg/L, significant stress
DO_lethal: float = 1.0 # mg/L, fish die
# Allometric: W = a_wl * L^b_wl
a_wl: float = 0.0282 # weight-length coefficient
b_wl: float = 3.0 # isometric growth
@dataclass(frozen=True)
class WaterParams:
"""Water quality parameters and thresholds.
Source: KB-01 Sec 2-4, KB-03 Sec 2, KB-01 Sec 16
"""
# Dissolved Oxygen thresholds (mg/L) — tilapia-specific
DO_optimal: float = 5.0 # above this = no growth reduction
DO_crit: float = 5.0 # growth reduction begins (KB-03 Sec 1.2)
DO_min: float = 3.0 # growth ceases (KB-03 Sec 1.2)
DO_lethal: float = 1.0 # fish die (KB-01 Sec 2.2)
DO_saturation_30C: float = 7.54 # mg/L at 30C (KB-01 Sec 2.1)
# Unionized Ammonia thresholds (mg/L) — KB-01 Sec 3.3, KB-03 Sec 1.2
UIA_safe: float = 0.02 # safe zone
UIA_crit: float = 0.05 # chronic stress begins
UIA_lethal: float = 0.6 # lethal (KB-03 Sec 15.6)
# TAN (mg/L)
TAN_max: float = 5.0 # maximum before emergency (KB-03 Sec 15.6)
# Nitrite (mg/L) — KB-01 Sec 3.4
NO2_safe: float = 0.1
NO2_stress: float = 0.5
NO2_lethal: float = 5.0
# pH — KB-01 Sec 4.1
pH_min: float = 6.5
pH_max: float = 8.5
pH_lethal_low: float = 4.0
pH_lethal_high: float = 11.0
pH_default: float = 7.5
# Nitrification — KB-01 Sec 3.1
O2_per_TAN: float = 4.57 # g O2 per g TAN oxidized (KB-01 Sec 3.1)
alkalinity_per_TAN: float = 7.14 # g CaCO3 per g TAN oxidized
# Reaeration coefficient (1/h) — KB-03 Sec 2.1
K_a_base: float = 0.04 # base reaeration at moderate wind
# Biofilter removal efficiency (fraction) — KB-03 Sec 2.2
biofilter_efficiency: float = 0.6
@dataclass(frozen=True)
class DiseaseParams:
"""SEIR disease model parameters.
Source: KB-03 Sec 4, KB-01 Sec 7
"""
# SEIR rates (per day) — KB-03 Sec 4.1-4.2
beta: float = 0.4 # transmission coefficient
sigma: float = 0.2 # 1/latent_period (5 day latent)
gamma: float = 0.1 # recovery rate (10 day infectious)
alpha: float = 0.05 # disease-induced mortality
mu: float = 0.0005 # natural mortality (background)
# Environmental triggers — KB-01 Sec 7
stress_DO_threshold: float = 3.5 # DO below this increases disease risk
stress_ammonia_threshold: float = 0.04 # UIA above this increases disease risk
stress_temp_deviation: float = 5.0 # degrees from optimal that increases risk
stress_density_threshold: float = 80.0 # fish/m3 that increases disease risk
# Disease probability per hour when stressed
outbreak_prob_per_hour: float = 0.0005 # ~1.2% chance per day under stress
# Treatment effectiveness
treatment_recovery_boost: float = 2.0 # multiplier on gamma during treatment
treatment_cost_per_day: float = 50.0 # $/day
treatment_duration_days: int = 5
@dataclass(frozen=True)
class EconomicsParams:
"""Economic parameters.
Source: KB-02 Sec 7, KB-03 Sec 6
"""
feed_price_per_kg: float = 0.50 # $/kg feed (KB-03 Sec 15.6)
market_price_per_kg: float = 3.00 # $/kg fish (KB-03 Sec 15.6)
fixed_cost_per_day: float = 10.0 # $/day operating (KB-03 Sec 15.6)
harvest_cost_per_kg: float = 0.30 # $/kg harvested
fingerling_cost: float = 0.05 # $/fingerling
electricity_cost_per_kwh: float = 0.12
aeration_power_kw: float = 2.0 # aerator power consumption
heater_power_kw: float = 5.0
water_cost_per_m3: float = 0.50
@dataclass(frozen=True)
class SystemParams:
"""Physical system parameters.
Source: KB-02 Sec 14, KB-03 Sec 15.6
"""
dt_hours: float = 1.0 # agent decision interval (hours)
sub_steps: int = 10 # water quality sub-steps per hour (6 min each)
tank_volume_m3: float = 100.0 # RAS tank volume
tank_depth_m: float = 1.5 # tank depth
initial_stocking_density: float = 50.0 # fish/m3
# Aeration
# Real RAS: 2kW aerator × 1.8 SAE = 3.6 kg O2/h ÷ 100m³ tank = 36 mg/L/h
# We cap at 20 to represent a well-sized but not over-engineered system
max_aeration_rate: float = 20.0 # mg O2/L/h at full power (KB-02 Sec 4)
aerator_SAE: float = 1.8 # kg O2/kWh standard aeration efficiency
# Water exchange
max_exchange_rate: float = 0.10 # fraction of volume per hour
incoming_water_DO: float = 7.0 # mg/L (fresh water supply)
incoming_water_temp: float = 28.0
incoming_water_TAN: float = 0.0
# Biofilter
biofilter_volume_m3: float = 5.0
biofilter_VTR: float = 350.0 # g TAN/m3/d moving-bed reactor (KB-03 Sec 2.2)
# Latitude for photoperiod (tropical)
latitude: float = 10.0 # degrees N (tropical fish farm)
# ---- Singleton instances ----
TILAPIA = TilapiaParams()
WATER = WaterParams()
DISEASE = DiseaseParams()
ECONOMICS = EconomicsParams()
SYSTEM = SystemParams()
# ---- Utility functions ----
def uia_fraction(pH: float, temp_c: float) -> float:
"""Calculate fraction of TAN that is toxic unionized ammonia (NH3).
Source: KB-01 Sec 3.2
Fraction NH3 = 1 / (1 + 10^(pKa - pH))
pKa = 0.09018 + 2729.92 / T_kelvin
"""
T_kelvin = temp_c + 273.15
pKa = 0.09018 + 2729.92 / T_kelvin
fraction = 1.0 / (1.0 + 10.0 ** (pKa - pH))
return fraction
def do_saturation(temp_c: float) -> float:
"""DO saturation concentration at given temperature (freshwater, sea level).
Source: KB-03 Sec 2.1
DO_sat(T) = 468 / (31.6 + T) [approximate, mg/L]
"""
return 468.0 / (31.6 + temp_c)
def photoperiod_hours(day_of_year: int, latitude: float = 10.0) -> float:
"""Calculate daylight hours from day of year and latitude.
Source: KB-03 Sec 15.3 (photoperiod scalar pi = P_h / 12)
"""
declination = 23.45 * math.sin(math.radians(360 / 365 * (day_of_year - 81)))
lat_rad = math.radians(latitude)
dec_rad = math.radians(declination)
cos_hour_angle = -math.tan(lat_rad) * math.tan(dec_rad)
cos_hour_angle = max(-1.0, min(1.0, cos_hour_angle))
hour_angle = math.degrees(math.acos(cos_hour_angle))
return 2.0 * hour_angle / 15.0
def temperature_factor(T: float, T_min: float = TILAPIA.T_min,
T_opt: float = TILAPIA.T_opt,
T_max: float = TILAPIA.T_max) -> float:
"""Bell-shaped temperature response function tau(T).
Source: KB-03 Sec 1.2
tau(T) = exp{-4.6 * ((T_opt - T) / (T_opt - T_min))^4} if T < T_opt
tau(T) = exp{-4.6 * ((T - T_opt) / (T_max - T_opt))^4} if T >= T_opt
"""
if T <= T_min or T >= T_max:
return 0.0
if T < T_opt:
x = (T_opt - T) / (T_opt - T_min)
else:
x = (T - T_opt) / (T_max - T_opt)
return math.exp(-4.6 * x ** 4)
def do_factor(DO: float, DO_crit: float = WATER.DO_crit,
DO_min: float = WATER.DO_min) -> float:
"""Piecewise linear DO response function sigma(DO).
Source: KB-03 Sec 1.2
"""
if DO >= DO_crit:
return 1.0
elif DO >= DO_min:
return (DO - DO_min) / (DO_crit - DO_min)
else:
return 0.0
def uia_factor(UIA: float, UIA_crit: float = WATER.UIA_crit,
UIA_max: float = WATER.UIA_lethal) -> float:
"""Piecewise linear UIA response function v(UIA).
Source: KB-03 Sec 1.2
"""
if UIA <= UIA_crit:
return 1.0
elif UIA <= UIA_max:
return (UIA_max - UIA) / (UIA_max - UIA_crit)
else:
return 0.0