์๋ณด๋ฉด ์ํด ์ํ ์์๋ ฅ๋ฐ์ ์ ๋ชจ๋ ๋์์ด ํ๋ก๊ทธ๋จ Don't Miss Out: Small Modular Reactor Helper Program"
⚛ SMR Helper Program v1.0 — NuScale × TerraPower Benchmarked
์ํ ๋ชจ๋ํ ์์๋ ฅ๋ฐ์ ์
ํฌํผ ํ๋ก๊ทธ๋จ
NuScale Ansys ์คํ์ผ ์ด์ ๋ฌ ๋ถ์ + TerraPower ARMI Python ๊ธฐ๋ฐ
Reactor Model Hub-and-Spoke ์๋ํ ๊ตฌํ ๊ฐ์ด๋
#1
NuScale Power — Ansys ๋ฉํฐํผ์ง์ค
#2
TerraPower — ARMI ์คํ์์ค ํ๋ ์์ํฌ
๋ฒค์น๋งํน ๋ถ์ — ์ธ๊ณ 1·2์ SMR ํ๋ ์์ํฌ
NuScale Ansys vs TerraPower ARMI ํต์ฌ ์ํคํ
์ฒ ๋น๊ต
ํฌํผ ํ๋ก๊ทธ๋จ ๋ชฉํ: NuScale์ Ansys ์ด·๊ตฌ์กฐ·์์ ๋ถ์ ํ์ดํ๋ผ์ธ๊ณผ TerraPower ARMI์
Hub-and-Spoke ํ๋ฌ๊ทธ์ธ ์ํคํ
์ฒ๋ฅผ ํ์ด์ฌ ์คํ์์ค๋ก ์ฌํํฉ๋๋ค.
ํฌํผ ํ๋ก๊ทธ๋จ ํต์ฌ ๊ธฐ๋ฅ 6๊ฐ์ง
๐
① ์์๋ก ๋ชจ๋ธ ์์ฑ
YAML ์
๋ ฅ → ์ก๊ฐํ/์ฌ๊ฐํ ์ฝ์ด ์๋ ์์ฑ. ํต์ฐ๋ฃ ์งํฉ์ฒด·์ ์ด๋ด·๋๊ฐ์ฌ ์ฑ๋ ํ๋ผ๋ฏธํฐํ
๐ก
② ์ด์ ์ฒด ์๋ฎฌ๋ ์ด์
NuScale Ansys ๋ชจ๋ฐฉ conjugate heat transfer. Nusselt ์ ์๊ด๊ด๊ณ๋ก ๋๊ฐ์ฌ ์จ๋ ๋ถํฌ ๊ณ์ฐ
⚛
③ ์ค์ฑ์ ๋ฌผ๋ฆฌ ์ปค๋
4-์ธ์ ๊ณต์์ผ๋ก ์๊ณ ์กฐ๊ฑด(k-eff) ๊ณ์ฐ. ํต์ฐ๋ฃ ์์ง ๋ชจ๋ธ๋ง(Bateman ๋ฐฉ์ ์)
๐ก
④ ์์ ๋ถ์ ๋ชจ๋
DNBR · ์ต๋ ์ฐ๋ฃ ์จ๋ · ์๋ ฅ ๋ง์ง ๊ฒ์ฆ. NRC 10 CFR 50 ๊ธฐ์ค ์๋ ์ฑํฌ
๐
⑤ ํ๋ผ๋ฏธํฐ ์ค์
Python multiprocessing 1000+ ์ผ์ด์ค ๋ณ๋ ฌ ์คํ. ARMI ์คํ์ผ ์ต์ ํ ๋ฃจํ
๐
⑥ ์๋ํ ๋ฆฌํฌํธ
YAML ์
๋ ฅ → ์๋ฎฌ ์คํ → CSV/PNG ์ถ๋ ฅ ์์ ์๋ํ ์ํฌํ๋ก
ํ๊ฒฝ ์ค์น (ARMI ์์กด์ฑ ๋ฒค์น๋งํน)
bash — ํ๊ฒฝ ์ค์น
# 1. ๊ฐ์ํ๊ฒฝ ์์ฑ python -m venv smr_env source smr_env/bin/activate # Windows: smr_env\Scripts\activate # 2. ํต์ฌ ์์กด์ฑ ์ค์น (ARMI ๋ฒค์น๋งํน) pip install numpy scipy matplotlib pandas pyyaml # 3. ์ ํ: TerraPower ARMI ์๋ณธ ํด๋ก git clone https://github.com/terrapower/armi.git cd armi && pip install -e . # 4. ์ ํ: ์๊ฐํ ํ์ฅ pip install plotly seaborn h5py
SMR ๋ฌผ๋ฆฌ ์๋ฎฌ๋ ์ดํฐ (์ธํฐ๋ํฐ๋ธ)
NuScale ์ด์ ์ฒด + TerraPower ARMI ์คํ์ผ ์ค์๊ฐ ๊ณ์ฐ
⚛ SMR Core Thermal-Hydraulics Simulator v1.0
READY
์ฐ๋ฃ ์จ๋ (T_fuel)
620°C
๋๊ฐ์ฌ ์ ์ (Flow)
60%
์ ์ด๋ด ์ฝ์
(CR)
30%
๋์ถ๋ (Enrichment)
5.0%
k-eff (์๊ณ)
1.024
๋ฌด์ฐจ์
์ด์ถ๋ ฅ
77.0
MWth
DNBR
2.41
≥ 1.30 ์์
๋๊ฐ์ฌ ์ถ๊ตฌ T
285
°C
์๋ ฅ ๋ง์ง
18.2
% ์ฌ์
์์ ์ํ
✅ ์ ์
All limits OK
[00:00:00]SMR Helper Program v1.0 ์ด๊ธฐํ ์๋ฃ
[00:00:00]NuScale ์ด์ ์ฒด ๋ชจ๋ ๋ก๋๋จ
[00:00:00]ARMI Reactor ํด๋์ค ์ด๊ธฐํ๋จ
[00:00:00]ํ๋ผ๋ฏธํฐ ์ฌ๋ผ์ด๋๋ฅผ ์กฐ์ ํ๊ณ ์๋ฎฌ๋ ์ด์
์ ์คํํ์ธ์.
์ฝ๋ ๊ตฌ์กฐ ์ค๊ณ — ARMI Hub-and-Spoke ์ํคํ
์ฒ
TerraPower ARMI ๋ฒค์น๋งํน Python ๊ตฌํ์ฒด
STEP 1 — Reactor ํด๋์ค (Hub)
ARMI์ ์ค์ Reactor ํด๋์ค๋ฅผ ๋ชจ๋ฐฉํ ์ํ ๊ด๋ฆฌ ํ๋ธ. ํต์ฐ๋ฃ·๋๊ฐ์ฌ ๋ฐ๋·์จ๋ ๋ฑ ์ ์ฒด ์์๋ก ์ํ๋ฅผ ๋จ์ผ ๊ฐ์ฒด๋ก ๊ด๋ฆฌํฉ๋๋ค.
python — reactor_core.py (ARMI Hub ๋ชจ๋ฐฉ)
import numpy as np import yaml from dataclasses import dataclass, field from typing import Dict, List, Optional # ============================================================ # SMR Helper — Reactor Core (TerraPower ARMI ๋ฒค์น๋งํน) # Hub-and-Spoke ์ํคํ ์ฒ: Reactor๊ฐ ๋ชจ๋ ํ๋ฌ๊ทธ์ธ์ ํ๋ธ # ============================================================ @dataclass class FuelAssembly: """๋จ์ผ ํต์ฐ๋ฃ ์งํฉ์ฒด ์ํ (ARMI Block ๋ชจ๋ฐฉ)""" assembly_id: int enrichment: float # U-235 ๋์ถ๋ (%) burnup: float = 0.0 # ์ฐ์๋ (MWd/kgU) temp_fuel: float = 620.0 # ์ฐ๋ฃ ์จ๋ (°C) temp_clad: float = 340.0 # ํผ๋ณต๊ด ์จ๋ (°C) linear_power: float = 0.0 # ์ ํ ์ถ๋ ฅ ๋ฐ๋ (kW/m) @dataclass class ReactorState: """์ ์ฒด ์์๋ก ์ํ ์ค๋ ์ท""" power_mwth: float = 77.0 # ์ด์ถ๋ ฅ (MW) keff: float = 1.0 # ์ ํจ ์ฆ๋ฐฐ๊ณ์ t_inlet: float = 258.0 # ๋๊ฐ์ฌ ์ ๊ตฌ ์จ๋ (°C) t_outlet: float = 285.0 # ๋๊ฐ์ฌ ์ถ๊ตฌ ์จ๋ (°C) flow_rate: float = 587.0 # ์ง๋ ์ ๋ (kg/s) pressure: float = 12.76 # 1์ฐจ๊ณ ์๋ ฅ (MPa) dnbr_min: float = 2.41 # ์ต์ DNBR class Reactor: """ SMR Helper ์ค์ Reactor ํด๋์ค ─ TerraPower ARMI์ Reactor Hub ๋ชจ๋ฐฉ ─ ๋ชจ๋ ํ๋ฌ๊ทธ์ธ์ด ์ด ๊ฐ์ฒด๋ฅผ ํตํด ์ํธ์์ฉ """ def __init__(self, geom: str = 'hex', config_path: Optional[str] = None): self.geom = geom self.state = ReactorState() self.assemblies: List[FuelAssembly] = [] self.plugins: Dict = {} self._history: List[ReactorState] = [] if config_path: self._load_yaml(config_path) else: self._init_default_core() def _init_default_core(self): """NuScale SMR ๊ธฐ๋ณธ 77MWth ์ฝ์ด ์ด๊ธฐํ""" n_assemblies = 37 # NuScale ํ์ค ์งํฉ์ฒด ์ for i in range(n_assemblies): enr = 4.95 if i % 3 != 0 else 2.35 # 2์กด ์ฅ์ self.assemblies.append(FuelAssembly(i, enrichment=enr)) def _load_yaml(self, path: str): with open(path) as f: cfg = yaml.safe_load(f) self.state.power_mwth = cfg.get('power_mwth', 77.0) enr = cfg.get('enrichment', 4.95) n = cfg.get('n_assemblies', 37) self.assemblies = [FuelAssembly(i, enrichment=enr) for i in range(n)] def register_plugin(self, name: str, plugin): """Hub-and-Spoke: ํ๋ฌ๊ทธ์ธ ๋ฑ๋ก""" self.plugins[name] = plugin plugin.reactor = self # ์ญ์ฐธ์กฐ def run(self) -> ReactorState: """์ ์ฒด ๋ฌผ๋ฆฌ ํด์ ์์ฐจ ์คํ""" for name, plugin in self.plugins.items(): plugin.execute() self._history.append(ReactorState(**vars(self.state))) return self.state def avg_enrichment(self) -> float: return np.mean([a.enrichment for a in self.assemblies])
STEP 2 — ์ด์ ์ฒด ํ๋ฌ๊ทธ์ธ (NuScale Ansys ๋ชจ๋ฐฉ)
Dittus-Boelter ์๊ด๊ด๊ณ + DNBR ๊ณ์ฐ. NuScale์ด Ansys์์ ์ํํ๋ conjugate heat transfer๋ฅผ NumPy/SciPy๋ก ๊ตฌํํฉ๋๋ค.
python — thermal_plugin.py (NuScale Ansys ์ด์ ์ฒด ๋ชจ๋ฐฉ)
import numpy as np class ThermalHydraulicsPlugin: """ NuScale Ansys ์คํ์ผ ์ด์ ์ฒด ๋ถ์ ํ๋ฌ๊ทธ์ธ ─ Dittus-Boelter ์๊ด๊ด๊ณ (๊ฐ์ ๋๋ฅ Nu์) ─ DNBR: Departure from Nucleate Boiling Ratio ─ ๋๊ฐ์ฌ ์ถ๊ตฌ ์จ๋, ์ฐ๋ฃ/ํผ๋ณต๊ด ์จ๋ ๋ถํฌ ๊ณ์ฐ """ # ── NuScale SMR ์ค๊ณ ์์ ── CP_WATER = 5200 # ๋๊ฐ์ฌ ๋น์ด (J/kg·K) @ 285°C MU_WATER = 9.5e-5 # ๋์ ๋ (Pa·s) K_WATER = 0.58 # ์ด์ ๋๋ (W/m·K) PR_WATER = 0.86 # Prandtl ์ D_HYDRO = 0.012 # ์๋ ฅ ์ง๊ฒฝ (m) DNB_LIMIT = 1.3 # NRC ์ต์ DNBR ๊ธฐ์ค K_FUEL = 3.0 # UO2 ์ด์ ๋๋ (W/m·K) R_PELLET = 0.00465 # ์ฐ๋ฃ ํ ๋ ๋ฐ๊ฒฝ (m) def __init__(self): self.reactor = None # Hub๊ฐ ์ฃผ์ def execute(self): """ํ๋ฌ๊ทธ์ธ ์คํ ์ง์ ์ """ r = self.reactor m_dot = r.state.flow_rate q_total = r.state.power_mwth * 1e6 # W # 1. ๋๊ฐ์ฌ ์ถ๊ตฌ ์จ๋ (์๋์ง ๋ณด์กด) delta_T = q_total / (m_dot * self.CP_WATER) r.state.t_outlet = r.state.t_inlet + delta_T # 2. Dittus-Boelter Nu์ → ์ด์ ๋ฌ ๊ณ์ Re = (m_dot * self.D_HYDRO) / (self.MU_WATER * 0.05) Nu = 0.023 * (Re ** 0.8) * (self.PR_WATER ** 0.4) h_conv = Nu * self.K_WATER / self.D_HYDRO # W/m²·K # 3. ์ต๋ ์ ํ ์ถ๋ ฅ ๋ฐ๋ n_rods = len(r.assemblies) * 264 rod_len = 2.0 q_lin = q_total / (n_rods * rod_len) # W/m # 4. DNBR ๊ณ์ฐ (W-3 ์๊ด๊ด๊ณ ๋จ์ํ) q_dnb = 3.154e6 * (r.state.pressure / 15.5) ** 0.5 r.state.dnbr_min = q_dnb / max(q_lin / 0.05, 1) r.state.dnbr_min = min(r.state.dnbr_min, 5.0) # ์ํ ํด๋ฆฌํ # 5. ์ฐ๋ฃ ์ค์ฌ ์จ๋ q_triple = q_lin / (np.pi * self.R_PELLET ** 2) delta_T_fuel = q_triple * (self.R_PELLET ** 2) / (4 * self.K_FUEL) T_fuel_max = r.state.t_outlet + delta_T_fuel / 1000 # 6. ๊ฐ ์งํฉ์ฒด ์จ๋ ์ ๋ฐ์ดํธ for asm in r.assemblies: factor = asm.enrichment / r.avg_enrichment() asm.temp_fuel = T_fuel_max * factor asm.linear_power = q_lin * factor / 1000 # kW/m def get_temp_distribution(self) -> np.ndarray: """NuScale ์คํ์ผ ๋ฐ๊ฒฝ ๋ฐฉํฅ ์จ๋ ๋ถํฌ ๋ฐํ""" r_arr = np.linspace(0, self.R_PELLET, 50) T_surface = self.reactor.state.t_outlet + 50 q3 = (self.reactor.state.power_mwth * 1e6 / (len(self.reactor.assemblies) * 264 * 2.0) / (np.pi * self.R_PELLET ** 2)) return T_surface + (q3 / (4 * self.K_FUEL)) * (self.R_PELLET**2 - r_arr**2)
STEP 3 — ์ค์ฑ์ ๋ฌผ๋ฆฌ ํ๋ฌ๊ทธ์ธ (4์ธ์ ๊ณต์ + ์ฐ์)
TerraPower ARMI์ ์ค์ฑ์ ๋ฌผ๋ฆฌ ์ปค๋์ 4์ธ์ ๊ณต์์ผ๋ก ๊ทผ์ฌ. k-eff ๊ณ์ฐ ๋ฐ Bateman ๋ฐฉ์ ์ ๊ธฐ๋ฐ ํต์ฐ๋ฃ ์์ง ๋ชจ๋ธ๋ง.
python — neutronics_plugin.py (ARMI ์ค์ฑ์ ๋ฌผ๋ฆฌ ๋ชจ๋ฐฉ)
import numpy as np from scipy.integrate import odeint class NeutronicsPlugin: """ TerraPower ARMI ์ค์ฑ์ ๋ฌผ๋ฆฌ ํ๋ฌ๊ทธ์ธ ๋ชจ๋ฐฉ ─ 4์ธ์ ๊ณต์: k∞ = ฮท × ฮต × p × f ─ k_eff = k∞ / (1 + M²·B²) with migration area M² ─ Bateman ODE: ํต์ฐ๋ฃ ์์ง (์ฐ์๋ → k_eff ๊ฐ์) """ def __init__(self): self.reactor = None def four_factor(self, enrichment: float, burnup: float, cr_fraction: float) -> float: """ 4์ธ์ ๊ณต์์ผ๋ก k_eff ๊ณ์ฐ enrichment : U-235 ๋์ถ๋ (%) burnup : ์ฐ์๋ (MWd/kgU) cr_fraction: ์ ์ด๋ด ์ฝ์ ๋น์จ (0~1) """ # ฮท: U-235 ํต๋ถ์ด ์ค์ฑ์ ์ (๋์ถ๋ ์์กด) eta = 2.065 * (1 - np.exp(-enrichment / 3.0)) # ฮต: ๊ณ ์ ํต๋ถ์ด ์ธ์ (์์ ๊ทผ์ฌ) epsilon = 1.048 # p: ๊ณต๋ช ํ์ถ ํ๋ฅ (์ฐ์๋ ์ฆ๊ฐ ์ ๊ฐ์) p = 0.82 * np.exp(-burnup / 80.0) # f: ์ด์ค์ฑ์ ์ด์ฉ๋ฅ (๋์ถ๋ ์ฆ๊ฐ → ์ฆ๊ฐ) f = 0.71 + 0.018 * enrichment k_inf = eta * epsilon * p * f # ๋ฌดํ ์ฆ๋ฐฐ๊ณ์ # ๋น๋์ค ํ๋ฅ (์ํตํ ์ฝ์ด, M² ≈ 50 cm²) M_sq = 50.0 # cm² B_sq = (2.405 / 150.0) ** 2 + (np.pi / 240.0) ** 2 # cm⁻² nonleak = 1.0 / (1 + M_sq * B_sq * 1e4) # ์ ์ด๋ด ์์ ๋ฐ์๋ (CR ์ฝ์ ) rho_cr = -0.025 * cr_fraction k_eff = k_inf * (1 - abs(rho_cr)) return max(k_eff, 0.0) def execute(self): """Hub์์ ํธ์ถ๋๋ ์คํ ์ง์ ์ """ r = self.reactor avg_enr = r.avg_enrichment() avg_bu = np.mean([a.burnup for a in r.assemblies]) cr_frac = getattr(r, 'cr_fraction', 0.3) r.state.keff = self.four_factor(avg_enr, avg_bu, cr_frac) # ์ด์ถ๋ ฅ์ k_eff์ ์๊ณ ์ํ์ ๋ฐ๋ผ ์กฐ์ if r.state.keff > 1.0: r.state.power_mwth = 77.0 * r.state.keff else: r.state.power_mwth = 77.0 * r.state.keff * 0.95 def burnup_step(self, dt_efpd: float = 1.0): """์ฐ์๋ ์ฆ๊ฐ (Bateman ๋ฐฉ์ ์ ๋จ์ํ)""" flux = self.reactor.state.power_mwth / (77.0 * 37) for asm in self.reactor.assemblies: asm.burnup += flux * dt_efpd * 0.95
STEP 4 — ํ๋ผ๋ฏธํฐ ์ค์ ์๋ํ (ARMI ์ต์ ํ)
Python multiprocessing์ผ๋ก 1000+ ์ผ์ด์ค ๋ณ๋ ฌ ์คํ. YAML ์
๋ ฅ → ๋ชจ๋ธ ์์ฑ → ์๋ฎฌ → CSV/PNG ์ถ๋ ฅ ์์ ์๋ํ.
python — param_sweep.py (ARMI ํ๋ผ๋ฏธํฐ ์ค์ ์๋ํ)
import multiprocessing as mp import numpy as np import pandas as pd import matplotlib.pyplot as plt from reactor_core import Reactor from thermal_plugin import ThermalHydraulicsPlugin from neutronics_plugin import NeutronicsPlugin def run_case(params: dict) -> dict: """๋จ์ผ ์ผ์ด์ค ์คํ (multiprocessing worker)""" r = Reactor(geom='hex') r.state.flow_rate = params['flow_rate'] r.cr_fraction = params['cr_fraction'] for asm in r.assemblies: asm.enrichment = params['enrichment'] neut = NeutronicsPlugin() th = ThermalHydraulicsPlugin() r.register_plugin('neutronics', neut) r.register_plugin('thermal', th) state = r.run() return { 'enrichment' : params['enrichment'], 'cr_fraction' : params['cr_fraction'], 'flow_rate' : params['flow_rate'], 'keff' : round(state.keff, 4), 'power_mwth' : round(state.power_mwth, 2), 't_outlet' : round(state.t_outlet, 1), 'dnbr_min' : round(state.dnbr_min, 3), 'safe' : state.dnbr_min >= 1.3 and state.t_outlet < 325, } def parameter_sweep(n_cases: int = 1000) -> pd.DataFrame: """ ARMI ์คํ์ผ 1000+ ์ผ์ด์ค ๋ณ๋ ฌ ํ๋ผ๋ฏธํฐ ์ค์ ─ ๋์ถ๋, ์ ์ด๋ด ์ฝ์ , ์ ๋ 3D ๊ฒฉ์ ์ค์ """ enrichments = np.linspace(2.0, 10.0, 10) cr_fracs = np.linspace(0.0, 0.8, 10) flows = np.linspace(400, 700, 10) param_list = [ {'enrichment': e, 'cr_fraction': c, 'flow_rate': f} for e in enrichments for c in cr_fracs for f in flows ] # multiprocessing ๋ณ๋ ฌ ์คํ with mp.Pool(processes=mp.cpu_count()) as pool: results = pool.map(run_case, param_list[:n_cases]) df = pd.DataFrame(results) df.to_csv('smr_sweep_results.csv', index=False) plot_sweep_results(df) return df def plot_sweep_results(df: pd.DataFrame): """NuScale ์คํ์ผ ํ๋ผ๋ฏธํฐ ์ค์ ์๊ฐํ""" fig, axes = plt.subplots(1, 3, figsize=(15, 5)) fig.patch.set_facecolor('#0a0e1a') for ax in axes: ax.set_facecolor('#0f1525') axes[0].scatter(df['enrichment'], df['keff'], c=df['cr_fraction'], cmap='viridis', s=4, alpha=0.7) axes[0].axhline(1.0, color='#ef4444', linestyle='--', lw=1) axes[0].set_xlabel('Enrichment (%)', color='#94a3b8') axes[0].set_ylabel('k-eff', color='#94a3b8') axes[1].scatter(df['power_mwth'], df['dnbr_min'], c=df['safe'].astype(int), cmap='RdYlGn', s=4, alpha=0.7) axes[1].axhline(1.3, color='#ef4444', linestyle='--', lw=1) axes[2].hist(df['t_outlet'], bins=40, color='#3b82f6', alpha=0.8) axes[2].set_xlabel('T_outlet (°C)', color='#94a3b8') plt.tight_layout() plt.savefig('smr_sweep.png', dpi=150, bbox_inches='tight') # ── ์คํ ์ง์ ์ ── if __name__ == '__main__': df = parameter_sweep(n_cases=1000) print(f"์์ ์ผ์ด์ค: {df['safe'].sum()} / {len(df)} ({df['safe'].mean():.1%})")
Hub-and-Spoke ํ๋ฌ๊ทธ์ธ ์์คํ
(ARMI ์ํคํ
์ฒ)
๋ชจ๋์ ์ ํ ํ์ฑํํ์ฌ ์ปค์คํ
์๋ฎฌ๋ ์ด์
ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ
TerraPower ARMI Hub-and-Spoke: ์ค์ Reactor ๊ฐ์ฒด(Hub)์ ํ๋ฌ๊ทธ์ธ(Spoke)์ ๋์ ์ผ๋ก ๋ฑ๋กํฉ๋๋ค.
๊ฐ ํ๋ฌ๊ทธ์ธ์ ๋
๋ฆฝ์ ์ผ๋ก ์คํ๋๋ฉฐ Reactor ์ํ๋ฅผ ๊ณต์ ํฉ๋๋ค.
NeutronicsPlugin
4์ธ์ ๊ณต์ k-eff · Bateman ์ฐ์ · ๋ฐ์๋ ๊ณ์
ThermalPlugin
Dittus-Boelter ์ด์ ๋ฌ · DNBR · ์จ๋ ๋ถํฌ
SafetyPlugin
NRC 10 CFR 50 · LOCA · RIA ์ฌ๊ณ ๋ถ์
BurnupPlugin
ORIGEN ์คํ์ผ ํต์ข
๋ณํ · ์๋ฅ ๋ฐ์ด
EconomicsPlugin
LCOE ๊ณ์ฐ · ์ฐ๋ฃ ๋น์ฉ ์ต์ ํ · ROI ๋ถ์
VisualizPlugin
Matplotlib ์ด์ง๋ · ์ค์ฑ์์ ๋ถํฌ · ์ฝ์ด ๋งต
ํ์ฑ ํ๋ฌ๊ทธ์ธ ํ์ดํ๋ผ์ธ (์คํ ์์):
YAML ์
๋ ฅ ํ์ผ ์๋ํฐ (์๋ํ ์ํฌํ๋ก)
smr_config.yaml — ์์๋ก ์
๋ ฅ ํ์ผ
python — main.py (ARMI ์คํ์ผ Hub ํ๋ฌ๊ทธ์ธ ๋ฑ๋ก)
from reactor_core import Reactor from neutronics_plugin import NeutronicsPlugin from thermal_plugin import ThermalHydraulicsPlugin from safety_plugin import SafetyPlugin import yaml # ── 1. YAML ์ ๋ ฅ์ผ๋ก Reactor(Hub) ์์ฑ ── reactor = Reactor(geom='hex', config_path='smr_config.yaml') # ── 2. ํ๋ฌ๊ทธ์ธ(Spoke) ๋ฑ๋ก (ARMI Hub-and-Spoke) ── reactor.register_plugin('neutronics', NeutronicsPlugin()) reactor.register_plugin('thermal', ThermalHydraulicsPlugin()) reactor.register_plugin('safety', SafetyPlugin()) # ── 3. ์๋ฎฌ๋ ์ด์ ์คํ ── state = reactor.run() # ── 4. ๊ฒฐ๊ณผ ์ถ๋ ฅ ── print(f"k-eff : {state.keff:.4f}") print(f"์ด์ถ๋ ฅ : {state.power_mwth:.1f} MWth") print(f"T_outlet : {state.t_outlet:.1f} °C") print(f"DNBR_min : {state.dnbr_min:.3f}") print(f"์์ ํ์ : {'OK ✅' if state.dnbr_min >= 1.3 else 'FAIL ❌'}") # ── 5. ํ๋ผ๋ฏธํฐ ์ค์ (1000 ์ผ์ด์ค ๋ณ๋ ฌ) ── from param_sweep import parameter_sweep df = parameter_sweep(n_cases=1000) print(df.describe())
์์ ๋ถ์ ๋ชจ๋ (NRC 10 CFR 50 ๊ธฐ์ค)
NuScale NRC ์ธ์ฆ ๊ธฐ์ค ์ ์ฉ ์๋ ์์ ๊ฒ์ฆ ์ฒดํฌ๋ฆฌ์คํธ
๊ต์ก·์ฐ๊ตฌ ๋ชฉ์ ์๋ฎฌ๋ ์ดํฐ: ๋ณธ ํฌํผ ํ๋ก๊ทธ๋จ์ ๊ฐ๋
ํ์ต ๋ฐ ์ฝ๋ ์ํคํ
์ฒ ์ดํด๋ฅผ ์ํ ๋๊ตฌ์
๋๋ค.
์ค์ ์์๋ ฅ ์์ ๋ถ์์ NRC/IAEA ์ธ์ฆ ์ฝ๋(RELAP5, TRACE, PARCS)๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์์ ๊ธฐ์ค ์ฒดํฌ๋ฆฌ์คํธ
-
—① DNBR ≥ 1.30 (๋น๋น๋ฑ ํ๊ดด ์ดํ๋น)์ฐ๋ฃ๋ด ๋นํต๋น๋ฑ ํ๊ดด ๋ฐฉ์ง. NRC ์ค๊ณ ๊ธฐ์ค ์ดํ๋น ์ต์๊ฐ
-
—② T_fuel_max ≤ 1482°C (UO₂ ์ฉ์ต์ )ํต์ฐ๋ฃ ์ต๋ ์จ๋๊ฐ UO₂ ์ฉ์ต ์จ๋ ์ดํ ์ ์ง
-
—③ T_clad_max ≤ 1200°C (ํผ๋ณต๊ด ์ค๊ณ ํ๊ณ)์ง๋ฅด์ฝ๋ ํผ๋ณต๊ด ECCS ๋๊ฐ์ ๋ถ์ฌ ๊ธฐ์ค
-
—④ T_outlet ≤ 325°C (๋๊ฐ์ฌ ํฌํ ๋ง์ง)1์ฐจ๊ณ ๋๊ฐ์ฌ ํฌํ ์จ๋(@ 12.76 MPa: 331°C) ๋๋น ๋ง์ง ํ๋ณด
-
—⑤ k-eff ≤ 0.95 (๋๊ฐ์ ๋ฐฐ์ถ ์ํ)LOCA(๋๊ฐ์ ์์ค ์ฌ๊ณ ) ์ ์๊ณ ๋ฏธ๋ฌ ๋ณด์ฅ
-
—⑥ ๋ฐ์๋ ํฌ์ ์จ ≤ 5 pcm/s (RIA)๋ฐ์๋ ์ฝ์ ์ฌ๊ณ ์ ์ด ๋ฐ์๋ ์ฝ์ ์๋ ์ ํ
-
—⑦ ์๋ ฅ ๋ง์ง ≥ 10% (1์ฐจ๊ณ ์ค๊ณ์๋ ฅ)์ด์ ์ค 1์ฐจ๊ณ ์๋ ฅ์ด ์ค๊ณ ์๋ ฅ ๋๋น 10% ์ด์ ๋ง์ง ์ ์ง
-
—⑧ ๋น์ ๋๊ฐ ์ฉ๋ ≥ ์์ฌ ์ด์ถ๋ ฅ์๋ ๋๊ฐ ์์คํ (NuScale DHRS)์ ์์ฌ ๋ฐ์ด ์ ๊ฑฐ ๋ฅ๋ ฅ
SafetyPlugin ๊ตฌํ ์ฝ๋
python — safety_plugin.py
class SafetyPlugin: """ NRC 10 CFR 50 ์์ ๊ธฐ์ค ์๋ ๊ฒ์ฆ ํ๋ฌ๊ทธ์ธ ─ DNBR, ์ฐ๋ฃ ์จ๋, ๋๊ฐ์ฌ ์จ๋, ์๋ ฅ ๋ง์ง ─ LOCA / RIA ์ฌ๊ณ ์๋๋ฆฌ์ค ์ฒดํฌ """ # NRC ์ค๊ณ ํ๊ณ (NuScale SMR ๊ธฐ์ค) DNBR_LIMIT = 1.30 # ๋น๋น๋ฑ ํ๊ดด ์ดํ๋น T_FUEL_MAX = 1482.0 # UO2 ์ฉ์ต ์จ๋ (°C) T_CLAD_MAX = 1200.0 # ECCS ํผ๋ณต๊ด ํ๊ณ (°C) T_COOLANT_MAX = 325.0 # ๋๊ฐ์ฌ ์ต๋ ์จ๋ (°C) P_DESIGN = 16.0 # 1์ฐจ๊ณ ์ค๊ณ ์๋ ฅ (MPa) P_MARGIN_MIN = 0.10 # ์ต์ ์๋ ฅ ๋ง์ง (10%) def __init__(self): self.reactor = None self.results: dict = {} self.passed: bool = False def execute(self): """์ ์ฒด ์์ ๊ธฐ์ค ์ผ๊ด ๊ฒ์ฆ""" s = self.reactor.state self.results = { 'dnbr_ok' : s.dnbr_min >= self.DNBR_LIMIT, 't_cool_ok' : s.t_outlet <= self.T_COOLANT_MAX, 'press_ok' : (self.P_DESIGN - s.pressure) / self.P_DESIGN >= self.P_MARGIN_MIN, 'keff_ok' : s.keff <= 1.10, # ์ด์ ๋ฒ์ } self.passed = all(self.results.values()) def loca_analysis(self) -> dict: """๋๊ฐ์ ์์ค ์ฌ๊ณ (LOCA) ๊ฐ์ด ๋ถ์""" import numpy as np s = self.reactor.state # ์์ฌ ๋ฐ์ด (ANS-5.1 ๋ถ๊ดด์ด ๋ชจ๋ธ) t_arr = np.logspace(0, 7, 100) # 1s ~ 10^7 s decay = s.power_mwth * 0.066 * (t_arr ** -0.2) # MW # ํผ๋ ๋๊ฐ ๊ณํต (NuScale DHRS) ์ฉ๋ dhrs_capacity = 2.0 # MW (NuScale ์ค๊ณ๊ฐ) return { 'time_s' : t_arr.tolist(), 'decay_heat_mw': decay.tolist(), 'dhrs_mw' : dhrs_capacity, 'safety_margin': (decay < dhrs_capacity).tolist(), } def report(self) -> str: lines = ["=== SMR Safety Analysis Report ==="] for k, v in self.results.items(): status = "✅ PASS" if v else "❌ FAIL" lines.append(f" {k:15s}: {status}") lines.append(f"\nOverall: {'✅ SAFE' if self.passed else '❌ UNSAFE'}") return "\n".join(lines)
์๋ํ ์ํฌํ๋ก (YAML → ์๋ฎฌ → ์ถ๋ ฅ)
TerraPower ARMI ์คํ์ผ End-to-End ํ์ดํ๋ผ์ธ
YAML ์
๋ ฅ
์ค๊ณ ํ๋ผ๋ฏธํฐ
→
๋ชจ๋ธ ์์ฑ
Reactor Hub
→
์ค์ฑ์ ํด์
k-eff ๊ณ์ฐ
→
์ด์ ์ฒด ํด์
DNBR · T
→
์์ ๊ฒ์ฆ
NRC ๊ธฐ์ค
→
๊ฒฐ๊ณผ ์ถ๋ ฅ
CSV · PNG
[──────]์คํ
์ ํด๋ฆญํ๊ฑฐ๋ ์ ์ฒด ์ํฌํ๋ก ์คํ ๋ฒํผ์ ๋๋ฅด์ธ์.
์ํ์ค ๋ค์ด์ด๊ทธ๋จ — Hub-and-Spoke ์คํ ํ๋ฆ
ORNL Modelica ๋ฒค์น๋งํน — ๊ฒ์ฆ ๊ธฐ์ค๊ฐ
Oak Ridge National Laboratory SMR ๊ฒ์ฆ ๋ฐ์ดํฐ์ ๋น๊ต
