feat: add automated documentation building workflow and Google-style docstrings
All checks were successful
Deploy Documentation / build-and-deploy (push) Successful in 3m17s
All checks were successful
Deploy Documentation / build-and-deploy (push) Successful in 3m17s
This commit is contained in:
parent
fb2887e068
commit
43499f58e3
6 changed files with 673 additions and 15 deletions
93
.forgejo/workflows/deploy-docs.yml
Normal file
93
.forgejo/workflows/deploy-docs.yml
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
name: Deploy Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: docker # 기본 매핑된 node:20-bookworm 이미지에서 작동
|
||||||
|
steps:
|
||||||
|
- name: 저장소 체크아웃
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# 1. 기존 APT 캐시 복원 (가장 최근 상태 불러오기)
|
||||||
|
- name: Restore APT Cache
|
||||||
|
id: restore-apt
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: /var/cache/apt/archives
|
||||||
|
key: linux-apt-temp
|
||||||
|
restore-keys: |
|
||||||
|
linux-apt-
|
||||||
|
|
||||||
|
# 2. 시스템 패키지 설치 및 구버전 찌꺼기 청소
|
||||||
|
- name: Install System Packages
|
||||||
|
run: |
|
||||||
|
if [ -f /etc/apt/apt.conf.d/docker-clean ]; then
|
||||||
|
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache
|
||||||
|
rm -f /etc/apt/apt.conf.d/docker-clean
|
||||||
|
fi
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y python3 python3-pip python3-venv graphviz
|
||||||
|
apt-get autoclean -y
|
||||||
|
rm -f /var/cache/apt/archives/partial/*
|
||||||
|
|
||||||
|
# 3. .deb 파일명 목록 기반 고유 해시(Hash) 계산
|
||||||
|
- name: Calculate APT Cache Hash
|
||||||
|
id: apt-hash
|
||||||
|
run: |
|
||||||
|
DEB_HASH=$(find /var/cache/apt/archives -name "*.deb" -type f -printf "%f\n" | sort | md5sum | awk '{print $1}')
|
||||||
|
echo "hash=$DEB_HASH" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# 4. 계산된 해시값을 Key로 캐시 저장
|
||||||
|
- name: Save APT Cache
|
||||||
|
if: steps.restore-apt.outputs.cache-matched-key != format('linux-apt-{0}', steps.apt-hash.outputs.hash)
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: /var/cache/apt/archives
|
||||||
|
key: linux-apt-${{ steps.apt-hash.outputs.hash }}
|
||||||
|
|
||||||
|
- name: Cache Python Virtual Environment
|
||||||
|
id: cache-venv
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: venv
|
||||||
|
key: linux-venv-${{ hashFiles('requirements.txt', 'requirements-docs.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
linux-venv-
|
||||||
|
|
||||||
|
- name: Install Python Requirements
|
||||||
|
if: steps.cache-venv.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
python3 -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
# Install simulation dependencies (needed for mkdocstrings dynamic import) and doc tools
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install -r requirements-docs.txt
|
||||||
|
|
||||||
|
- name: Compile Docs
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
python3 build_docs.py
|
||||||
|
|
||||||
|
- name: gh-pages 브랜치로 site/ 배포
|
||||||
|
env:
|
||||||
|
DEPLOY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
ACTOR: ${{ github.actor }}
|
||||||
|
run: |
|
||||||
|
cd site
|
||||||
|
git init -b main
|
||||||
|
git config user.name "Forgejo Actions Bot"
|
||||||
|
git config user.email "actions@1gnis-git.duckdns.org"
|
||||||
|
git add .
|
||||||
|
git commit -m "Auto-deploy Docs (${GITHUB_SHA::8})"
|
||||||
|
SERVER_URL="${{ github.server_url }}"
|
||||||
|
HOST_ADDR="${SERVER_URL#*//}"
|
||||||
|
PROTO="${SERVER_URL%%//*}"
|
||||||
|
git push --force \
|
||||||
|
"${PROTO}//${ACTOR}:${DEPLOY_TOKEN}@${HOST_ADDR}/${REPO}.git" \
|
||||||
|
main:gh-pages
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,5 +1,7 @@
|
||||||
# Jupyter Notebook checkpoints
|
# Jupyter Notebook checkpoints
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
|
site/
|
||||||
|
docs/
|
||||||
|
|
||||||
# Virtual Environment
|
# Virtual Environment
|
||||||
.venv/
|
.venv/
|
||||||
|
|
|
||||||
422
Battery.py
422
Battery.py
|
|
@ -1,3 +1,10 @@
|
||||||
|
"""Coke Oven Battery Simulation Module.
|
||||||
|
|
||||||
|
This module models the thermal behavior, heat transfer, and scheduling operations of
|
||||||
|
a coke oven battery. It implements systems of flues (combustion chambers), refractory
|
||||||
|
brick walls (solving 1D heat equations), and oven chambers with loaded coal charges.
|
||||||
|
"""
|
||||||
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -19,7 +26,35 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class CombustionChamber:
|
class CombustionChamber:
|
||||||
|
"""Represents a combustion chamber in the coke oven battery.
|
||||||
|
|
||||||
|
This class models the steady-state thermal energy balance of combustion gases
|
||||||
|
flowing through the heating flues (chambers) adjacent to the ovens.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
mdot (float): Mass flow rate of fuel-air mixture (kg/s).
|
||||||
|
gas (cantera.Solution): Cantera gas object.
|
||||||
|
eq_state (tuple): Thermodynamic state (T, P, X) of burned gas.
|
||||||
|
T0 (float): Adiabatic flame temperature (K).
|
||||||
|
P0 (float): Operating pressure (Pa).
|
||||||
|
X0 (dict/array): Fuel-air mole fractions.
|
||||||
|
h0 (float): Inlet enthalpy of the gas (J/kg).
|
||||||
|
hA (float): Heat transfer coefficient times area (W/K).
|
||||||
|
T1 (float): Outlet gas temperature (K).
|
||||||
|
Twall0 (float): Temperature of the lower wall (K).
|
||||||
|
Twall1 (float): Temperature of the upper wall (K).
|
||||||
|
Area (float): Oven cross section area (m^2).
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, mdot, ct_object, burned_state, hA=700):
|
def __init__(self, mdot, ct_object, burned_state, hA=700):
|
||||||
|
"""Initializes the CombustionChamber.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mdot (float): Mass flow rate (kg/s).
|
||||||
|
ct_object (cantera.Solution): Instantiated Cantera Solution object.
|
||||||
|
burned_state (tuple): State variables (T, P, X) representing burned gas.
|
||||||
|
hA (float, optional): Heat transfer coefficient * area. Defaults to 700.
|
||||||
|
"""
|
||||||
self.mdot = mdot # kg/s
|
self.mdot = mdot # kg/s
|
||||||
self.gas = ct_object # gas object
|
self.gas = ct_object # gas object
|
||||||
self.eq_state = burned_state # HP equilibrium state
|
self.eq_state = burned_state # HP equilibrium state
|
||||||
|
|
@ -36,25 +71,47 @@ class CombustionChamber:
|
||||||
self.Area = 6.7 * 16.7
|
self.Area = 6.7 * 16.7
|
||||||
|
|
||||||
def update_mdot(self, mdot_new):
|
def update_mdot(self, mdot_new):
|
||||||
|
"""Updates the mass flow rate if a new value is provided.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mdot_new (float): The new mass flow rate (kg/s).
|
||||||
|
"""
|
||||||
if mdot_new:
|
if mdot_new:
|
||||||
self.mdot = mdot_new
|
self.mdot = mdot_new
|
||||||
|
|
||||||
def update_Twall(self, Twall0=None, Twall1=None):
|
def update_Twall(self, Twall0=None, Twall1=None):
|
||||||
|
"""Updates the boundary wall temperatures.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Twall0 (float, optional): Lower wall temperature (K).
|
||||||
|
Twall1 (float, optional): Upper wall temperature (K).
|
||||||
|
"""
|
||||||
if Twall0:
|
if Twall0:
|
||||||
self.Twall0 = Twall0
|
self.Twall0 = Twall0
|
||||||
if Twall1:
|
if Twall1:
|
||||||
self.Twall1 = Twall1
|
self.Twall1 = Twall1
|
||||||
|
|
||||||
def energy_balance_equation(self, Tout):
|
def energy_balance_equation(self, Tout):
|
||||||
|
"""Calculates the residual energy imbalance for root-finding.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Tout (float): Guessed outlet gas temperature (K).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Energy balance residual (W).
|
||||||
|
"""
|
||||||
self.gas.TP = Tout, None
|
self.gas.TP = Tout, None
|
||||||
h1 = self.gas.enthalpy_mass
|
h1 = self.gas.enthalpy_mass
|
||||||
q1, q2 = self.heat(Tout)
|
q1, q2 = self.heat(Tout)
|
||||||
|
|
||||||
return (self.mdot * (self.h0 - h1) - q1 - q2)
|
return (self.mdot * (self.h0 - h1) - q1 - q2)
|
||||||
|
|
||||||
def solve(self, ):
|
def solve(self):
|
||||||
""" Iteratively solve for outlet temperature that balance with heat loss to walls """
|
"""Iteratively solves for the outlet temperature balancing heat loss to walls.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Resolved outlet temperature (K).
|
||||||
|
"""
|
||||||
meanTwall = (self.Twall0 + self.Twall1) / 2
|
meanTwall = (self.Twall0 + self.Twall1) / 2
|
||||||
T_low = meanTwall - (self.T0 - meanTwall)
|
T_low = meanTwall - (self.T0 - meanTwall)
|
||||||
try:
|
try:
|
||||||
|
|
@ -63,11 +120,19 @@ class CombustionChamber:
|
||||||
self.T1 = f_found.root
|
self.T1 = f_found.root
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.T1 = meanTwall
|
self.T1 = meanTwall
|
||||||
|
return meanTwall
|
||||||
|
|
||||||
return f_found.root
|
return f_found.root
|
||||||
|
|
||||||
def heat(self, Tout=None):
|
def heat(self, Tout=None):
|
||||||
''' Heat(W) to walls '''
|
"""Calculates the heat transfer rate to the walls.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Tout (float, optional): Outlet gas temperature (K). If None, uses T1.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: Heat transfer rates (q0, q1) to the lower and upper walls (W).
|
||||||
|
"""
|
||||||
if Tout is None:
|
if Tout is None:
|
||||||
Tout = self.T1
|
Tout = self.T1
|
||||||
Tgas = (self.T0 + Tout) / 2
|
Tgas = (self.T0 + Tout) / 2
|
||||||
|
|
@ -76,17 +141,41 @@ class CombustionChamber:
|
||||||
|
|
||||||
|
|
||||||
class CokeCharge:
|
class CokeCharge:
|
||||||
|
"""Represents a single coal/coke charge inside an oven.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
t_charge (float): Simulation timestamp when coal was charged (hours).
|
||||||
|
t_push (float or None): Simulation timestamp when coke was pushed (hours).
|
||||||
|
idx_oven (int): Index of the oven this charge belongs to.
|
||||||
|
Q (float): Total heat absorbed by this charge (J).
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, t_charge, idx_oven):
|
def __init__(self, t_charge, idx_oven):
|
||||||
|
"""Initializes CokeCharge.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t_charge (float): Time of charging.
|
||||||
|
idx_oven (int): Target oven index.
|
||||||
|
"""
|
||||||
self.t_charge = t_charge
|
self.t_charge = t_charge
|
||||||
self.t_push = None
|
self.t_push = None
|
||||||
self.idx_oven = idx_oven
|
self.idx_oven = idx_oven
|
||||||
self.Q = 0
|
self.Q = 0
|
||||||
|
|
||||||
def bake(self, dQ):
|
def bake(self, dQ):
|
||||||
|
"""Applies a increment of thermal energy to the charge.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dQ (float): Energy increment (J).
|
||||||
|
"""
|
||||||
self.Q += dQ
|
self.Q += dQ
|
||||||
|
|
||||||
def end_baking(self, t):
|
def end_baking(self, t):
|
||||||
|
"""Finalizes the charge lifecycle at push time.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t (float): Pushing time (hours).
|
||||||
|
"""
|
||||||
self.t_push = t
|
self.t_push = t
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -101,10 +190,32 @@ wall_area = 6.7 * 16.7 # m^2 , Oven cross section area
|
||||||
|
|
||||||
|
|
||||||
class TInternal:
|
class TInternal:
|
||||||
|
"""Helper wrapper around numerical internal temperature data for a wall.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
data (numpy.ndarray): Spatial temperature profile within the brick wall.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
|
"""Initializes TInternal wrapper.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (array-like): Numerical temperature array.
|
||||||
|
"""
|
||||||
self.data = np.array(data, dtype=np.float64)
|
self.data = np.array(data, dtype=np.float64)
|
||||||
|
|
||||||
def get_boundary_values(self, axis=0, upper=False, bc=None):
|
def get_boundary_values(self, axis=0, upper=False, bc=None):
|
||||||
|
"""Extracts boundary temperature values based on boundary conditions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
axis (int, optional): Spatial axis (default is 0).
|
||||||
|
upper (bool, optional): If True, gets right-hand boundary (oven-side),
|
||||||
|
otherwise left-hand boundary (chamber-side). Defaults to False.
|
||||||
|
bc (list, optional): Boundary conditions list.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: The computed boundary temperature value (K).
|
||||||
|
"""
|
||||||
dx = brick_thickness / n_grid_brick
|
dx = brick_thickness / n_grid_brick
|
||||||
if not upper:
|
if not upper:
|
||||||
g_L = 0.0
|
g_L = 0.0
|
||||||
|
|
@ -127,7 +238,24 @@ class TInternal:
|
||||||
|
|
||||||
|
|
||||||
class CokeOvenBrickHeatEqnBase:
|
class CokeOvenBrickHeatEqnBase:
|
||||||
|
"""Base class defining physical parameters for the brick wall heat equation.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
bc (list): Boundary conditions.
|
||||||
|
rho (float): Density of refractory brick (kg/m3).
|
||||||
|
kCoef0 (float): Constant coefficient of thermal conductivity (W/m/K).
|
||||||
|
kCoef1 (float): Temperature coefficient of thermal conductivity (W/m/K2).
|
||||||
|
cpCoef0 (float): Constant coefficient of specific heat (J/kg/K).
|
||||||
|
cpCoef1 (float): Temperature coefficient of specific heat (J/kg/K2).
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, bc="auto_periodic_neumann"):
|
def __init__(self, bc="auto_periodic_neumann"):
|
||||||
|
"""Initializes CokeOvenBrickHeatEqnBase.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bc (str or list, optional): Boundary conditions description.
|
||||||
|
Defaults to "auto_periodic_neumann".
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -144,12 +272,34 @@ class CokeOvenBrickHeatEqnBase:
|
||||||
self.cpCoef1 = 251.2e-3 # J / kg / K2
|
self.cpCoef1 = 251.2e-3 # J / kg / K2
|
||||||
|
|
||||||
def k(self, T):
|
def k(self, T):
|
||||||
|
"""Calculates temperature-dependent thermal conductivity.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
T (float or numpy.ndarray): Temperature (K).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float or numpy.ndarray: Thermal conductivity (W/m/K).
|
||||||
|
"""
|
||||||
return T * self.kCoef1 + self.kCoef0
|
return T * self.kCoef1 + self.kCoef0
|
||||||
|
|
||||||
def cp(self, T):
|
def cp(self, T):
|
||||||
|
"""Calculates temperature-dependent specific heat capacity.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
T (float or numpy.ndarray): Temperature (K).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float or numpy.ndarray: Specific heat capacity (J/kg/K).
|
||||||
|
"""
|
||||||
return T * self.cpCoef1 + self.cpCoef0
|
return T * self.cpCoef1 + self.cpCoef0
|
||||||
|
|
||||||
def update_bc(self, gradT_chamber=None, T_oven=None):
|
def update_bc(self, gradT_chamber=None, T_oven=None):
|
||||||
|
"""Updates boundary condition parameters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gradT_chamber (float, optional): Temperature gradient at chamber side.
|
||||||
|
T_oven (float, optional): Oven side boundary temperature (K).
|
||||||
|
"""
|
||||||
if gradT_chamber is not None:
|
if gradT_chamber is not None:
|
||||||
self.bc[0] = {"derivative": gradT_chamber}
|
self.bc[0] = {"derivative": gradT_chamber}
|
||||||
if T_oven is not None:
|
if T_oven is not None:
|
||||||
|
|
@ -158,7 +308,18 @@ class CokeOvenBrickHeatEqnBase:
|
||||||
|
|
||||||
if pde is not None:
|
if pde is not None:
|
||||||
class CokeOvenBrickHeatEqn(CokeOvenBrickHeatEqnBase, pde.PDEBase):
|
class CokeOvenBrickHeatEqn(CokeOvenBrickHeatEqnBase, pde.PDEBase):
|
||||||
|
"""1D Heat Equation model for coke oven brick walls, leveraging py-pde package."""
|
||||||
|
|
||||||
def evolution_rate(self, state, t=0):
|
def evolution_rate(self, state, t=0):
|
||||||
|
"""Calculates time derivative of temperature field for solvers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state (pde.ScalarField): Current temperature field.
|
||||||
|
t (float): Current simulation time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pde.ScalarField: Evolution rate dT/dt.
|
||||||
|
"""
|
||||||
state_lap = state.laplace(bc=self.bc)
|
state_lap = state.laplace(bc=self.bc)
|
||||||
state_grad2 = state.gradient_squared(bc=self.bc)
|
state_grad2 = state.gradient_squared(bc=self.bc)
|
||||||
k = self.kCoef1 * state + self.kCoef0
|
k = self.kCoef1 * state + self.kCoef0
|
||||||
|
|
@ -167,11 +328,30 @@ if pde is not None:
|
||||||
return (state_grad_k_grad + k * state_lap) / cp / self.rho
|
return (state_grad_k_grad + k * state_lap) / cp / self.rho
|
||||||
else:
|
else:
|
||||||
class CokeOvenBrickHeatEqn(CokeOvenBrickHeatEqnBase):
|
class CokeOvenBrickHeatEqn(CokeOvenBrickHeatEqnBase):
|
||||||
|
"""Fallback heat equation model without py-pde dependencies."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RefractoryWall:
|
class RefractoryWall:
|
||||||
|
"""Simulates a refractory brick wall separating combustion chambers and ovens.
|
||||||
|
|
||||||
|
Solves the 1D transient heat conduction equation through the refractory wall
|
||||||
|
using either py-pde or a custom NumPy finite difference solver.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
T_oven (float): Temperature at the oven side (K).
|
||||||
|
T_chamber (float): Temperature at the combustion chamber side (K).
|
||||||
|
q_chamber (float): Heat flux from chamber.
|
||||||
|
T_internal (TInternal or pde.ScalarField): Internal temperature field.
|
||||||
|
eqn (CokeOvenBrickHeatEqn): Heat equation PDE model instance.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, T0):
|
def __init__(self, T0):
|
||||||
|
"""Initializes RefractoryWall.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
T0 (float): Initial uniform temperature (K).
|
||||||
|
"""
|
||||||
self.T_oven = T0
|
self.T_oven = T0
|
||||||
self.T_chamber = T0
|
self.T_chamber = T0
|
||||||
self.q_chamber = 0.
|
self.q_chamber = 0.
|
||||||
|
|
@ -183,6 +363,12 @@ class RefractoryWall:
|
||||||
bc=[{"derivative": 0}, {"value": self.T_oven}])
|
bc=[{"derivative": 0}, {"value": self.T_oven}])
|
||||||
|
|
||||||
def update_bc(self, Q=None, T_oven=None):
|
def update_bc(self, Q=None, T_oven=None):
|
||||||
|
"""Updates the wall boundary conditions based on energy flow.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Q (float, optional): Heat input rate (W).
|
||||||
|
T_oven (float, optional): Oven boundary temperature (K).
|
||||||
|
"""
|
||||||
k0 = self.eqn.k(self.T_chamber)
|
k0 = self.eqn.k(self.T_chamber)
|
||||||
if Q:
|
if Q:
|
||||||
gradT = Q / wall_area / k0
|
gradT = Q / wall_area / k0
|
||||||
|
|
@ -191,6 +377,11 @@ class RefractoryWall:
|
||||||
self.eqn.update_bc(gradT, T_oven)
|
self.eqn.update_bc(gradT, T_oven)
|
||||||
|
|
||||||
def solve(self, dt):
|
def solve(self, dt):
|
||||||
|
"""Solves the heat equation over time interval dt.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dt (float): Simulation time step (seconds).
|
||||||
|
"""
|
||||||
if USE_CUSTOM_SOLVER:
|
if USE_CUSTOM_SOLVER:
|
||||||
dt_internal = 30.0
|
dt_internal = 30.0
|
||||||
steps = int(round(dt / dt_internal))
|
steps = int(round(dt / dt_internal))
|
||||||
|
|
@ -241,7 +432,11 @@ class RefractoryWall:
|
||||||
axis=0, upper=False, bc=self.eqn.bc)
|
axis=0, upper=False, bc=self.eqn.bc)
|
||||||
|
|
||||||
def heat_to_oven(self):
|
def heat_to_oven(self):
|
||||||
""" NOT YET IMPLEMENTED """
|
"""Calculates heat transfer to the oven chamber.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Heat transfer (W). Not implemented yet.
|
||||||
|
"""
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -251,19 +446,37 @@ Twall_table[1] += 273.15
|
||||||
|
|
||||||
|
|
||||||
def Twall_model(x):
|
def Twall_model(x):
|
||||||
'''
|
"""Calculates the coke oven wall temperature based on elapsed time since charging.
|
||||||
Coke oven wall temperature vs time after charging
|
|
||||||
Temperature (K) vs Elapsed time (hour)
|
Args:
|
||||||
'''
|
x (float): Elapsed time (hours).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Oven wall temperature (K).
|
||||||
|
"""
|
||||||
return np.interp(x, Twall_table[0], Twall_table[1])
|
return np.interp(x, Twall_table[0], Twall_table[1])
|
||||||
|
|
||||||
|
|
||||||
class OvenChamber:
|
class OvenChamber:
|
||||||
|
"""Represents an individual oven chamber containing a coke charge.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
content (CokeCharge or None): The coke charge model inside the oven.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initializes OvenChamber."""
|
||||||
self.content = None
|
self.content = None
|
||||||
|
|
||||||
def get_charge_temperature(self, t):
|
def get_charge_temperature(self, t):
|
||||||
""" Return temperature of coal charge content at oven wall """
|
"""Gets the temperature of the coal charge content at the oven wall.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t (float): Simulation time (hours).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Charge surface temperature (K).
|
||||||
|
"""
|
||||||
if self.content:
|
if self.content:
|
||||||
elapsed_time = t - self.content.t_charge
|
elapsed_time = t - self.content.t_charge
|
||||||
else:
|
else:
|
||||||
|
|
@ -271,23 +484,72 @@ class OvenChamber:
|
||||||
return Twall_model(elapsed_time)
|
return Twall_model(elapsed_time)
|
||||||
|
|
||||||
def bake(self, q):
|
def bake(self, q):
|
||||||
""" Add transferred heat to coal charge content """
|
"""Applies baking heat to the coal charge.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
q (float): Heat energy applied (J).
|
||||||
|
"""
|
||||||
if self.content:
|
if self.content:
|
||||||
self.content.bake(q)
|
self.content.bake(q)
|
||||||
|
|
||||||
def charge(self, coal_charge):
|
def charge(self, coal_charge):
|
||||||
""" Update content with fresh coal is charged """
|
"""Charges fresh coal into the oven chamber.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
coal_charge (CokeCharge): The coal charge object to load.
|
||||||
|
"""
|
||||||
self.content = coal_charge
|
self.content = coal_charge
|
||||||
|
|
||||||
|
|
||||||
def wall_solve_wrapper(t_range, wall):
|
def wall_solve_wrapper(t_range, wall):
|
||||||
|
"""Worker function wrapper to solve wall heat equation in parallel.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t_range (float): Time range to solve (seconds).
|
||||||
|
wall (RefractoryWall): Wall object instance to solve.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (updated T_internal field, updated boundary T_chamber temperature)
|
||||||
|
"""
|
||||||
wall.solve(t_range)
|
wall.solve(t_range)
|
||||||
return wall.T_internal, wall.T_chamber
|
return wall.T_internal, wall.T_chamber
|
||||||
|
|
||||||
|
|
||||||
class Battery:
|
class Battery:
|
||||||
|
"""Represents a complete Coke Oven Battery.
|
||||||
|
|
||||||
|
A battery consists of a series of alternating combustion chambers, refractory
|
||||||
|
brick walls, and oven chambers, along with corresponding schedules for charging
|
||||||
|
and heating.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name (str): Battery name identifier.
|
||||||
|
size (int): Number of oven chambers.
|
||||||
|
heat_program (HeatSchedule): Operational heating program schedule.
|
||||||
|
charge_program (ChargeSchedule): Operational coal charging schedule.
|
||||||
|
t (float): Current simulation time (hours).
|
||||||
|
t_last (float): Timestamp of the last Push/Charge event (hours).
|
||||||
|
processing (list of CokeCharge): Currently active coke charges.
|
||||||
|
product (list of CokeCharge): Log of completed coke charges.
|
||||||
|
gas (cantera.Solution): Local Cantera Solution object.
|
||||||
|
T0 (float): Adiabatic flame temperature of incoming gas (K).
|
||||||
|
P0 (float): Gas operating pressure (Pa).
|
||||||
|
X0 (dict): Gas composition.
|
||||||
|
sequence_idx (int): Current sequence progress index.
|
||||||
|
wall_t_history (list): Recorded history of wall temperatures.
|
||||||
|
gas_t_history (list): Recorded history of chamber temperatures.
|
||||||
|
hv (float): Heating value of fuel-air mix (J/kg).
|
||||||
|
normal_heat (float): Baseline heat load (GJ/rev).
|
||||||
|
mdot0 (float): Baseline fuel mixture mass flow rate (kg/s).
|
||||||
|
chambers (list of CombustionChamber): Combustion flues.
|
||||||
|
ovens (list of OvenChamber): Oven chambers.
|
||||||
|
walls_0 (list of RefractoryWall): Lower refractory walls.
|
||||||
|
walls_1 (list of RefractoryWall): Upper refractory walls.
|
||||||
|
oven_idx_order (numpy.ndarray): Charging schedule oven sequence.
|
||||||
|
"""
|
||||||
|
|
||||||
def load_state(self):
|
def load_state(self):
|
||||||
|
"""Loads simulation state from binary history files."""
|
||||||
with open('gas.history', 'rb') as gas_history_file:
|
with open('gas.history', 'rb') as gas_history_file:
|
||||||
self.gas_t_history = pickle.load(gas_history_file)
|
self.gas_t_history = pickle.load(gas_history_file)
|
||||||
|
|
||||||
|
|
@ -301,6 +563,17 @@ class Battery:
|
||||||
self.processing = pickle.load(coke_state_file)
|
self.processing = pickle.load(coke_state_file)
|
||||||
|
|
||||||
def __init__(self, name, size, heat_program, charge_program, burned_gas_state, hv, init_from_file=False):
|
def __init__(self, name, size, heat_program, charge_program, burned_gas_state, hv, init_from_file=False):
|
||||||
|
"""Initializes Battery simulation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): Identifier name.
|
||||||
|
size (int): Total count of ovens.
|
||||||
|
heat_program (HeatSchedule): Heating scheduler object.
|
||||||
|
charge_program (ChargeSchedule): Charging scheduler object.
|
||||||
|
burned_gas_state (tuple): Initial TPX state of burned flue gas.
|
||||||
|
hv (float): Net heating value (J/kg).
|
||||||
|
init_from_file (bool, optional): Recover state from pickle. Defaults to False.
|
||||||
|
"""
|
||||||
self.name = name # Battery name
|
self.name = name # Battery name
|
||||||
self.size = size # Size of battery, number of ovens
|
self.size = size # Size of battery, number of ovens
|
||||||
self.heat_program = heat_program # Heat program or schedule object
|
self.heat_program = heat_program # Heat program or schedule object
|
||||||
|
|
@ -401,15 +674,32 @@ class Battery:
|
||||||
self.update(dt) # Time adavancement
|
self.update(dt) # Time adavancement
|
||||||
|
|
||||||
def mdot(self, t):
|
def mdot(self, t):
|
||||||
|
"""Calculates mass flow rate of gas at time t.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t (float): Simulation time (hours).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Mass flow rate (kg/s).
|
||||||
|
"""
|
||||||
return self.mdot0 * self.heat_program.f(t) / self.normal_heat
|
return self.mdot0 * self.heat_program.f(t) / self.normal_heat
|
||||||
|
|
||||||
def next_oven(self):
|
def next_oven(self):
|
||||||
''' Index of the oven to which apply push and charge '''
|
"""Returns the index of the next oven to be pushed and charged.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Oven index (0-indexed).
|
||||||
|
"""
|
||||||
next_oven_id = self.oven_idx_order[self.sequence_idx % self.size]
|
next_oven_id = self.oven_idx_order[self.sequence_idx % self.size]
|
||||||
self.sequence_idx += 1
|
self.sequence_idx += 1
|
||||||
return next_oven_id
|
return next_oven_id
|
||||||
|
|
||||||
def bake(self, dt):
|
def bake(self, dt):
|
||||||
|
"""Advances thermal states of combustion chambers, walls, and ovens.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dt (float): Simulation time step (hours).
|
||||||
|
"""
|
||||||
# update combustion chamber equilibrium temperature
|
# update combustion chamber equilibrium temperature
|
||||||
# Tad = 연료 조성과 공연비로 결정
|
# Tad = 연료 조성과 공연비로 결정
|
||||||
# m_dot = 연료 발열량과 공급열량 공연비로 결정
|
# m_dot = 연료 발열량과 공급열량 공연비로 결정
|
||||||
|
|
@ -482,29 +772,63 @@ class Battery:
|
||||||
# integrate heat to oven # 오븐 벽면 온도 우선 시간 함수로
|
# integrate heat to oven # 오븐 벽면 온도 우선 시간 함수로
|
||||||
|
|
||||||
def push_and_charge(self, coke_charge):
|
def push_and_charge(self, coke_charge):
|
||||||
|
"""Orchestrates pushing older coke and charging fresh coal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
coke_charge (CokeCharge): The fresh coke charge instance.
|
||||||
|
"""
|
||||||
if len(self.processing) >= self.size:
|
if len(self.processing) >= self.size:
|
||||||
self.push(coke_charge.t_charge)
|
self.push(coke_charge.t_charge)
|
||||||
self.charge(coke_charge)
|
self.charge(coke_charge)
|
||||||
|
|
||||||
def push(self, t):
|
def push(self, t):
|
||||||
""" Push complete coke out of oven """
|
"""Pushes the finished coke out of the oven.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t (float): Current time (hours).
|
||||||
|
"""
|
||||||
coke = self.processing.pop(0)
|
coke = self.processing.pop(0)
|
||||||
coke.end_baking(t)
|
coke.end_baking(t)
|
||||||
self.product.append(coke)
|
self.product.append(coke)
|
||||||
|
|
||||||
def charge(self, coke_charge):
|
def charge(self, coke_charge):
|
||||||
|
"""Charges a fresh coal unit into the oven list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
coke_charge (CokeCharge): The coal charge instance.
|
||||||
|
"""
|
||||||
self.ovens[coke_charge.idx_oven].charge(coke_charge)
|
self.ovens[coke_charge.idx_oven].charge(coke_charge)
|
||||||
self.processing.append(coke_charge)
|
self.processing.append(coke_charge)
|
||||||
|
|
||||||
def dQ(self, dt):
|
def dQ(self, dt):
|
||||||
|
"""Calculates total heat supplied over time interval dt.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dt (float): Time interval (hours).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Cumulative heat (GJ).
|
||||||
|
"""
|
||||||
return self.heat_program.dQ(self.t, self.t+dt)
|
return self.heat_program.dQ(self.t, self.t+dt)
|
||||||
|
|
||||||
def is_pc_time(self, dt):
|
def is_pc_time(self, dt):
|
||||||
''' Whether P/C should be done in this time step '''
|
"""Checks if push/charge should happen in the current time step.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dt (float): Time step (hours).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if push/charge is scheduled.
|
||||||
|
"""
|
||||||
period = self.charge_program.period(self.t)
|
period = self.charge_program.period(self.t)
|
||||||
return self.t + dt >= period + self.t_last
|
return self.t + dt >= period + self.t_last
|
||||||
|
|
||||||
def update(self, dt):
|
def update(self, dt):
|
||||||
|
"""Advances simulation by dt.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dt (float): Simulation step size (hours).
|
||||||
|
"""
|
||||||
# dQ = self.heat_program.dQ(self.t, self.t+dt) # t, t+dt 사이 공급하는 열량, array 로 대체 필요
|
# dQ = self.heat_program.dQ(self.t, self.t+dt) # t, t+dt 사이 공급하는 열량, array 로 대체 필요
|
||||||
|
|
||||||
# t 에서 t+dt 까지 탄화실 가열
|
# t 에서 t+dt 까지 탄화실 가열
|
||||||
|
|
@ -548,7 +872,16 @@ class Battery:
|
||||||
|
|
||||||
|
|
||||||
def coke_oven_exhaust_stoichiometry(phi=1.0, return_unburned=False):
|
def coke_oven_exhaust_stoichiometry(phi=1.0, return_unburned=False):
|
||||||
|
"""Calculates exhaust gas composition for coke oven gas combustion.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
phi (float, optional): Equivalence ratio. Defaults to 1.0.
|
||||||
|
return_unburned (bool, optional): If True, returns both unburned and
|
||||||
|
burned gas compositions. Defaults to False.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict or tuple: Burned composition dictionary, or (unburned, burned) tuple.
|
||||||
|
"""
|
||||||
# Define the oxidizer composition, here air with 21 mol-% O2 and 79 mol-% N2
|
# Define the oxidizer composition, here air with 21 mol-% O2 and 79 mol-% N2
|
||||||
air = "O2:1,N2:3.762"
|
air = "O2:1,N2:3.762"
|
||||||
coke_oven_fuel = "H2:6.42, O2:0.39, N2:47.28, CH4:1.79, CO:24.25, CO2:19.72, C2H4:0.13, C2H6:0.04"
|
coke_oven_fuel = "H2:6.42, O2:0.39, N2:47.28, CH4:1.79, CO:24.25, CO2:19.72, C2H4:0.13, C2H6:0.04"
|
||||||
|
|
@ -578,18 +911,60 @@ def coke_oven_exhaust_stoichiometry(phi=1.0, return_unburned=False):
|
||||||
|
|
||||||
|
|
||||||
class HeatSchedule:
|
class HeatSchedule:
|
||||||
|
"""Represents a heat supply program schedule.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
xp (array-like): Timeline anchor points (hours).
|
||||||
|
fp (array-like): Heat loads at anchor points (GJ/rev).
|
||||||
|
f (callable): Interpolation function mapping time -> heat load.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, xp, fp):
|
def __init__(self, xp, fp):
|
||||||
|
"""Initializes HeatSchedule.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
xp (array-like): Timeline hours.
|
||||||
|
fp (array-like): Heat load array.
|
||||||
|
"""
|
||||||
self.xp = xp
|
self.xp = xp
|
||||||
self.fp = fp
|
self.fp = fp
|
||||||
self.f = lambda x: np.interp(x, self.xp, self.fp)
|
self.f = lambda x: np.interp(x, self.xp, self.fp)
|
||||||
|
|
||||||
def dQ(self, t0, t1):
|
def dQ(self, t0, t1):
|
||||||
|
"""Integrates heat input from time t0 to t1.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t0 (float): Start time (hours).
|
||||||
|
t1 (float): End time (hours).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Cumulative heat (GJ).
|
||||||
|
"""
|
||||||
x = np.linspace(t0, t1, 31)
|
x = np.linspace(t0, t1, 31)
|
||||||
return np.trapz(self.f(x), x)
|
return np.trapz(self.f(x), x)
|
||||||
|
|
||||||
|
|
||||||
class ChargeSchedule:
|
class ChargeSchedule:
|
||||||
|
"""Represents the scheduling sequence of coal charging operations.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
xp (numpy.ndarray): Charging program phase change hours.
|
||||||
|
fp (numpy.ndarray): Charging rates during phases.
|
||||||
|
f (callable): Interpolation function mapping time -> charging rate.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, normal_load, service_start, service_time, service_load, aux_start, aux_time, aux_load):
|
def __init__(self, normal_load, service_start, service_time, service_load, aux_start, aux_time, aux_load):
|
||||||
|
"""Initializes ChargeSchedule.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
normal_load (float): Baseline charging rate.
|
||||||
|
service_start (float): Start hour for maintenance service.
|
||||||
|
service_time (float): Duration of maintenance service (hours).
|
||||||
|
service_load (float): Charging rate during maintenance.
|
||||||
|
aux_start (float): Start hour for auxiliary service phase.
|
||||||
|
aux_time (float): Duration of auxiliary phase (hours).
|
||||||
|
aux_load (float): Charging rate during auxiliary phase.
|
||||||
|
"""
|
||||||
self.xp = np.array([service_start, service_start, service_start+service_time, service_start+service_time,
|
self.xp = np.array([service_start, service_start, service_start+service_time, service_start+service_time,
|
||||||
aux_start, aux_start, aux_start+aux_time, aux_start+aux_time, ])
|
aux_start, aux_start, aux_start+aux_time, aux_start+aux_time, ])
|
||||||
self.fp = np.array([normal_load, service_load, service_load, normal_load,
|
self.fp = np.array([normal_load, service_load, service_load, normal_load,
|
||||||
|
|
@ -597,10 +972,27 @@ class ChargeSchedule:
|
||||||
self.f = lambda x: np.interp(x, self.xp, self.fp)
|
self.f = lambda x: np.interp(x, self.xp, self.fp)
|
||||||
|
|
||||||
def to_charge(self, t0, t1):
|
def to_charge(self, t0, t1):
|
||||||
self.f(t0)
|
"""Calculates cumulative coal units charged between t0 and t1.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t0 (float): Start time.
|
||||||
|
t1 (float): End time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Total units charged.
|
||||||
|
"""
|
||||||
|
# (Note: 'x' is not defined here in original, keeping it as is to preserve original logic)
|
||||||
return np.trapz(self.f(x), x)
|
return np.trapz(self.f(x), x)
|
||||||
|
|
||||||
def period(self, t):
|
def period(self, t):
|
||||||
|
"""Calculates the time interval between subsequent charges.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t (float): Current time (hours).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Time period (hours).
|
||||||
|
"""
|
||||||
return 24 / self.f(t)
|
return 24 / self.f(t)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
118
build_docs.py
Normal file
118
build_docs.py
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def check_command_installed(cmd):
|
||||||
|
"""Checks if a command-line tool is installed in the current environment."""
|
||||||
|
return shutil.which(cmd) is not None
|
||||||
|
|
||||||
|
def run_process(args, description):
|
||||||
|
"""Runs a subprocess with clean console updates."""
|
||||||
|
print(f"\n[Running] {description}...")
|
||||||
|
try:
|
||||||
|
res = subprocess.run(args, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
print(f"[Success] {description} completed successfully!")
|
||||||
|
if res.stdout.strip():
|
||||||
|
print(res.stdout.strip())
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"[Error] {description} FAILED with exit code {e.returncode}!")
|
||||||
|
if e.stdout:
|
||||||
|
print("--- STDOUT ---")
|
||||||
|
print(e.stdout.strip())
|
||||||
|
if e.stderr:
|
||||||
|
print("--- STDERR ---")
|
||||||
|
print(e.stderr.strip())
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Error] Failed to execute process: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def build_orchestrator():
|
||||||
|
workspace_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
docs_dir = os.path.join(workspace_dir, "docs")
|
||||||
|
site_dir = os.path.join(workspace_dir, "site")
|
||||||
|
|
||||||
|
# Clean old docs and site
|
||||||
|
if os.path.exists(docs_dir):
|
||||||
|
shutil.rmtree(docs_dir)
|
||||||
|
os.makedirs(docs_dir, exist_ok=True)
|
||||||
|
os.makedirs(os.path.join(docs_dir, "python"), exist_ok=True)
|
||||||
|
|
||||||
|
if os.path.exists(site_dir):
|
||||||
|
shutil.rmtree(site_dir)
|
||||||
|
|
||||||
|
# 1. Create landing index.md
|
||||||
|
index_path = os.path.join(docs_dir, "index.md")
|
||||||
|
print(f"Creating landing index: {index_path}")
|
||||||
|
with open(index_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write("""# Coke Oven Maintenance Plan Reference Manual
|
||||||
|
|
||||||
|
Welcome to the automated documentation suite for the coke oven maintenance plan project! This manual provides comprehensive technical reference for simulating coke oven processes, thermal dynamics, and battery operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
This project simulates the thermal response of a coke oven battery including combustion chambers, refractory brick walls, and oven charges. Below is the primary object-oriented component interaction:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
Battery[Battery] -->|Manages| CombustionChamber[CombustionChamber]
|
||||||
|
Battery -->|Manages| RefractoryWall[RefractoryWall]
|
||||||
|
Battery -->|Manages| OvenChamber[OvenChamber]
|
||||||
|
OvenChamber -->|Holds| CokeCharge[CokeCharge]
|
||||||
|
RefractoryWall -->|Uses| TInternal[TInternal]
|
||||||
|
RefractoryWall -->|Uses| CokeOvenBrickHeatEqn[CokeOvenBrickHeatEqn]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Navigation Guide
|
||||||
|
|
||||||
|
- **[Python API Reference](python/battery.md)**: Automatically parsed API, signatures, and detailed documentation for the `Battery.py` classes and helper subroutines.
|
||||||
|
|
||||||
|
*Generated automatically using industry-standard tools: MkDocs, mkdocstrings, and Material theme.*
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 2. Create Python stubs for mkdocstrings
|
||||||
|
stub_path = os.path.join(docs_dir, "python", "battery.md")
|
||||||
|
print(f"Creating Python stub: {stub_path}")
|
||||||
|
with open(stub_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write("""# Battery API Reference
|
||||||
|
|
||||||
|
::: Battery
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 3. Environment validation
|
||||||
|
if not check_command_installed("mkdocs"):
|
||||||
|
print("\n" + "="*80)
|
||||||
|
print(" [Warning] Missing Required Documentation Tools!")
|
||||||
|
print("="*80)
|
||||||
|
print("To compile the complete manual, please install the requirements:")
|
||||||
|
print(" pip install -r requirements-docs.txt")
|
||||||
|
print("Note: If running in a virtualenv, make sure it is activated.")
|
||||||
|
print("="*80 + "\n")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 4. Compile Python API and build MkDocs site
|
||||||
|
mkdocs_success = run_process(["mkdocs", "build"], "MkDocs Website Compiler")
|
||||||
|
if mkdocs_success:
|
||||||
|
print("\n" + "="*80)
|
||||||
|
print(" 🎉 Documentation manual built successfully!")
|
||||||
|
print("="*80)
|
||||||
|
print(f"Output directory: {site_dir}")
|
||||||
|
print("To view or serve the website locally, run:")
|
||||||
|
print(" mkdocs serve")
|
||||||
|
print("="*80)
|
||||||
|
else:
|
||||||
|
print("\n[Error] MkDocs website compilation FAILED!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
success = build_orchestrator()
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
48
mkdocs.yml
Normal file
48
mkdocs.yml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
site_name: Coke Oven Maintenance Plan Reference Manual
|
||||||
|
theme:
|
||||||
|
name: material
|
||||||
|
palette:
|
||||||
|
- media: "(prefers-color-scheme: dark)"
|
||||||
|
scheme: slate
|
||||||
|
primary: red
|
||||||
|
accent: deep orange
|
||||||
|
toggle:
|
||||||
|
icon: material/brightness-4
|
||||||
|
name: Switch to light mode
|
||||||
|
- media: "(prefers-color-scheme: light)"
|
||||||
|
scheme: default
|
||||||
|
primary: red
|
||||||
|
accent: deep orange
|
||||||
|
toggle:
|
||||||
|
icon: material/brightness-7
|
||||||
|
name: Switch to dark mode
|
||||||
|
features:
|
||||||
|
- navigation.tabs
|
||||||
|
- navigation.sections
|
||||||
|
- navigation.expand
|
||||||
|
- content.code.copy
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- search
|
||||||
|
- mkdocstrings:
|
||||||
|
default_handler: python
|
||||||
|
handlers:
|
||||||
|
python:
|
||||||
|
paths: [.]
|
||||||
|
options:
|
||||||
|
docstring_style: google
|
||||||
|
show_source: true
|
||||||
|
show_root_heading: true
|
||||||
|
show_bases: true
|
||||||
|
|
||||||
|
nav:
|
||||||
|
- Home: index.md
|
||||||
|
- Python API Reference:
|
||||||
|
- Battery Module: python/battery.md
|
||||||
|
|
||||||
|
markdown_extensions:
|
||||||
|
- pymdownx.superfences:
|
||||||
|
custom_fences:
|
||||||
|
- name: mermaid
|
||||||
|
class: mermaid
|
||||||
|
format: !!python/name:pymdownx.superfences.fence_code_format
|
||||||
5
requirements-docs.txt
Normal file
5
requirements-docs.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
mkdocs==1.6.1
|
||||||
|
mkdocs-material==9.7.6
|
||||||
|
mkdocstrings[python]==0.25.2
|
||||||
|
mkdocs-autorefs==1.3.1
|
||||||
|
pygments==2.17.2
|
||||||
Loading…
Add table
Reference in a new issue