二暴露區模式 (Two-Zone Model) 暴露評估工具
<%
# 設置頁面編碼為 UTF-8
request$content_type("text/html; charset=utf-8")
# 檢查是否為表單提交
is_post <- request$method() == "POST"
# 初始化變數
G <- VN <- VF <- Q <- beta_val <- t_val <- MW <- 0
CN0 <- CF0 <- 0
twa_limit <- stel_limit <- idlh_limit <- oel_limit <- NA
twa_unit <- stel_unit <- idlh_unit <- oel_unit <- "mg_m3"
calc_option <- "time_dependent"
results <- NULL
if (is_post) {
# 從表單獲取輸入值
params <- request$params()
# 將輸入值轉換為數值
G <- as.numeric(params$G)
VN <- as.numeric(params$VN)
VF <- as.numeric(params$VF)
Q <- as.numeric(params$Q)
beta_val <- as.numeric(params$beta_val)
t_val <- as.numeric(params$t_val)
MW <- as.numeric(params$MW)
CN0 <- as.numeric(params$CN0)
CF0 <- as.numeric(params$CF0)
twa_limit_val <- as.numeric(params$twa_limit)
stel_limit_val <- as.numeric(params$stel_limit)
idlh_limit_val <- as.numeric(params$idlh_limit)
oel_limit_val <- as.numeric(params$oel_limit)
twa_unit <- params$twa_unit
stel_unit <- params$stel_unit
idlh_unit <- params$idlh_unit
oel_unit <- params$oel_unit
calc_option <- params$calc_option
# --- 單位轉換函數 ---
# ppm 轉換為 mg/m3
ppm_to_mgm3 <- function(ppm, mw) {
if (is.na(ppm) || is.na(mw) || mw == 0) return(NA)
return(ppm * mw / 24.45)
}
# 根據選擇的單位進行轉換
twa_limit <- ifelse(twa_unit == "ppm", ppm_to_mgm3(twa_limit_val, MW), twa_limit_val)
stel_limit <- ifelse(stel_unit == "ppm", ppm_to_mgm3(stel_limit_val, MW), stel_limit_val)
idlh_limit <- ifelse(idlh_unit == "ppm", ppm_to_mgm3(idlh_limit_val, MW), idlh_limit_val)
oel_limit <- ifelse(oel_unit == "ppm", ppm_to_mgm3(oel_limit_val, MW), oel_limit_val)
# --- 核心計算函數 ---
# 計算 lambda 1 和 lambda 2
calc_lambdas <- function(Q, beta_val, VN, VF) {
# 確保分母不為零
if (VN == 0 || VF == 0) return(list(lambda1 = NA, lambda2 = NA))
term1 <- (beta_val * VF + VN * (beta_val + Q)) / (VN * VF)
term2_sqrt <- (term1^2) - 4 * (beta_val * Q / (VN * VF))
# 檢查開根號項是否為負
if (term2_sqrt < 0) return(list(lambda1 = NA, lambda2 = NA))
lambda1 <- 0.5 * (-term1 + sqrt(term2_sqrt))
lambda2 <- 0.5 * (-term1 - sqrt(term2_sqrt))
return(list(lambda1 = lambda1, lambda2 = lambda2))
}
# --- 計算邏輯 ---
lambdas <- calc_lambdas(Q, beta_val, VN, VF)
lambda1 <- lambdas$lambda1
lambda2 <- lambdas$lambda2
# 初始化結果列表
results <- list()
# 檢查 lambda 是否有效
if (!is.na(lambda1) && !is.na(lambda2)) {
time_points <- 1:t_val
CN_t <- numeric(t_val)
CF_t <- numeric(t_val)
if (calc_option == "time_dependent") {
# 功能1: 時間濃度解
if (lambda1 != lambda2 && beta_val != 0 && Q != 0 && VN != 0) {
A <- G / Q * (beta_val / (beta_val + Q))
B_num <- beta_val * Q + lambda2 * VN * (beta_val + Q)
B_den <- beta_val * Q * VN * (lambda1 - lambda2)
B <- G * (B_num / B_den)
C_num <- beta_val * Q + lambda1 * VN * (beta_val + Q)
C_den <- beta_val * Q * VN * (lambda1 - lambda2)
C <- G * (C_num / C_den)
for(i in time_points) {
CN_t[i] <- A + B * exp(lambda1 * i) - C * exp(lambda2 * i)
}
D <- G / Q
for(i in time_points) {
term_B <- G * ((lambda1 * VN + beta_val) / beta_val) * (B_num / B_den) * exp(lambda1 * i)
term_C <- G * ((lambda2 * VN + beta_val) / beta_val) * (C_num / C_den) * exp(lambda2 * i)
CF_t[i] <- D + term_B - term_C
}
} else {
CN_t[] <- NA
CF_t[] <- NA
}
results$CN_final <- CN_t[t_val]
results$CF_final <- CF_t[t_val]
} else if (calc_option == "steady_state") {
# 功能2: 穩態濃度
if (Q > 0 && beta_val > 0) {
CN_ss <- G / Q + G / beta_val
CF_ss <- G / Q
CN_t[] <- CN_ss
CF_t[] <- CF_ss
} else {
CN_ss <- CF_ss <- NA
}
results$CN_final <- CN_ss
results$CF_final <- CF_ss
} else if (calc_option == "decay") {
# 功能3: 濃度衰減
if (lambda1 != lambda2 && VN != 0) {
for(i in time_points) {
term1_num <- beta_val * (CF0 - CN0) - lambda2 * VN * CN0
term1_den <- VN * (lambda1 - lambda2)
term1 <- (term1_num / term1_den) * exp(lambda1 * i)
term2_num <- beta_val * (CN0 - CF0) + lambda1 * VN * CN0
term2_den <- VN * (lambda1 - lambda2)
term2 <- (term2_num / term2_den) * exp(lambda2 * i)
CN_t[i] <- term1 + term2
cf_term1_factor <- (lambda1 * VN + beta_val) / beta_val
cf_term2_factor <- (lambda2 * VN + beta_val) / beta_val
CF_t[i] <- cf_term1_factor * term1 + cf_term2_factor * term2
}
} else {
CN_t[] <- NA
CF_t[] <- NA
}
results$CN_final <- CN_t[t_val]
results$CF_final <- CF_t[t_val]
}
results$time_points <- time_points
results$CN_t <- CN_t
results$CF_t <- CF_t
# --- TWA 和 STEL 計算 ---
if(t_val > 0 && !any(is.na(CN_t))) {
# TWA 計算 (基於近場濃度)
results$twa_calc <- sum(CN_t) / 480
# STEL 計算 (基於近場濃度,取最大15分鐘移動平均)
if (t_val >= 15) {
rolling_sums <- zoo::rollapply(CN_t, width = 15, FUN = sum, align = "right")
results$stel_calc <- max(rolling_sums / 15)
} else {
results$stel_calc <- sum(CN_t) / 15 # 如果時間不足15分鐘,則以現有時間計算
}
} else {
results$twa_calc <- NA
results$stel_calc <- NA
}
# --- 風險分級 ---
risk_table <- data.frame(
Parameter = character(),
Exposure_Limit = character(),
Calculated_Value = character(),
Risk_Ratio = character(),
Risk_Level = character(),
stringsAsFactors = FALSE
)
# 1. IDLH
if (!is.na(idlh_limit)) {
peak_conc <- if(length(CN_t) > 0) max(CN_t, na.rm = TRUE) else NA
risk_ratio <- if (!is.na(peak_conc)) peak_conc / idlh_limit else NA
level <- if (!is.na(risk_ratio) && risk_ratio > 1) "4 (立即危害)" else "低於立即危害濃度"
risk_table[nrow(risk_table) + 1,] <- c("IDLH (短期)", paste(round(idlh_limit, 2), "mg/m³"), paste(round(peak_conc, 2), "mg/m³ (峰值)"), if(!is.na(risk_ratio)) round(risk_ratio, 2) else "N/A", level)
}
# 2. STEL
if (!is.na(stel_limit)) {
risk_ratio <- if (!is.na(results$stel_calc)) results$stel_calc / stel_limit else NA
level <- if (!is.na(risk_ratio) && risk_ratio > 1) "4 (有健康危險)" else "可接受"
risk_table[nrow(risk_table) + 1,] <- c("STEL (短期)", paste(round(stel_limit, 2), "mg/m³"), paste(round(results$stel_calc, 2), "mg/m³ (15分鐘)"), if(!is.na(risk_ratio)) round(risk_ratio, 2) else "N/A", level)
}
# 3. TWA
if (!is.na(twa_limit)) {
risk_ratio <- if (!is.na(results$twa_calc)) results$twa_calc / twa_limit else NA
level <- if (is.na(risk_ratio)) "N/A" else if (risk_ratio > 1) "4" else if (risk_ratio > 0.5) "3" else if (risk_ratio > 0.1) "2" else "1"
risk_table[nrow(risk_table) + 1,] <- c("TWA (長期)", paste(round(twa_limit, 2), "mg/m³"), paste(round(results$twa_calc, 2), "mg/m³ (8小時)"), if(!is.na(risk_ratio)) round(risk_ratio, 2) else "N/A", level)
}
# 4. OEL (與 TWA 比較)
if (!is.na(oel_limit)) {
risk_ratio <- if (!is.na(results$twa_calc)) results$twa_calc / oel_limit else NA
level <- if (is.na(risk_ratio)) "N/A" else if (risk_ratio > 1) "4" else if (risk_ratio > 0.5) "3" else if (risk_ratio > 0.1) "2" else "1"
risk_table[nrow(risk_table) + 1,] <- c("OEL (長期)", paste(round(oel_limit, 2), "mg/m³"), paste(round(results$twa_calc, 2), "mg/m³ (8小時)"), if(!is.na(risk_ratio)) round(risk_ratio, 2) else "N/A", level)
}
results$risk_table <- risk_table
# --- 建議控制措施 ---
final_risk_level <- 0
if (any(risk_table$Risk_Level %in% c("4 (立即危害)", "4 (有健康危險)"))) {
final_risk_level <- 4
} else {
levels <- as.numeric(risk_table$Risk_Level)
if(any(!is.na(levels))) final_risk_level <- max(levels, na.rm=TRUE)
}
results$control_measures <- switch(as.character(final_risk_level),
"1" = "管理等級 1 (低風險): 建議維持良好作業環境,持續進行通風與環境整理。",
"2" = "管理等級 2 (中度風險): 建議改善整體換氣裝置,或考慮局部抽氣裝置。",
"3" = "管理等級 3 (高風險): 應立即採取有效的工程控制,如安裝密閉系統或局部抽氣裝置,並考慮使用個人防護具。",
"4" = "管理等級 4 (極高風險): 立即停止作業!尋求工業衛生專家協助,重新評估作業流程與控制措施,在改善前不可復工。",
"0" = "無法評估風險等級,請確認輸入值。",
"無法評估風險等級,請確認輸入值。"
)
} else {
results$error <- "輸入參數錯誤,無法計算 λ (lambda) 值 (例如: 根號內為負數或體積為零)。"
}
}
%>
<% if (is_post && !is.null(results)) { %>
計算結果
<% if (!is.null(results$error)) { %>
<%= results$error %>
<% } else { %>
濃度計算
| 項目 |
數值 (mg/m³) |
| 近場 <%= ifelse(calc_option == 'steady_state', '穩態濃度', paste('在', t_val, '分鐘時的濃度')) %> (CN) |
<%= round(results$CN_final, 2) %> |
| 遠場 <%= ifelse(calc_option == 'steady_state', '穩態濃度', paste('在', t_val, '分鐘時的濃度')) %> (CF) |
<%= round(results$CF_final, 2) %> |
| 計算之 TWA (8小時日時量平均濃度) |
<%= round(results$twa_calc, 2) %> |
| 計算之 STEL (最大15分鐘平均濃度) |
<%= round(results$stel_calc, 2) %> |
濃度隨時間變化圖
風險評估與管理分級
| 評估項目 |
暴露限值 (mg/m³) |
計算值 (mg/m³) |
風險比 (計算值/限值) |
風險等級 |
<% for(i in 1:nrow(results$risk_table)) {
row <- results$risk_table[i, ]
level <- row$Risk_Level
class <- ""
if (grepl("4", level)) {
class <- "risk-4"
} else if (grepl("3", level)) {
class <- "risk-3"
} else if (grepl("2", level)) {
class <- "risk-2"
} else if (grepl("1", level)) {
class <- "risk-1"
}
%>
| <%= row$Parameter %> |
<%= row$Exposure_Limit %> |
<%= row$Calculated_Value %> |
<%= row$Risk_Ratio %> |
<%= row$Risk_Level %> |
<% } %>
建議控制措施
<%= results$control_measures %>
<% } %>
<% } %>