From e374c445604d3e88559e864ba6615627605bc46a Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 8 Jan 2026 20:21:42 +0000 Subject: [PATCH 01/21] Initial implemetnatino of rust for all equations by CoPilot --- crates/sfpe_handbook/src/chapter_50.rs | 3 ++ .../src/chapter_50/equation_50_18.rs | 31 +++++++++++++++++++ .../src/chapter_50/equation_50_19.rs | 31 +++++++++++++++++++ .../src/chapter_50/equation_50_20.rs | 31 +++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 crates/sfpe_handbook/src/chapter_50/equation_50_18.rs create mode 100644 crates/sfpe_handbook/src/chapter_50/equation_50_19.rs create mode 100644 crates/sfpe_handbook/src/chapter_50/equation_50_20.rs diff --git a/crates/sfpe_handbook/src/chapter_50.rs b/crates/sfpe_handbook/src/chapter_50.rs index 069e53a0..f7dde780 100644 --- a/crates/sfpe_handbook/src/chapter_50.rs +++ b/crates/sfpe_handbook/src/chapter_50.rs @@ -2,7 +2,10 @@ pub mod equation_50_1; pub mod equation_50_14; pub mod equation_50_15; pub mod equation_50_16; +pub mod equation_50_18; +pub mod equation_50_19; pub mod equation_50_2; +pub mod equation_50_20; pub mod equation_50_4; pub mod equation_50_6; pub mod equation_50_7; diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs new file mode 100644 index 00000000..b0a0a9f3 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs @@ -0,0 +1,31 @@ +pub fn factor(a_sb: f64, a_bo: f64, t_b: f64, t_s: f64) -> f64 { + let numerator = a_sb.powf(2.0) * (t_b + 273.0); + let denominator = a_bo.powf(2.0) * (t_s + 273.0); + 1.0 + numerator / denominator +} + +#[cfg(not(coverage))] +pub fn factor_equation( + f_r: String, + a_sb: String, + t_b: String, + a_bo: String, + t_s: String, +) -> String { + format!( + "{} = 1 + \\frac{{ {}^2 \\times ( {} + 273 ) }}{{ {}^2 \\times ( {} + 273 ) }}", + f_r, a_sb, t_b, a_bo, t_s + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_factor() { + let result = factor(0.005, 0.005, 15.0, 5.0); + let expected = 2.035971223; + assert!((result - expected).abs() < 1e-6); + } +} \ No newline at end of file diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs new file mode 100644 index 00000000..b0a0a9f3 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs @@ -0,0 +1,31 @@ +pub fn factor(a_sb: f64, a_bo: f64, t_b: f64, t_s: f64) -> f64 { + let numerator = a_sb.powf(2.0) * (t_b + 273.0); + let denominator = a_bo.powf(2.0) * (t_s + 273.0); + 1.0 + numerator / denominator +} + +#[cfg(not(coverage))] +pub fn factor_equation( + f_r: String, + a_sb: String, + t_b: String, + a_bo: String, + t_s: String, +) -> String { + format!( + "{} = 1 + \\frac{{ {}^2 \\times ( {} + 273 ) }}{{ {}^2 \\times ( {} + 273 ) }}", + f_r, a_sb, t_b, a_bo, t_s + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_factor() { + let result = factor(0.005, 0.005, 15.0, 5.0); + let expected = 2.035971223; + assert!((result - expected).abs() < 1e-6); + } +} \ No newline at end of file diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs new file mode 100644 index 00000000..b0a0a9f3 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs @@ -0,0 +1,31 @@ +pub fn factor(a_sb: f64, a_bo: f64, t_b: f64, t_s: f64) -> f64 { + let numerator = a_sb.powf(2.0) * (t_b + 273.0); + let denominator = a_bo.powf(2.0) * (t_s + 273.0); + 1.0 + numerator / denominator +} + +#[cfg(not(coverage))] +pub fn factor_equation( + f_r: String, + a_sb: String, + t_b: String, + a_bo: String, + t_s: String, +) -> String { + format!( + "{} = 1 + \\frac{{ {}^2 \\times ( {} + 273 ) }}{{ {}^2 \\times ( {} + 273 ) }}", + f_r, a_sb, t_b, a_bo, t_s + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_factor() { + let result = factor(0.005, 0.005, 15.0, 5.0); + let expected = 2.035971223; + assert!((result - expected).abs() < 1e-6); + } +} \ No newline at end of file From f9b86228de80d04c5c53ccc24a360be10cf1a4ae Mon Sep 17 00:00:00 2001 From: simonsantama Date: Sun, 11 Jan 2026 19:33:14 +0000 Subject: [PATCH 02/21] Adds equation to list of similar equations, including equation in cibse guide e --- crates/fire_dynamics_tools/src/lib.rs | 1 + .../src/similar_equations.rs | 46 +++++++++++++++++++ .../src/chapter_50/equation_50_18.rs | 31 +++++++------ 3 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 crates/fire_dynamics_tools/src/similar_equations.rs diff --git a/crates/fire_dynamics_tools/src/lib.rs b/crates/fire_dynamics_tools/src/lib.rs index d8eec221..e9e6aa18 100644 --- a/crates/fire_dynamics_tools/src/lib.rs +++ b/crates/fire_dynamics_tools/src/lib.rs @@ -3,3 +3,4 @@ pub mod chapter_2; pub mod chapter_4; pub mod chapter_5; pub mod chapter_9; +pub mod similar_equations; diff --git a/crates/fire_dynamics_tools/src/similar_equations.rs b/crates/fire_dynamics_tools/src/similar_equations.rs new file mode 100644 index 00000000..956995e9 --- /dev/null +++ b/crates/fire_dynamics_tools/src/similar_equations.rs @@ -0,0 +1,46 @@ +/// Similar equations grouped by functionality across different fire engineering standards. +/// This module provides cross-references to equations that calculate similar parameters +/// but may appear in different standards or use slightly different approaches. + +/// Fractional Effective Dose (FED) calculations +/// +/// These equations calculate the fractional effective dose, which represents +/// the accumulated toxic exposure over time as a fraction of the lethal dose. +pub mod fractional_effective_dose { + /// SFPE Handbook Chapter 50, Equation 50-18 + /// + /// Calculates FED using sum of concentration × time intervals divided by lethal concentration. + /// Formula: FED = Σ(c_i × Δt_i) / LC_t50 + /// + /// This implementation handles discrete time intervals with varying concentrations. + pub use sfpe_handbook::chapter_50::equation_50_18::fed as sfpe_fed; + + /// CIBSE Guide E Chapter 10, Equation 10-8 + /// + /// Calculates FED using mass flow rate × time divided by lethal concentration. + /// Formula: FED = (m_f × t) / LC_50 + /// + /// This implementation assumes constant mass flow rate over the exposure time. + pub use cibse_guide_e::chapter_10::equation_10_8::fractional_effective_dose as cibse_fed; + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn test_sfpe_fed() { + let c_i = vec![0.1, 0.2, 0.3]; + let delta_t_i = vec![1.0, 2.0, 3.0]; + let lc_t50 = 10.0; + let result = sfpe_fed(c_i, delta_t_i, lc_t50); + let expected = 0.14; // (0.1*1.0 + 0.2*2.0 + 0.3*3.0) / 10.0 + assert!((result - expected).abs() < 1e-6); + } + + #[test] + fn test_cibse_fed() { + let result = cibse_fed(2.0, 120.0, 1000.0); + assert_eq!(result, 0.24); + } + } +} \ No newline at end of file diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs index b0a0a9f3..5b7bd9c3 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs @@ -1,20 +1,18 @@ -pub fn factor(a_sb: f64, a_bo: f64, t_b: f64, t_s: f64) -> f64 { - let numerator = a_sb.powf(2.0) * (t_b + 273.0); - let denominator = a_bo.powf(2.0) * (t_s + 273.0); - 1.0 + numerator / denominator +pub fn fed(c_i: Vec, delta_t_i: Vec, lc_t50: f64) -> f64 { + let numerator: f64 = c_i.iter().zip(delta_t_i.iter()).map(|(c, dt)| c * dt).sum(); + numerator / lc_t50 } #[cfg(not(coverage))] -pub fn factor_equation( - f_r: String, - a_sb: String, - t_b: String, - a_bo: String, - t_s: String, +pub fn fed_equation( + fed: String, + c_i: String, + delta_t_i: String, + lc_t50: String, ) -> String { format!( - "{} = 1 + \\frac{{ {}^2 \\times ( {} + 273 ) }}{{ {}^2 \\times ( {} + 273 ) }}", - f_r, a_sb, t_b, a_bo, t_s + "{} = \\frac{{ \\sum {} \\times {} }}{{ {} }}", + fed, c_i, delta_t_i, lc_t50 ) } @@ -23,9 +21,12 @@ mod tests { use super::*; #[test] - fn test_factor() { - let result = factor(0.005, 0.005, 15.0, 5.0); - let expected = 2.035971223; + fn test_fed() { + let c_i = vec![0.1, 0.2, 0.3]; + let delta_t_i = vec![1.0, 2.0, 3.0]; + let lc_t50 = 10.0; + let result = fed(c_i, delta_t_i, lc_t50); + let expected = (0.1 * 1.0 + 0.2 * 2.0 + 0.3 * 3.0) / 10.0; // (0.1 + 0.4 + 0.9) / 10.0 = 0.14 assert!((result - expected).abs() < 1e-6); } } \ No newline at end of file From 4dfe7508fd788523a0a9261f3ace514bd27f638c Mon Sep 17 00:00:00 2001 From: simonsantama Date: Sun, 11 Jan 2026 19:41:13 +0000 Subject: [PATCH 03/21] Adds equation to list of similar equations, including equation in cibse guide e --- Cargo.lock | 4 ++ crates/fire_dynamics_tools/Cargo.toml | 4 ++ crates/fire_dynamics_tools/src/lib.rs | 1 - .../src/similar_equations.rs | 46 ------------------- .../docs/guide/equation-relationships.rst | 14 +++++- 5 files changed, 20 insertions(+), 49 deletions(-) delete mode 100644 crates/fire_dynamics_tools/src/similar_equations.rs diff --git a/Cargo.lock b/Cargo.lock index 50349c22..e60462c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,10 @@ version = "0.1.0" [[package]] name = "fire_dynamics_tools" version = "0.1.0" +dependencies = [ + "cibse_guide_e", + "sfpe_handbook", +] [[package]] name = "generic-array" diff --git a/crates/fire_dynamics_tools/Cargo.toml b/crates/fire_dynamics_tools/Cargo.toml index a5f5fcc6..6fdbe9e8 100644 --- a/crates/fire_dynamics_tools/Cargo.toml +++ b/crates/fire_dynamics_tools/Cargo.toml @@ -3,5 +3,9 @@ name = "fire_dynamics_tools" version = "0.1.0" edition = "2024" +[dependencies] +sfpe_handbook = { path = "../sfpe_handbook" } +cibse_guide_e = { path = "../cibse_guide_e" } + [lints] workspace = true \ No newline at end of file diff --git a/crates/fire_dynamics_tools/src/lib.rs b/crates/fire_dynamics_tools/src/lib.rs index e9e6aa18..d8eec221 100644 --- a/crates/fire_dynamics_tools/src/lib.rs +++ b/crates/fire_dynamics_tools/src/lib.rs @@ -3,4 +3,3 @@ pub mod chapter_2; pub mod chapter_4; pub mod chapter_5; pub mod chapter_9; -pub mod similar_equations; diff --git a/crates/fire_dynamics_tools/src/similar_equations.rs b/crates/fire_dynamics_tools/src/similar_equations.rs deleted file mode 100644 index 956995e9..00000000 --- a/crates/fire_dynamics_tools/src/similar_equations.rs +++ /dev/null @@ -1,46 +0,0 @@ -/// Similar equations grouped by functionality across different fire engineering standards. -/// This module provides cross-references to equations that calculate similar parameters -/// but may appear in different standards or use slightly different approaches. - -/// Fractional Effective Dose (FED) calculations -/// -/// These equations calculate the fractional effective dose, which represents -/// the accumulated toxic exposure over time as a fraction of the lethal dose. -pub mod fractional_effective_dose { - /// SFPE Handbook Chapter 50, Equation 50-18 - /// - /// Calculates FED using sum of concentration × time intervals divided by lethal concentration. - /// Formula: FED = Σ(c_i × Δt_i) / LC_t50 - /// - /// This implementation handles discrete time intervals with varying concentrations. - pub use sfpe_handbook::chapter_50::equation_50_18::fed as sfpe_fed; - - /// CIBSE Guide E Chapter 10, Equation 10-8 - /// - /// Calculates FED using mass flow rate × time divided by lethal concentration. - /// Formula: FED = (m_f × t) / LC_50 - /// - /// This implementation assumes constant mass flow rate over the exposure time. - pub use cibse_guide_e::chapter_10::equation_10_8::fractional_effective_dose as cibse_fed; - - #[cfg(test)] - mod tests { - use super::*; - - #[test] - fn test_sfpe_fed() { - let c_i = vec![0.1, 0.2, 0.3]; - let delta_t_i = vec![1.0, 2.0, 3.0]; - let lc_t50 = 10.0; - let result = sfpe_fed(c_i, delta_t_i, lc_t50); - let expected = 0.14; // (0.1*1.0 + 0.2*2.0 + 0.3*3.0) / 10.0 - assert!((result - expected).abs() < 1e-6); - } - - #[test] - fn test_cibse_fed() { - let result = cibse_fed(2.0, 120.0, 1000.0); - assert_eq!(result, 0.24); - } - } -} \ No newline at end of file diff --git a/crates/python_api/docs/guide/equation-relationships.rst b/crates/python_api/docs/guide/equation-relationships.rst index 6bb05c4a..0788a5c8 100644 --- a/crates/python_api/docs/guide/equation-relationships.rst +++ b/crates/python_api/docs/guide/equation-relationships.rst @@ -61,8 +61,18 @@ Visibility These equations calculate visibility distance through smoke but use different formulations: -- :func:`ofire.cibse_guide_e.chapter_10.equation_10_7.visibility` - CIBSE Guide E, Chapter 10, Equation 10.7 (uses extinction coefficient and optical density: k/(2.303×d)) -- :func:`ofire.fire_dynamics_tools.chapter_18.equation_18_1.visibility` - Fire Dynamics Tools, Chapter 18, Equation 18.1 (uses extinction coefficient, mass extinction coefficient, and particulate concentration: k/(αₘ×mₚ)) +- :func:`ofire.cibse_guide_e.chapter_10.equation_10_7.visibility` - CIBSE Guide E, Chapter 10, Equation 10.7 +- :func:`ofire.fire_dynamics_tools.chapter_18.equation_18_1.visibility` - Fire Dynamics Tools, Chapter 18, Equation 18.1 + +Fractional Effective Dose +~~~~~~~~~~~~~~~~~~~~~~~~~ + +*Group 3: Fractional Effective Dose Calculations* + +These equations calculate the fractional effective dose (FED), representing accumulated toxic exposure over time as a fraction of the lethal dose, but use different formulations: + +- :func:`ofire.sfpe_handbook.chapter_50.equation_50_18.fed` - SFPE Handbook, Chapter 50, Equation 50-18 +- :func:`ofire.cibse_guide_e.chapter_10.equation_10_8.fractional_effective_dose` - CIBSE Guide E, Chapter 10, Equation 10-8 Contributing to Equation Relationships --------------------------------------- From 7bfe11939848cc07ac40f0890890aeef9edcaf70 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Sun, 11 Jan 2026 19:47:09 +0000 Subject: [PATCH 04/21] Finishes 50-18 for rust --- .../src/chapter_50/equation_50_18.rs | 17 ++++++----------- .../src/chapter_50/equation_50_19.rs | 2 +- .../src/chapter_50/equation_50_20.rs | 2 +- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs index 5b7bd9c3..aba27fa4 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs @@ -4,12 +4,7 @@ pub fn fed(c_i: Vec, delta_t_i: Vec, lc_t50: f64) -> f64 { } #[cfg(not(coverage))] -pub fn fed_equation( - fed: String, - c_i: String, - delta_t_i: String, - lc_t50: String, -) -> String { +pub fn fed_equation(fed: String, c_i: String, delta_t_i: String, lc_t50: String) -> String { format!( "{} = \\frac{{ \\sum {} \\times {} }}{{ {} }}", fed, c_i, delta_t_i, lc_t50 @@ -22,11 +17,11 @@ mod tests { #[test] fn test_fed() { - let c_i = vec![0.1, 0.2, 0.3]; - let delta_t_i = vec![1.0, 2.0, 3.0]; - let lc_t50 = 10.0; + let c_i = vec![0.001, 0.003]; + let delta_t_i = vec![1.0, 2.0]; + let lc_t50 = 0.015; let result = fed(c_i, delta_t_i, lc_t50); - let expected = (0.1 * 1.0 + 0.2 * 2.0 + 0.3 * 3.0) / 10.0; // (0.1 + 0.4 + 0.9) / 10.0 = 0.14 + let expected = 0.466666667; assert!((result - expected).abs() < 1e-6); } -} \ No newline at end of file +} diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs index b0a0a9f3..7f839bed 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs @@ -28,4 +28,4 @@ mod tests { let expected = 2.035971223; assert!((result - expected).abs() < 1e-6); } -} \ No newline at end of file +} diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs index b0a0a9f3..7f839bed 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs @@ -28,4 +28,4 @@ mod tests { let expected = 2.035971223; assert!((result - expected).abs() < 1e-6); } -} \ No newline at end of file +} From 6af661cee46c29b3b154d234856f47ac130a7482 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Sun, 11 Jan 2026 19:57:50 +0000 Subject: [PATCH 05/21] Finishes 50-19 --- crates/python_api/docs/api/sfpe-handbook.rst | 8 ++++ .../src/sfpe_handbook/chapter_50.rs | 2 + .../chapter_50/equation_50_18.rs | 43 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index 9ed1e63b..8529b98c 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -104,6 +104,14 @@ Equation 50.16 - Flow Area Factor for Pressurization Systems ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_50.equation_50_16 + :members: + :undoc-members: + :show-inheritance: + +Equation 50.18 - Fractional Effective Dose +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_50.equation_50_18 :members: :undoc-members: :show-inheritance: \ No newline at end of file diff --git a/crates/python_api/src/sfpe_handbook/chapter_50.rs b/crates/python_api/src/sfpe_handbook/chapter_50.rs index a4a74490..0091fa58 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50.rs @@ -2,6 +2,7 @@ pub mod equation_50_1; pub mod equation_50_14; pub mod equation_50_15; pub mod equation_50_16; +pub mod equation_50_18; pub mod equation_50_2; pub mod equation_50_4; pub mod equation_50_6; @@ -21,5 +22,6 @@ pub fn chapter_50(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_50_14::equation_50_14))?; m.add_wrapped(wrap_pymodule!(equation_50_15::equation_50_15))?; m.add_wrapped(wrap_pymodule!(equation_50_16::equation_50_16))?; + m.add_wrapped(wrap_pymodule!(equation_50_18::equation_50_18))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs new file mode 100644 index 00000000..c3362a3b --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -0,0 +1,43 @@ +use pyo3::prelude::*; +use pyo3::wrap_pyfunction; + +use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; + +#[pyfunction] +/// Fractional Effective Dose (FED) calculation for for evaluation exposure to smoke. +/// +/// .. math:: +/// +/// FED = \frac{\sum (C_i \Delta t_i)}{LC_{t50}} +/// +/// where: +/// +/// - :math:`FED` is the fractional effective dose (dimensionless) +/// - :math:`C_i` is the mass concentration of material burned at the end of time interval i (g/m³) +/// - :math:`\Delta t_i` is the time interval i (s) +/// - :math:`LC_{t50}` is the lethal exposure dose from test data (mg/m³ or ppm) +/// +/// Args: +/// c_i (list[float]): Concentration values at each time interval (g/m³) +/// delta_t_i (list[float]): Time intervals (s) +/// lc_t50 (float): Lethal exposure dose from test data (mg/m³ or ppm) +/// +/// Returns: +/// float: Fractional effective dose (dimensionless) +/// +/// Example: +/// >>> import ofire +/// >>> c_i = [0.001, 0.002, 0.003] +/// >>> delta_t_i = [1.0, 5.0, 1.0] +/// >>> lc_t50 = 10.0 +/// >>> result = ofire.sfpe_handbook.chapter_50.equation_50_18.fed(c_i, delta_t_i, lc_t50) +/// >>> print(f"{result:.2f}") +fn fed(c_i: Vec, delta_t_i: Vec, lc_t50: f64) -> PyResult { + Ok(rust_equation_50_18::fed(c_i, delta_t_i, lc_t50)) +} + +#[pymodule] +pub fn equation_50_18(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(fed, m)?)?; + Ok(()) +} \ No newline at end of file From a088e07e780e3fa9f01e2c5edfea41ce75808910 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Sun, 11 Jan 2026 20:02:25 +0000 Subject: [PATCH 06/21] Finishes 50-18 --- .../src/sfpe_handbook/chapter_50/equation_50_18.rs | 8 +++++++- crates/sfpe_handbook/src/chapter_50/equation_50_18.rs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs index c3362a3b..7d71e034 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -25,10 +25,16 @@ use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; /// Returns: /// float: Fractional effective dose (dimensionless) /// +/// Assumptions: +/// Uniform time intervals. +/// +/// Limitations: +/// Simplest model for evaluating exposure to smoke. +/// /// Example: /// >>> import ofire /// >>> c_i = [0.001, 0.002, 0.003] -/// >>> delta_t_i = [1.0, 5.0, 1.0] +/// >>> delta_t_i = [1.0, 1.0, 1.0] /// >>> lc_t50 = 10.0 /// >>> result = ofire.sfpe_handbook.chapter_50.equation_50_18.fed(c_i, delta_t_i, lc_t50) /// >>> print(f"{result:.2f}") diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs index aba27fa4..8027837a 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs @@ -18,10 +18,10 @@ mod tests { #[test] fn test_fed() { let c_i = vec![0.001, 0.003]; - let delta_t_i = vec![1.0, 2.0]; + let delta_t_i = vec![1.0, 1.0]; let lc_t50 = 0.015; let result = fed(c_i, delta_t_i, lc_t50); - let expected = 0.466666667; + let expected = 0.266666667; assert!((result - expected).abs() < 1e-6); } } From 4e648df11506d54c3ca0302faace9ae1969d989b Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Tue, 13 Jan 2026 20:45:13 +0000 Subject: [PATCH 07/21] Update crates/fire_dynamics_tools/Cargo.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- crates/fire_dynamics_tools/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/fire_dynamics_tools/Cargo.toml b/crates/fire_dynamics_tools/Cargo.toml index 6fdbe9e8..a5f5fcc6 100644 --- a/crates/fire_dynamics_tools/Cargo.toml +++ b/crates/fire_dynamics_tools/Cargo.toml @@ -3,9 +3,5 @@ name = "fire_dynamics_tools" version = "0.1.0" edition = "2024" -[dependencies] -sfpe_handbook = { path = "../sfpe_handbook" } -cibse_guide_e = { path = "../cibse_guide_e" } - [lints] workspace = true \ No newline at end of file From d7e2113f729c359771e841b53c7f4894df314328 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 15 Jan 2026 11:35:57 +0000 Subject: [PATCH 08/21] Implements 50-19 --- Cargo.lock | 4 -- crates/python_api/docs/api/sfpe-handbook.rst | 8 ++++ .../src/sfpe_handbook/chapter_50.rs | 2 + .../chapter_50/equation_50_18.rs | 2 +- .../chapter_50/equation_50_19.rs | 46 +++++++++++++++++++ .../src/chapter_50/equation_50_18.rs | 12 ++--- .../src/chapter_50/equation_50_19.rs | 27 +++++------ 7 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs diff --git a/Cargo.lock b/Cargo.lock index e60462c7..50349c22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,10 +83,6 @@ version = "0.1.0" [[package]] name = "fire_dynamics_tools" version = "0.1.0" -dependencies = [ - "cibse_guide_e", - "sfpe_handbook", -] [[package]] name = "generic-array" diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index 8529b98c..3e5d1327 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -112,6 +112,14 @@ Equation 50.18 - Fractional Effective Dose ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_50.equation_50_18 + :members: + :undoc-members: + :show-inheritance: + +Equation 50.19 - Visibility Through Uniform Smoke +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_50.equation_50_19 :members: :undoc-members: :show-inheritance: \ No newline at end of file diff --git a/crates/python_api/src/sfpe_handbook/chapter_50.rs b/crates/python_api/src/sfpe_handbook/chapter_50.rs index 0091fa58..98dcbacd 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50.rs @@ -3,6 +3,7 @@ pub mod equation_50_14; pub mod equation_50_15; pub mod equation_50_16; pub mod equation_50_18; +pub mod equation_50_19; pub mod equation_50_2; pub mod equation_50_4; pub mod equation_50_6; @@ -23,5 +24,6 @@ pub fn chapter_50(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_50_15::equation_50_15))?; m.add_wrapped(wrap_pymodule!(equation_50_16::equation_50_16))?; m.add_wrapped(wrap_pymodule!(equation_50_18::equation_50_18))?; + m.add_wrapped(wrap_pymodule!(equation_50_19::equation_50_19))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs index 7d71e034..6aa75e5f 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -38,7 +38,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; /// >>> lc_t50 = 10.0 /// >>> result = ofire.sfpe_handbook.chapter_50.equation_50_18.fed(c_i, delta_t_i, lc_t50) /// >>> print(f"{result:.2f}") -fn fed(c_i: Vec, delta_t_i: Vec, lc_t50: f64) -> PyResult { +fn fed(c_i: Vec, delta_t_i: f64, lc_t50: f64) -> PyResult { Ok(rust_equation_50_18::fed(c_i, delta_t_i, lc_t50)) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs new file mode 100644 index 00000000..22634713 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs @@ -0,0 +1,46 @@ +use pyo3::prelude::*; +use pyo3::wrap_pyfunction; + +use openfire::sfpe_handbook::chapter_50::equation_50_19 as rust_equation_50_19; + +#[pyfunction] +/// Visibility in smoke at a point where mass concentration of fuel burned is known. +/// +/// .. math:: +/// +/// S_i = \frac{K}{2.303 \times \delta m \times C_i} +/// +/// where: +/// +/// - :math:`S_i` is the visibility through smoke (m) +/// - :math:`K` is the Bouguer-Beer law constant (dimensionless) +/// - :math:`\delta m` is the mass extinction coefficient (m²/g) +/// - :math:`C_i` is the mass concentration of particulates (g/m³) +/// +/// Args: +/// k (float): Bouguer-Beer law constant (dimensionless) +/// delta_m (float): Mass extinction coefficient (m²/g) +/// c_i (float): Mass concentration of particulates (g/m³) +/// +/// Returns: +/// float: Visibility through smoke (m) +/// +/// Assumptions: +/// The calculated visibility can be thought of as visibility if smoke is uniform. +/// +/// Limitations: +/// See assumptions above. Assumes uniform smoke and, utilises the proportionality constant K, commonly taken as 8 for illumnated signs and 3 for non-illuminated. +/// +/// Example: +/// >>> import ofire +/// >>> result = ofire.sfpe_handbook.chapter_50.equation_50_19.visibility(8.0, 0.22, 1.0) +/// >>> print(f"{result:.2f}") +fn visibility(k: f64, delta_m: f64, c_i: f64) -> PyResult { + Ok(rust_equation_50_19::visibility(k, delta_m, c_i)) +} + +#[pymodule] +pub fn equation_50_19(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(visibility, m)?)?; + Ok(()) +} \ No newline at end of file diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs index 8027837a..f4899fdd 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_18.rs @@ -1,13 +1,13 @@ -pub fn fed(c_i: Vec, delta_t_i: Vec, lc_t50: f64) -> f64 { - let numerator: f64 = c_i.iter().zip(delta_t_i.iter()).map(|(c, dt)| c * dt).sum(); +pub fn fed(c_i: Vec, delta_t: f64, lc_t50: f64) -> f64 { + let numerator: f64 = c_i.iter().map(|c| c * delta_t).sum(); numerator / lc_t50 } #[cfg(not(coverage))] -pub fn fed_equation(fed: String, c_i: String, delta_t_i: String, lc_t50: String) -> String { +pub fn fed_equation(fed: String, c_i: String, delta_t: String, lc_t50: String) -> String { format!( "{} = \\frac{{ \\sum {} \\times {} }}{{ {} }}", - fed, c_i, delta_t_i, lc_t50 + fed, c_i, delta_t, lc_t50 ) } @@ -18,9 +18,9 @@ mod tests { #[test] fn test_fed() { let c_i = vec![0.001, 0.003]; - let delta_t_i = vec![1.0, 1.0]; + let delta_t = 1.0; let lc_t50 = 0.015; - let result = fed(c_i, delta_t_i, lc_t50); + let result = fed(c_i, delta_t, lc_t50); let expected = 0.266666667; assert!((result - expected).abs() < 1e-6); } diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs index 7f839bed..3818d81d 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs @@ -1,20 +1,17 @@ -pub fn factor(a_sb: f64, a_bo: f64, t_b: f64, t_s: f64) -> f64 { - let numerator = a_sb.powf(2.0) * (t_b + 273.0); - let denominator = a_bo.powf(2.0) * (t_s + 273.0); - 1.0 + numerator / denominator +pub fn visibility(k: f64, delta_m: f64, c_i: f64) -> f64 { + k / (2.303 * delta_m * c_i) } #[cfg(not(coverage))] -pub fn factor_equation( - f_r: String, - a_sb: String, - t_b: String, - a_bo: String, - t_s: String, +pub fn visibility_equation( + s_i: String, + k: String, + delta_m: String, + c_i: String, ) -> String { format!( - "{} = 1 + \\frac{{ {}^2 \\times ( {} + 273 ) }}{{ {}^2 \\times ( {} + 273 ) }}", - f_r, a_sb, t_b, a_bo, t_s + "{} = \\frac{{{}}}{{2.303 \\times {} \\times {}}}", + s_i, k, delta_m, c_i, ) } @@ -23,9 +20,9 @@ mod tests { use super::*; #[test] - fn test_factor() { - let result = factor(0.005, 0.005, 15.0, 5.0); - let expected = 2.035971223; + fn test_visibility() { + let result = visibility(8.0, 0.22, 1.0); + let expected = 15.78968144; assert!((result - expected).abs() < 1e-6); } } From aabe1005a606d9147192ce54335b3c01f1f61a16 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 15 Jan 2026 11:50:00 +0000 Subject: [PATCH 09/21] Start of 50-20 --- .../src/chapter_50/equation_50_20.rs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs index 7f839bed..3818d81d 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs @@ -1,20 +1,17 @@ -pub fn factor(a_sb: f64, a_bo: f64, t_b: f64, t_s: f64) -> f64 { - let numerator = a_sb.powf(2.0) * (t_b + 273.0); - let denominator = a_bo.powf(2.0) * (t_s + 273.0); - 1.0 + numerator / denominator +pub fn visibility(k: f64, delta_m: f64, c_i: f64) -> f64 { + k / (2.303 * delta_m * c_i) } #[cfg(not(coverage))] -pub fn factor_equation( - f_r: String, - a_sb: String, - t_b: String, - a_bo: String, - t_s: String, +pub fn visibility_equation( + s_i: String, + k: String, + delta_m: String, + c_i: String, ) -> String { format!( - "{} = 1 + \\frac{{ {}^2 \\times ( {} + 273 ) }}{{ {}^2 \\times ( {} + 273 ) }}", - f_r, a_sb, t_b, a_bo, t_s + "{} = \\frac{{{}}}{{2.303 \\times {} \\times {}}}", + s_i, k, delta_m, c_i, ) } @@ -23,9 +20,9 @@ mod tests { use super::*; #[test] - fn test_factor() { - let result = factor(0.005, 0.005, 15.0, 5.0); - let expected = 2.035971223; + fn test_visibility() { + let result = visibility(8.0, 0.22, 1.0); + let expected = 15.78968144; assert!((result - expected).abs() < 1e-6); } } From f5d17cd197f1d5de14ffe1b9096b57d256a0a0e1 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 15 Jan 2026 12:19:25 +0000 Subject: [PATCH 10/21] Implements rust aspect of 50-20 --- .../chapter_50/equation_50_18.rs | 2 +- .../chapter_50/equation_50_19.rs | 2 +- .../src/chapter_50/equation_50_19.rs | 9 ++------- .../src/chapter_50/equation_50_20.rs | 19 +++++++------------ 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs index 6aa75e5f..05ad3e00 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -46,4 +46,4 @@ fn fed(c_i: Vec, delta_t_i: f64, lc_t50: f64) -> PyResult { pub fn equation_50_18(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(fed, m)?)?; Ok(()) -} \ No newline at end of file +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs index 22634713..775deb03 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs @@ -43,4 +43,4 @@ fn visibility(k: f64, delta_m: f64, c_i: f64) -> PyResult { pub fn equation_50_19(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(visibility, m)?)?; Ok(()) -} \ No newline at end of file +} diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs index 3818d81d..142fc6fb 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_19.rs @@ -3,15 +3,10 @@ pub fn visibility(k: f64, delta_m: f64, c_i: f64) -> f64 { } #[cfg(not(coverage))] -pub fn visibility_equation( - s_i: String, - k: String, - delta_m: String, - c_i: String, -) -> String { +pub fn visibility_equation(s_i: String, k: String, delta_m: String, c_i: String) -> String { format!( "{} = \\frac{{{}}}{{2.303 \\times {} \\times {}}}", - s_i, k, delta_m, c_i, + s_i, k, delta_m, c_i, ) } diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs index 3818d81d..2ce827e9 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs @@ -1,17 +1,12 @@ -pub fn visibility(k: f64, delta_m: f64, c_i: f64) -> f64 { - k / (2.303 * delta_m * c_i) +pub fn visibility(k: f64, l: f64, lambda: f64) -> f64 { + -k * l / ((1.0 - lambda / 100.0).ln()) } #[cfg(not(coverage))] -pub fn visibility_equation( - s_i: String, - k: String, - delta_m: String, - c_i: String, -) -> String { +pub fn visibility_equation(s_i: String, k: String, l: String, lambda: String) -> String { format!( - "{} = \\frac{{{}}}{{2.303 \\times {} \\times {}}}", - s_i, k, delta_m, c_i, + "{} = \\frac{{{}}}{{\\ln(1 - \\frac{{{}}}{{100}})}}", + s_i, k, lambda, ) } @@ -21,8 +16,8 @@ mod tests { #[test] fn test_visibility() { - let result = visibility(8.0, 0.22, 1.0); - let expected = 15.78968144; + let result = visibility(8.0, 10.0, 95.0); + let expected = 26.70465606; assert!((result - expected).abs() < 1e-6); } } From 8b3f07d8425c3f56a1a24ceefe6ad4b4fb72d9b5 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 15 Jan 2026 12:25:30 +0000 Subject: [PATCH 11/21] Finishes first implementation of 50-20 --- crates/python_api/docs/api/sfpe-handbook.rst | 7 +++ .../src/sfpe_handbook/chapter_50.rs | 2 + .../chapter_50/equation_50_20.rs | 46 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index 3e5d1327..fbb9437d 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -120,6 +120,13 @@ Equation 50.19 - Visibility Through Uniform Smoke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_50.equation_50_19 + :members: + :undoc-members: + :show-inheritance: +Equation 50.20 - Visibility Through Smoke (Percent Obscuration) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_50.equation_50_20 :members: :undoc-members: :show-inheritance: \ No newline at end of file diff --git a/crates/python_api/src/sfpe_handbook/chapter_50.rs b/crates/python_api/src/sfpe_handbook/chapter_50.rs index 98dcbacd..9d0a733c 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50.rs @@ -5,6 +5,7 @@ pub mod equation_50_16; pub mod equation_50_18; pub mod equation_50_19; pub mod equation_50_2; +pub mod equation_50_20; pub mod equation_50_4; pub mod equation_50_6; pub mod equation_50_7; @@ -25,5 +26,6 @@ pub fn chapter_50(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_50_16::equation_50_16))?; m.add_wrapped(wrap_pymodule!(equation_50_18::equation_50_18))?; m.add_wrapped(wrap_pymodule!(equation_50_19::equation_50_19))?; + m.add_wrapped(wrap_pymodule!(equation_50_20::equation_50_20))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs new file mode 100644 index 00000000..1216d0ab --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs @@ -0,0 +1,46 @@ +use pyo3::prelude::*; +use pyo3::wrap_pyfunction; + +use openfire::sfpe_handbook::chapter_50::equation_50_20 as rust_equation_50_20; + +#[pyfunction] +/// Visibility calculation through smoke from percent obscuration. +/// +/// .. math:: +/// +/// S_i = -\frac{K \times L}{\ln(1 - \frac{\lambda}{100})} +/// +/// where: +/// +/// - :math:`S_i` is the visibility through smoke (m) +/// - :math:`K` is proportionality constant (dimensionless) +/// - :math:`L` is the path length (m) +/// - :math:`\lambda` is the percent obscuration (dimensionless) +/// +/// Args: +/// k (float): Proportionality constant (dimensionless) +/// l (float): Path length (m) +/// lambda (float): Percent obscuration (dimensionless) +/// +/// Returns: +/// float: Visibility through smoke (m) +/// +/// Assumptions: +/// An object can be seen for S > L. +/// +/// Limitations: +/// None stated. +/// +/// Example: +/// >>> import ofire +/// >>> result = ofire.sfpe_handbook.chapter_50.equation_50_20.visibility(8.0, 10.0, 95.0) +/// >>> print(f"{result:.2f}") +fn visibility(k: f64, l: f64, lambda: f64) -> PyResult { + Ok(rust_equation_50_20::visibility(k, l, lambda)) +} + +#[pymodule] +pub fn equation_50_20(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(visibility, m)?)?; + Ok(()) +} \ No newline at end of file From 61e27a9e24e5057b7776d1bfd98b605ad51f47cb Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 15 Jan 2026 12:31:45 +0000 Subject: [PATCH 12/21] Minor changes to fix issues with docs --- .../src/sfpe_handbook/chapter_50/equation_50_18.rs | 4 ++-- .../src/sfpe_handbook/chapter_50/equation_50_19.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs index 05ad3e00..a022cb3e 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -4,7 +4,7 @@ use pyo3::wrap_pyfunction; use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; #[pyfunction] -/// Fractional Effective Dose (FED) calculation for for evaluation exposure to smoke. +/// Fractional Effective Dose (FED) calculation for evaluation of exposure to smoke. /// /// .. math:: /// @@ -19,7 +19,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; /// /// Args: /// c_i (list[float]): Concentration values at each time interval (g/m³) -/// delta_t_i (list[float]): Time intervals (s) +/// delta_t_i (float): Time intervals (s) /// lc_t50 (float): Lethal exposure dose from test data (mg/m³ or ppm) /// /// Returns: diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs index 775deb03..b3fe9081 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs @@ -13,14 +13,14 @@ use openfire::sfpe_handbook::chapter_50::equation_50_19 as rust_equation_50_19; /// where: /// /// - :math:`S_i` is the visibility through smoke (m) -/// - :math:`K` is the Bouguer-Beer law constant (dimensionless) -/// - :math:`\delta m` is the mass extinction coefficient (m²/g) -/// - :math:`C_i` is the mass concentration of particulates (g/m³) +/// - :math:`K` is the proportionality constant (dimensionless) +/// - :math:`\delta m` is the mass optical density (m²/g) +/// - :math:`C_i` is the mass concentration of fuel burned (g/m³) /// /// Args: -/// k (float): Bouguer-Beer law constant (dimensionless) -/// delta_m (float): Mass extinction coefficient (m²/g) -/// c_i (float): Mass concentration of particulates (g/m³) +/// k (float): Proportionality constant (dimensionless) +/// delta_m (float): Mass optical density (m²/g) +/// c_i (float): Mass concentration of fuel burned (g/m³) /// /// Returns: /// float: Visibility through smoke (m) From d2d6e926b981e4444ce041322126fb28c73304fb Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Thu, 15 Jan 2026 12:35:17 +0000 Subject: [PATCH 13/21] Update crates/python_api/docs/api/sfpe-handbook.rst Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- crates/python_api/docs/api/sfpe-handbook.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index fbb9437d..b44b787d 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -124,7 +124,7 @@ Equation 50.19 - Visibility Through Uniform Smoke :undoc-members: :show-inheritance: Equation 50.20 - Visibility Through Smoke (Percent Obscuration) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_50.equation_50_20 :members: From 3d9423b6ce49b394be20c80879084a0348c82097 Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Thu, 15 Jan 2026 12:41:00 +0000 Subject: [PATCH 14/21] Update crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs index b3fe9081..e008a9f2 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs @@ -29,7 +29,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_19 as rust_equation_50_19; /// The calculated visibility can be thought of as visibility if smoke is uniform. /// /// Limitations: -/// See assumptions above. Assumes uniform smoke and, utilises the proportionality constant K, commonly taken as 8 for illumnated signs and 3 for non-illuminated. +/// See assumptions above. Assumes uniform smoke and, utilises the proportionality constant K, commonly taken as 8 for illuminated signs and 3 for non-illuminated. /// /// Example: /// >>> import ofire From 68205018bf0360d65e1abca504bcc3e36006ff22 Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Thu, 15 Jan 2026 12:41:43 +0000 Subject: [PATCH 15/21] Update crates/sfpe_handbook/src/chapter_50/equation_50_20.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- crates/sfpe_handbook/src/chapter_50/equation_50_20.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs index 2ce827e9..b9ff493e 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_20.rs @@ -5,8 +5,8 @@ pub fn visibility(k: f64, l: f64, lambda: f64) -> f64 { #[cfg(not(coverage))] pub fn visibility_equation(s_i: String, k: String, l: String, lambda: String) -> String { format!( - "{} = \\frac{{{}}}{{\\ln(1 - \\frac{{{}}}{{100}})}}", - s_i, k, lambda, + "{} = \\frac{{{} \\times {}}}{{\\ln(1 - \\frac{{{}}}{{100}})}}", + s_i, k, l, lambda, ) } From cf1b2edb64eb5e4c9b6f4a23bfd246e40b72255a Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 15 Jan 2026 12:43:14 +0000 Subject: [PATCH 16/21] fixes error in example --- .../python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs index a022cb3e..56c21703 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -34,7 +34,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; /// Example: /// >>> import ofire /// >>> c_i = [0.001, 0.002, 0.003] -/// >>> delta_t_i = [1.0, 1.0, 1.0] +/// >>> delta_t_i = 1.0 /// >>> lc_t50 = 10.0 /// >>> result = ofire.sfpe_handbook.chapter_50.equation_50_18.fed(c_i, delta_t_i, lc_t50) /// >>> print(f"{result:.2f}") From 58192ee18a4f17c583d90cb5f54acc04885dcbaa Mon Sep 17 00:00:00 2001 From: simonsantama Date: Thu, 15 Jan 2026 19:46:59 +0000 Subject: [PATCH 17/21] Fixes error in units --- .../python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs index 56c21703..225eb2c3 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -15,12 +15,12 @@ use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; /// - :math:`FED` is the fractional effective dose (dimensionless) /// - :math:`C_i` is the mass concentration of material burned at the end of time interval i (g/m³) /// - :math:`\Delta t_i` is the time interval i (s) -/// - :math:`LC_{t50}` is the lethal exposure dose from test data (mg/m³ or ppm) +/// - :math:`LC_{t50}` is the lethal exposure dose from test data (g/m³) /// /// Args: /// c_i (list[float]): Concentration values at each time interval (g/m³) /// delta_t_i (float): Time intervals (s) -/// lc_t50 (float): Lethal exposure dose from test data (mg/m³ or ppm) +/// lc_t50 (float): Lethal exposure dose from test data (g/m³) /// /// Returns: /// float: Fractional effective dose (dimensionless) From c73134b4db0aa9a54aa7bcbd3c32c08a5e36d9f7 Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Thu, 15 Jan 2026 19:47:49 +0000 Subject: [PATCH 18/21] Update crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs Co-authored-by: Jamie Maclean <38719252+JamieMaclean@users.noreply.github.com> --- .../python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs index 225eb2c3..a038d1a8 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_18.rs @@ -8,7 +8,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_18 as rust_equation_50_18; /// /// .. math:: /// -/// FED = \frac{\sum (C_i \Delta t_i)}{LC_{t50}} +/// FED = \frac{\sum (C_i \Delta t_i)}{LCt_{50}} /// /// where: /// From eebb7eb6009a51d3a0dbc66682da1b5898ce6986 Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Thu, 15 Jan 2026 19:48:50 +0000 Subject: [PATCH 19/21] Update crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs Co-authored-by: Jamie Maclean <38719252+JamieMaclean@users.noreply.github.com> --- .../python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs index e008a9f2..1c19d883 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs @@ -14,7 +14,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_19 as rust_equation_50_19; /// /// - :math:`S_i` is the visibility through smoke (m) /// - :math:`K` is the proportionality constant (dimensionless) -/// - :math:`\delta m` is the mass optical density (m²/g) +/// - :math:`{\delta}_m` is the mass optical density (m²/g) /// - :math:`C_i` is the mass concentration of fuel burned (g/m³) /// /// Args: From 53dcb05834fcab824a4bb84ca4544cad98a8c78a Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Thu, 15 Jan 2026 19:49:06 +0000 Subject: [PATCH 20/21] Update crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs Co-authored-by: Jamie Maclean <38719252+JamieMaclean@users.noreply.github.com> --- .../python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs index 1c19d883..8c06d637 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_19.rs @@ -8,7 +8,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_19 as rust_equation_50_19; /// /// .. math:: /// -/// S_i = \frac{K}{2.303 \times \delta m \times C_i} +/// S_i = \frac{K}{2.303 {\delta}_m C_i} /// /// where: /// From 3d9535fbd4fb605685dcc33ab43d79af114e3dbb Mon Sep 17 00:00:00 2001 From: Simon Santamaria Date: Thu, 15 Jan 2026 19:49:27 +0000 Subject: [PATCH 21/21] Update crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs Co-authored-by: Jamie Maclean <38719252+JamieMaclean@users.noreply.github.com> --- .../python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs index 1216d0ab..2f2c6ba0 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_20.rs @@ -8,7 +8,7 @@ use openfire::sfpe_handbook::chapter_50::equation_50_20 as rust_equation_50_20; /// /// .. math:: /// -/// S_i = -\frac{K \times L}{\ln(1 - \frac{\lambda}{100})} +/// S_i = -\frac{K L}{\ln(1 - \frac{\lambda}{100})} /// /// where: ///