def _kamal_sourour_rate_vec(T: np.ndarray, alpha: np.ndarray) -> np.ndarray:
T_safe = np.clip(T, _T_FLOOR, _T_CEIL)
alpha_safe = np.clip(alpha, 0.0, 1.0)
inv_RT = 1.0 / (_R_GAS * T_safe)
arg1 = np.clip(_E1 * inv_RT, 0.0, _EXP_ARG_MAX)
arg2 = np.clip(_E2 * inv_RT, 0.0, _EXP_ARG_MAX)
k1 = _A1 * np.exp(-arg1)
k2 = _A2 * np.exp(-arg2)
return (k1 + k2 * np.power(alpha_safe, _M)) * np.power(1.0 - alpha_safe, _N_ORD)
def _fp_woven_fabric_2d_rhs(t, y):
T_flat = np.clip(y[:_WF_N2D], _T_FLOOR, _T_CEIL)
alpha_flat = np.clip(y[_WF_N2D:2 * _WF_N2D], 0.0, 1.0)
P_flat = y[2 * _WF_N2D:]
T = T_flat.reshape(_WF_NX, _WF_NY)
alpha = alpha_flat.reshape(_WF_NX, _WF_NY)
dadt_2d = _kamal_sourour_rate_vec(T, alpha)
dT = np.empty((_WF_NX, _WF_NY))
for i in range(_WF_NX):
for j in range(_WF_NY):
kx = _WF_KX[i, j]
diff_x = _WF_DIFF_X[i, j]
diff_y = _WF_DIFF_Y[i, j]
ky = _WF_KY[i, j]
# x-direction Laplacian
if j == 0:
lap_x = 0.0 # Left edge Dirichlet
elif j == _WF_NY - 1:
T_ghost = T[i, j] + (_H_CONV * _WF_DX / kx) * (_T_AMBIENT - T[i, j])
lap_x = (T[i, j - 1] - 2.0 * T[i, j] + T_ghost) * _WF_INV_DX2
else:
T_left = _WF_T_LEFT if j == 1 else T[i, j - 1]
lap_x = (T_left - 2.0 * T[i, j] + T[i, j + 1]) * _WF_INV_DX2
# y-direction Laplacian
if i == 0:
T_ghost = T[i, j] + (_H_CONV * _WF_DY / ky) * (_T_AMBIENT - T[i, j])
lap_y = (T_ghost - 2.0 * T[i, j] + T[i + 1, j]) * _WF_INV_DY2
elif i == _WF_NX - 1:
T_ghost = T[i, j] + (_H_CONV * _WF_DY / ky) * (_T_AMBIENT - T[i, j])
lap_y = (T[i - 1, j] - 2.0 * T[i, j] + T_ghost) * _WF_INV_DY2
else:
lap_y = (T[i - 1, j] - 2.0 * T[i, j] + T[i + 1, j]) * _WF_INV_DY2
dT[i, j] = diff_x * lap_x + diff_y * lap_y + _SRC_COEFF * dadt_2d[i, j]
dT[:, 0] = 0.0
# Gas pressure
dP = (
(_RHO_RESIN * _V_GAS_SPECIFIC * dadt_2d.ravel() * _R_GAS_IDEAL * T_flat) / _V_PORE
- P_flat * _PERM_LOSS
)
dy = np.empty(_WF_DIM)
dy[:_WF_N2D] = dT.ravel()
dy[_WF_N2D:2 * _WF_N2D] = dadt_2d.ravel()
dy[2 * _WF_N2D:] = dP
return dy