From 7484d444447f820927eb3a060f0461c18df14169 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 11 Mar 2026 20:33:24 +0000 Subject: [PATCH 01/11] Adds 59.5 of sfpe --- crates/python_api/docs/api/sfpe-handbook.rst | 66 ++++++++++++++++++- crates/python_api/src/sfpe_handbook.rs | 2 + .../chapter_50/equation_50_17.rs | 2 +- .../chapter_50/equation_50_20.rs | 2 +- .../src/sfpe_handbook/chapter_59.rs | 25 +++++++ .../chapter_59/equation_59_10.rs | 36 ++++++++++ .../chapter_59/equation_59_11.rs | 36 ++++++++++ .../sfpe_handbook/chapter_59/equation_59_5.rs | 39 +++++++++++ .../sfpe_handbook/chapter_59/equation_59_6.rs | 36 ++++++++++ .../sfpe_handbook/chapter_59/equation_59_7.rs | 36 ++++++++++ .../sfpe_handbook/chapter_59/equation_59_8.rs | 36 ++++++++++ .../sfpe_handbook/chapter_59/equation_59_9.rs | 36 ++++++++++ .../src/chapter_50/equation_50_16.rs | 2 +- .../src/chapter_50/equation_50_17.rs | 7 +- crates/sfpe_handbook/src/chapter_59.rs | 7 ++ .../src/chapter_59/equation_59_10.rs | 22 +++++++ .../src/chapter_59/equation_59_11.rs | 22 +++++++ .../src/chapter_59/equation_59_5.rs | 30 +++++++++ .../src/chapter_59/equation_59_6.rs | 22 +++++++ .../src/chapter_59/equation_59_7.rs | 22 +++++++ .../src/chapter_59/equation_59_8.rs | 22 +++++++ .../src/chapter_59/equation_59_9.rs | 22 +++++++ crates/sfpe_handbook/src/lib.rs | 1 + 23 files changed, 526 insertions(+), 5 deletions(-) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59.rs create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_5.rs create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs create mode 100644 crates/sfpe_handbook/src/chapter_59.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_10.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_11.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_5.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_6.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_7.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_8.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_9.rs diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index 8253ffd9..dbeb1d97 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -137,4 +137,68 @@ 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 + :show-inheritance: + +Chapter 59 +---------- + +.. automodule:: ofire.sfpe_handbook.chapter_59 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.5 +~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_5 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.6 +~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_6 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.7 +~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_7 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.8 +~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_8 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.9 +~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_9 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.10 +~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_10 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.11 +~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_11 + :members: + :undoc-members: + :show-inheritance: diff --git a/crates/python_api/src/sfpe_handbook.rs b/crates/python_api/src/sfpe_handbook.rs index e7819f46..f8cf8474 100644 --- a/crates/python_api/src/sfpe_handbook.rs +++ b/crates/python_api/src/sfpe_handbook.rs @@ -1,5 +1,6 @@ pub mod chapter_14; pub mod chapter_50; +pub mod chapter_59; use pyo3::prelude::*; use pyo3::wrap_pymodule; @@ -14,5 +15,6 @@ use pyo3::wrap_pymodule; pub fn sfpe_handbook(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(chapter_14::chapter_14))?; m.add_wrapped(wrap_pymodule!(chapter_50::chapter_50))?; + m.add_wrapped(wrap_pymodule!(chapter_59::chapter_59))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_17.rs b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_17.rs index 3a72c4fa..3fe71356 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_17.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_50/equation_50_17.rs @@ -43,4 +43,4 @@ fn stairwell_temperature(t_0: f64, eta: f64, t_b: f64) -> PyResult { pub fn equation_50_17(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(stairwell_temperature, m)?)?; Ok(()) -} \ No newline at end of file +} 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 2f2c6ba0..f28b274a 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 @@ -43,4 +43,4 @@ fn visibility(k: f64, l: f64, lambda: f64) -> PyResult { pub fn equation_50_20(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(visibility, m)?)?; Ok(()) -} \ No newline at end of file +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59.rs b/crates/python_api/src/sfpe_handbook/chapter_59.rs new file mode 100644 index 00000000..c09cd0e1 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59.rs @@ -0,0 +1,25 @@ +pub mod equation_59_10; +pub mod equation_59_11; +pub mod equation_59_5; +pub mod equation_59_6; +pub mod equation_59_7; +pub mod equation_59_8; +pub mod equation_59_9; + +use pyo3::prelude::*; +use pyo3::wrap_pymodule; + +#[pymodule] +/// Chapter 59 - Placeholder equations. +/// +/// This chapter contains placeholder equations that will be implemented. +pub fn chapter_59(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_wrapped(wrap_pymodule!(equation_59_5::equation_59_5))?; + m.add_wrapped(wrap_pymodule!(equation_59_6::equation_59_6))?; + m.add_wrapped(wrap_pymodule!(equation_59_7::equation_59_7))?; + m.add_wrapped(wrap_pymodule!(equation_59_8::equation_59_8))?; + m.add_wrapped(wrap_pymodule!(equation_59_9::equation_59_9))?; + m.add_wrapped(wrap_pymodule!(equation_59_10::equation_59_10))?; + m.add_wrapped(wrap_pymodule!(equation_59_11::equation_59_11))?; + Ok(()) +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs new file mode 100644 index 00000000..a5ef16c7 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs @@ -0,0 +1,36 @@ +use pyo3::prelude::*; + +#[pyfunction] +/// Placeholder for equation 59.10. +/// +/// This function is a placeholder and will be implemented with the actual +/// equation logic. +/// +/// .. math:: +/// +/// \text{TODO: Add equation} +/// +/// where: +/// +/// - TODO: Add variable definitions +/// +/// Args: +/// TODO: Add arguments +/// +/// Returns: +/// float: TODO: Add return description +/// +/// Example: +/// >>> import ofire +/// >>> # TODO: Add example when equation is implemented +fn equation_59_10_placeholder() -> PyResult { + Err(pyo3::exceptions::PyNotImplementedError::new_err( + "Equation 59.10 is not yet implemented", + )) +} + +#[pymodule] +pub fn equation_59_10(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(equation_59_10_placeholder, m)?)?; + Ok(()) +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs new file mode 100644 index 00000000..f8c4b3be --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs @@ -0,0 +1,36 @@ +use pyo3::prelude::*; + +#[pyfunction] +/// Placeholder for equation 59.11. +/// +/// This function is a placeholder and will be implemented with the actual +/// equation logic. +/// +/// .. math:: +/// +/// \text{TODO: Add equation} +/// +/// where: +/// +/// - TODO: Add variable definitions +/// +/// Args: +/// TODO: Add arguments +/// +/// Returns: +/// float: TODO: Add return description +/// +/// Example: +/// >>> import ofire +/// >>> # TODO: Add example when equation is implemented +fn equation_59_11_placeholder() -> PyResult { + Err(pyo3::exceptions::PyNotImplementedError::new_err( + "Equation 59.11 is not yet implemented", + )) +} + +#[pymodule] +pub fn equation_59_11(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(equation_59_11_placeholder, m)?)?; + Ok(()) +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_5.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_5.rs new file mode 100644 index 00000000..fee6ca21 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_5.rs @@ -0,0 +1,39 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_5 as rust_equation_59_5; + +#[pyfunction] +/// Calculates pedestrian travel speed along a path (Equation 59.5). +/// +/// .. math:: +/// +/// S = k - akD +/// +/// where: +/// +/// - :math:`S` is the speed along the line of travel +/// - :math:`k` is the speed constant from Table 59.2 +/// - :math:`a` is the unit conversion constant (0.266 for SI, 2.86 for imperial) +/// - :math:`D` is the population density in persons per unit area +/// +/// Args: +/// k (float): Speed constant from Table 59.2 +/// a (float): Unit conversion constant (0.266 for SI, 2.86 for imperial) +/// d (float): Population density (persons per unit area) +/// +/// Returns: +/// float: Speed S along the line of travel +/// +/// Example: +/// >>> import ofire +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_5.travel_speed(1.19, 0.266, 0.5) +/// >>> print(f"{result:.3f} m/s") +fn travel_speed(k: f64, a: f64, d: f64) -> PyResult { + Ok(rust_equation_59_5::travel_speed(k, a, d)) +} + +#[pymodule] +pub fn equation_59_5(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(travel_speed, m)?)?; + Ok(()) +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs new file mode 100644 index 00000000..e4fecaf5 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs @@ -0,0 +1,36 @@ +use pyo3::prelude::*; + +#[pyfunction] +/// Placeholder for equation 59.6. +/// +/// This function is a placeholder and will be implemented with the actual +/// equation logic. +/// +/// .. math:: +/// +/// \text{TODO: Add equation} +/// +/// where: +/// +/// - TODO: Add variable definitions +/// +/// Args: +/// TODO: Add arguments +/// +/// Returns: +/// float: TODO: Add return description +/// +/// Example: +/// >>> import ofire +/// >>> # TODO: Add example when equation is implemented +fn equation_59_6_placeholder() -> PyResult { + Err(pyo3::exceptions::PyNotImplementedError::new_err( + "Equation 59.6 is not yet implemented", + )) +} + +#[pymodule] +pub fn equation_59_6(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(equation_59_6_placeholder, m)?)?; + Ok(()) +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs new file mode 100644 index 00000000..fe991aee --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs @@ -0,0 +1,36 @@ +use pyo3::prelude::*; + +#[pyfunction] +/// Placeholder for equation 59.7. +/// +/// This function is a placeholder and will be implemented with the actual +/// equation logic. +/// +/// .. math:: +/// +/// \text{TODO: Add equation} +/// +/// where: +/// +/// - TODO: Add variable definitions +/// +/// Args: +/// TODO: Add arguments +/// +/// Returns: +/// float: TODO: Add return description +/// +/// Example: +/// >>> import ofire +/// >>> # TODO: Add example when equation is implemented +fn equation_59_7_placeholder() -> PyResult { + Err(pyo3::exceptions::PyNotImplementedError::new_err( + "Equation 59.7 is not yet implemented", + )) +} + +#[pymodule] +pub fn equation_59_7(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(equation_59_7_placeholder, m)?)?; + Ok(()) +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs new file mode 100644 index 00000000..4c86d59a --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs @@ -0,0 +1,36 @@ +use pyo3::prelude::*; + +#[pyfunction] +/// Placeholder for equation 59.8. +/// +/// This function is a placeholder and will be implemented with the actual +/// equation logic. +/// +/// .. math:: +/// +/// \text{TODO: Add equation} +/// +/// where: +/// +/// - TODO: Add variable definitions +/// +/// Args: +/// TODO: Add arguments +/// +/// Returns: +/// float: TODO: Add return description +/// +/// Example: +/// >>> import ofire +/// >>> # TODO: Add example when equation is implemented +fn equation_59_8_placeholder() -> PyResult { + Err(pyo3::exceptions::PyNotImplementedError::new_err( + "Equation 59.8 is not yet implemented", + )) +} + +#[pymodule] +pub fn equation_59_8(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(equation_59_8_placeholder, m)?)?; + Ok(()) +} diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs new file mode 100644 index 00000000..86843347 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs @@ -0,0 +1,36 @@ +use pyo3::prelude::*; + +#[pyfunction] +/// Placeholder for equation 59.9. +/// +/// This function is a placeholder and will be implemented with the actual +/// equation logic. +/// +/// .. math:: +/// +/// \text{TODO: Add equation} +/// +/// where: +/// +/// - TODO: Add variable definitions +/// +/// Args: +/// TODO: Add arguments +/// +/// Returns: +/// float: TODO: Add return description +/// +/// Example: +/// >>> import ofire +/// >>> # TODO: Add example when equation is implemented +fn equation_59_9_placeholder() -> PyResult { + Err(pyo3::exceptions::PyNotImplementedError::new_err( + "Equation 59.9 is not yet implemented", + )) +} + +#[pymodule] +pub fn equation_59_9(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(equation_59_9_placeholder, m)?)?; + Ok(()) +} diff --git a/crates/sfpe_handbook/src/chapter_50/equation_50_16.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_16.rs index 8ca1c1c5..5ee7ebf1 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_16.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_16.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_17.rs b/crates/sfpe_handbook/src/chapter_50/equation_50_17.rs index 79c8523e..3b58b4dd 100644 --- a/crates/sfpe_handbook/src/chapter_50/equation_50_17.rs +++ b/crates/sfpe_handbook/src/chapter_50/equation_50_17.rs @@ -3,7 +3,12 @@ pub fn stairwell_temperature(t_0: f64, eta: f64, t_b: f64) -> f64 { } #[cfg(not(coverage))] -pub fn stairwell_temperature_equation(t_s: String, t_0: String, eta: String, t_b: String) -> String { +pub fn stairwell_temperature_equation( + t_s: String, + t_0: String, + eta: String, + t_b: String, +) -> String { format!("{} = {} + {} \\times ( {} - {} )", t_s, t_0, eta, t_b, t_0) } diff --git a/crates/sfpe_handbook/src/chapter_59.rs b/crates/sfpe_handbook/src/chapter_59.rs new file mode 100644 index 00000000..ee9a40c2 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59.rs @@ -0,0 +1,7 @@ +pub mod equation_59_10; +pub mod equation_59_11; +pub mod equation_59_5; +pub mod equation_59_6; +pub mod equation_59_7; +pub mod equation_59_8; +pub mod equation_59_9; diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs new file mode 100644 index 00000000..7d00921e --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs @@ -0,0 +1,22 @@ +/// Placeholder for equation 59.10 calculation. +/// +/// TODO: Implement the actual equation logic. +pub fn equation_59_10_placeholder() -> f64 { + todo!("Implement equation 59.10") +} + +#[cfg(not(coverage))] +pub fn equation_59_10_equation() -> &'static str { + "TODO: Add equation string" +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "placeholder - implement when equation is added"] + fn test_equation_59_10() { + // TODO: Add test when equation is implemented + } +} diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs new file mode 100644 index 00000000..3cb8ba61 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs @@ -0,0 +1,22 @@ +/// Placeholder for equation 59.11 calculation. +/// +/// TODO: Implement the actual equation logic. +pub fn equation_59_11_placeholder() -> f64 { + todo!("Implement equation 59.11") +} + +#[cfg(not(coverage))] +pub fn equation_59_11_equation() -> &'static str { + "TODO: Add equation string" +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "placeholder - implement when equation is added"] + fn test_equation_59_11() { + // TODO: Add test when equation is implemented + } +} diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_5.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_5.rs new file mode 100644 index 00000000..2e063f94 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_5.rs @@ -0,0 +1,30 @@ +/// Calculates pedestrian travel speed along a path (Equation 59.5). +/// +/// # Arguments +/// +/// * `k` - Speed constant from Table 59.2 (units depend on unit system) +/// * `a` - Unit conversion constant (0.266 for SI, 2.86 for imperial) +/// * `d` - Population density in persons per unit area +/// +/// # Returns +/// +/// Speed S along the line of travel +pub fn travel_speed(k: f64, a: f64, d: f64) -> f64 { + k - a * k * d +} + +#[cfg(not(coverage))] +pub fn travel_speed_equation(s: String, k: String, a: String, d: String) -> String { + format!("{} = {} - {} \\cdot {} \\cdot {}", s, k, a, k, d) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_travel_speed_generic() { + let result = travel_speed(1.4, 0.266, 0.7); + assert!((result - 1.13932).abs() < 1e-6); + } +} diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs new file mode 100644 index 00000000..8c763d0a --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs @@ -0,0 +1,22 @@ +/// Placeholder for equation 59.6 calculation. +/// +/// TODO: Implement the actual equation logic. +pub fn equation_59_6_placeholder() -> f64 { + todo!("Implement equation 59.6") +} + +#[cfg(not(coverage))] +pub fn equation_59_6_equation() -> &'static str { + "TODO: Add equation string" +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "placeholder - implement when equation is added"] + fn test_equation_59_6() { + // TODO: Add test when equation is implemented + } +} diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs new file mode 100644 index 00000000..f3ced8c8 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs @@ -0,0 +1,22 @@ +/// Placeholder for equation 59.7 calculation. +/// +/// TODO: Implement the actual equation logic. +pub fn equation_59_7_placeholder() -> f64 { + todo!("Implement equation 59.7") +} + +#[cfg(not(coverage))] +pub fn equation_59_7_equation() -> &'static str { + "TODO: Add equation string" +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "placeholder - implement when equation is added"] + fn test_equation_59_7() { + // TODO: Add test when equation is implemented + } +} diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs new file mode 100644 index 00000000..a8d03be0 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs @@ -0,0 +1,22 @@ +/// Placeholder for equation 59.8 calculation. +/// +/// TODO: Implement the actual equation logic. +pub fn equation_59_8_placeholder() -> f64 { + todo!("Implement equation 59.8") +} + +#[cfg(not(coverage))] +pub fn equation_59_8_equation() -> &'static str { + "TODO: Add equation string" +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "placeholder - implement when equation is added"] + fn test_equation_59_8() { + // TODO: Add test when equation is implemented + } +} diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs new file mode 100644 index 00000000..43664f29 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs @@ -0,0 +1,22 @@ +/// Placeholder for equation 59.9 calculation. +/// +/// TODO: Implement the actual equation logic. +pub fn equation_59_9_placeholder() -> f64 { + todo!("Implement equation 59.9") +} + +#[cfg(not(coverage))] +pub fn equation_59_9_equation() -> &'static str { + "TODO: Add equation string" +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "placeholder - implement when equation is added"] + fn test_equation_59_9() { + // TODO: Add test when equation is implemented + } +} diff --git a/crates/sfpe_handbook/src/lib.rs b/crates/sfpe_handbook/src/lib.rs index 3ca79850..59930f31 100644 --- a/crates/sfpe_handbook/src/lib.rs +++ b/crates/sfpe_handbook/src/lib.rs @@ -1,2 +1,3 @@ pub mod chapter_14; pub mod chapter_50; +pub mod chapter_59; From c24918ac5d90b5c068497562ac76c10ff5194ab4 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 11 Mar 2026 20:54:08 +0000 Subject: [PATCH 02/11] Adds equation 59.6 --- .../sfpe_handbook/chapter_59/equation_59_6.rs | 30 +++++++++++-------- .../src/chapter_59/equation_59_6.rs | 29 ++++++++++++------ 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs index e4fecaf5..45dd4a0d 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs @@ -1,36 +1,40 @@ use pyo3::prelude::*; +use openfire::sfpe_handbook::chapter_59::equation_59_6 as rust_equation_59_6; + #[pyfunction] -/// Placeholder for equation 59.6. +/// Calculates specific flow of evacuating persons (Equation 59.6). /// -/// This function is a placeholder and will be implemented with the actual -/// equation logic. +/// Specific flow is the flow of evacuating persons past a point in the exit +/// route per unit of time per unit of effective width. /// /// .. math:: /// -/// \text{TODO: Add equation} +/// F_s = S \cdot D /// /// where: /// -/// - TODO: Add variable definitions +/// - :math:`F_s` is the specific flow (persons/min/ft or persons/s/m of effective width) +/// - :math:`S` is the speed along the line of travel (from Equation 59.5) +/// - :math:`D` is the population density (persons per unit area) /// /// Args: -/// TODO: Add arguments +/// s (float): Speed along the line of travel (m/s or ft/min) +/// d (float): Population density (persons/m² or persons/ft²) /// /// Returns: -/// float: TODO: Add return description +/// float: Specific flow Fs (persons/s/m or persons/min/ft of effective width) /// /// Example: /// >>> import ofire -/// >>> # TODO: Add example when equation is implemented -fn equation_59_6_placeholder() -> PyResult { - Err(pyo3::exceptions::PyNotImplementedError::new_err( - "Equation 59.6 is not yet implemented", - )) +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_6.specific_flow(1.0, 0.5) +/// >>> print(f"{result:.2f} persons/s/m") +fn specific_flow(s: f64, d: f64) -> PyResult { + Ok(rust_equation_59_6::specific_flow(s, d)) } #[pymodule] pub fn equation_59_6(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(pyo3::wrap_pyfunction!(equation_59_6_placeholder, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(specific_flow, m)?)?; Ok(()) } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs index 8c763d0a..dd916b0c 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs @@ -1,13 +1,23 @@ -/// Placeholder for equation 59.6 calculation. +/// Calculates specific flow of evacuating persons (Equation 59.6). /// -/// TODO: Implement the actual equation logic. -pub fn equation_59_6_placeholder() -> f64 { - todo!("Implement equation 59.6") +/// Specific flow is the flow of evacuating persons past a point in the exit +/// route per unit of time per unit of effective width. +/// +/// # Arguments +/// +/// * `s` - Speed along the line of travel (from Equation 59.5) +/// * `d` - Population density in persons per unit area +/// +/// # Returns +/// +/// Specific flow Fs (persons/min/ft or persons/s/m of effective width) +pub fn specific_flow(s: f64, d: f64) -> f64 { + s * d } #[cfg(not(coverage))] -pub fn equation_59_6_equation() -> &'static str { - "TODO: Add equation string" +pub fn specific_flow_equation(fs: String, s: String, d: String) -> String { + format!("{} = {} \\cdot {}", fs, s, d) } #[cfg(test)] @@ -15,8 +25,9 @@ mod tests { use super::*; #[test] - #[ignore = "placeholder - implement when equation is added"] - fn test_equation_59_6() { - // TODO: Add test when equation is implemented + fn test_specific_flow() { + // S = 1.0 m/s, D = 0.5 persons/m² -> Fs = 0.5 persons/s/m + let result = specific_flow(1.0, 0.5); + assert!((result - 0.5).abs() < 1e-6); } } From 2576f3bc4cb5804059fb61ef22c1f5f2fe2d693e Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 11 Mar 2026 21:07:35 +0000 Subject: [PATCH 03/11] Adds equation 59.7 --- .../sfpe_handbook/chapter_59/equation_59_7.rs | 32 +++++++++++-------- .../sfpe_handbook/chapter_59/equation_59_8.rs | 31 ++++++++++-------- .../src/chapter_59/equation_59_7.rs | 30 +++++++++++------ .../src/chapter_59/equation_59_8.rs | 31 ++++++++++++------ 4 files changed, 80 insertions(+), 44 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs index fe991aee..6219b7b3 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs @@ -1,36 +1,42 @@ use pyo3::prelude::*; +use openfire::sfpe_handbook::chapter_59::equation_59_7 as rust_equation_59_7; + #[pyfunction] -/// Placeholder for equation 59.7. +/// Calculates specific flow by combining Equations 59.5 and 59.6 (Equation 59.7). /// -/// This function is a placeholder and will be implemented with the actual -/// equation logic. +/// This equation combines the travel speed equation (59.5) with the specific +/// flow equation (59.6) to express specific flow directly in terms of density. /// /// .. math:: /// -/// \text{TODO: Add equation} +/// F_s = (1 - aD) \cdot k \cdot D /// /// where: /// -/// - TODO: Add variable definitions +/// - :math:`F_s` is the specific flow (persons/min/ft or persons/s/m of effective width) +/// - :math:`a` is the unit conversion constant (0.266 for SI, 2.86 for imperial) +/// - :math:`D` is the population density (persons per unit area) +/// - :math:`k` is the speed constant from Table 59.2 /// /// Args: -/// TODO: Add arguments +/// a (float): Unit conversion constant (0.266 for SI units, 2.86 for imperial units) +/// d (float): Population density (persons/m² or persons/ft²) +/// k (float): Speed constant from Table 59.2 /// /// Returns: -/// float: TODO: Add return description +/// float: Specific flow Fs (persons/s/m or persons/min/ft of effective width) /// /// Example: /// >>> import ofire -/// >>> # TODO: Add example when equation is implemented -fn equation_59_7_placeholder() -> PyResult { - Err(pyo3::exceptions::PyNotImplementedError::new_err( - "Equation 59.7 is not yet implemented", - )) +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_7.specific_flow(0.266, 0.5, 1.19) +/// >>> print(f"{result:.4f} persons/s/m") +fn specific_flow(a: f64, d: f64, k: f64) -> PyResult { + Ok(rust_equation_59_7::specific_flow(a, d, k)) } #[pymodule] pub fn equation_59_7(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(pyo3::wrap_pyfunction!(equation_59_7_placeholder, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(specific_flow, m)?)?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs index 4c86d59a..0450d9a5 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs @@ -1,36 +1,41 @@ use pyo3::prelude::*; +use openfire::sfpe_handbook::chapter_59::equation_59_8 as rust_equation_59_8; + #[pyfunction] -/// Placeholder for equation 59.8. +/// Calculates the flow rate of persons passing a point in an exit route (Equation 59.8). /// -/// This function is a placeholder and will be implemented with the actual -/// equation logic. +/// The calculated flow is the predicted flow rate of persons passing a particular +/// point in an exit route. This equation assumes the achievable flow rate through +/// a component is directly proportional to its width. /// /// .. math:: /// -/// \text{TODO: Add equation} +/// F_c = F_s \cdot W_e /// /// where: /// -/// - TODO: Add variable definitions +/// - :math:`F_c` is the calculated flow (persons/min or persons/s) +/// - :math:`F_s` is the specific flow (persons/min/ft or persons/s/m of effective width) +/// - :math:`W_e` is the effective width of the component being traversed (ft or m) /// /// Args: -/// TODO: Add arguments +/// fs (float): Specific flow (persons/min/ft or persons/s/m of effective width) +/// we (float): Effective width of the component being traversed (ft or m) /// /// Returns: -/// float: TODO: Add return description +/// float: Calculated flow Fc (persons/min or persons/s) /// /// Example: /// >>> import ofire -/// >>> # TODO: Add example when equation is implemented -fn equation_59_8_placeholder() -> PyResult { - Err(pyo3::exceptions::PyNotImplementedError::new_err( - "Equation 59.8 is not yet implemented", - )) +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_8.calculated_flow(1.3, 1.5) +/// >>> print(f"{result:.2f} persons/s") +fn calculated_flow(fs: f64, we: f64) -> PyResult { + Ok(rust_equation_59_8::calculated_flow(fs, we)) } #[pymodule] pub fn equation_59_8(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(pyo3::wrap_pyfunction!(equation_59_8_placeholder, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(calculated_flow, m)?)?; Ok(()) } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs index f3ced8c8..52edb317 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs @@ -1,13 +1,24 @@ -/// Placeholder for equation 59.7 calculation. +/// Calculates specific flow by combining Equations 59.5 and 59.6 (Equation 59.7). /// -/// TODO: Implement the actual equation logic. -pub fn equation_59_7_placeholder() -> f64 { - todo!("Implement equation 59.7") +/// This equation combines the travel speed equation (59.5) with the specific +/// flow equation (59.6) to express specific flow directly in terms of density. +/// +/// # Arguments +/// +/// * `a` - Unit conversion constant (0.266 for SI units, 2.86 for imperial units) +/// * `d` - Population density in persons per unit area +/// * `k` - Speed constant from Table 59.2 +/// +/// # Returns +/// +/// Specific flow Fs (persons/min/ft or persons/s/m of effective width) +pub fn specific_flow(a: f64, d: f64, k: f64) -> f64 { + (1.0 - a * d) * k * d } #[cfg(not(coverage))] -pub fn equation_59_7_equation() -> &'static str { - "TODO: Add equation string" +pub fn specific_flow_equation(fs: String, a: String, d: String, k: String) -> String { + format!("{} = (1 - {} \\cdot {}) \\cdot {} \\cdot {}", fs, a, d, k, d) } #[cfg(test)] @@ -15,8 +26,9 @@ mod tests { use super::*; #[test] - #[ignore = "placeholder - implement when equation is added"] - fn test_equation_59_7() { - // TODO: Add test when equation is implemented + fn test_specific_flow_combined() { + let result = specific_flow(0.266, 0.5, 1.4); + assert!((result - 0.6069).abs() < 1e-6); } + } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs index a8d03be0..a938007a 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs @@ -1,13 +1,24 @@ -/// Placeholder for equation 59.8 calculation. +/// Calculates the flow rate of persons passing a point in an exit route (Equation 59.8). /// -/// TODO: Implement the actual equation logic. -pub fn equation_59_8_placeholder() -> f64 { - todo!("Implement equation 59.8") +/// The calculated flow is the predicted flow rate of persons passing a particular +/// point in an exit route. This equation assumes the achievable flow rate through +/// a component is directly proportional to its width. +/// +/// # Arguments +/// +/// * `fs` - Specific flow (persons/min/ft or persons/s/m of effective width) +/// * `we` - Effective width of the component being traversed (ft or m) +/// +/// # Returns +/// +/// Calculated flow Fc (persons/min or persons/s) +pub fn calculated_flow(fs: f64, we: f64) -> f64 { + fs * we } #[cfg(not(coverage))] -pub fn equation_59_8_equation() -> &'static str { - "TODO: Add equation string" +pub fn calculated_flow_equation(fc: String, fs: String, we: String) -> String { + format!("{} = {} \\cdot {}", fc, fs, we) } #[cfg(test)] @@ -15,8 +26,10 @@ mod tests { use super::*; #[test] - #[ignore = "placeholder - implement when equation is added"] - fn test_equation_59_8() { - // TODO: Add test when equation is implemented + fn test_calculated_flow() { + // Fs = 1.3 persons/s/m, We = 1.5 m -> Fc = 1.95 persons/s + let result = calculated_flow(1.3, 1.5); + assert!((result - 1.95).abs() < 1e-6); } + } From 62d89c74a0c35e52a7145dd21f69d043c2777fb0 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 11 Mar 2026 21:14:59 +0000 Subject: [PATCH 04/11] implements equation 59-9 --- .../sfpe_handbook/chapter_59/equation_59_9.rs | 38 +++++++++----- .../src/chapter_59/equation_59_9.rs | 50 +++++++++++++++---- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs index 86843347..f2b1ea7f 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs @@ -1,36 +1,48 @@ use pyo3::prelude::*; +use openfire::sfpe_handbook::chapter_59::equation_59_9 as rust_equation_59_9; + #[pyfunction] -/// Placeholder for equation 59.9. +/// Calculates flow rate by combining Equations 59.7 and 59.8 (Equation 59.9). /// -/// This function is a placeholder and will be implemented with the actual -/// equation logic. +/// This equation combines the specific flow equation (59.7) with the calculated +/// flow equation (59.8) to express flow rate directly in terms of the fundamental +/// parameters. /// /// .. math:: /// -/// \text{TODO: Add equation} +/// F_c = (1 - aD) \cdot k \cdot D \cdot W_e /// /// where: /// -/// - TODO: Add variable definitions +/// - :math:`F_c` is the calculated flow (persons/min or persons/s) +/// - :math:`a` is constant (0.266 for SI, 2.86 for imperial) +/// - :math:`D` is the population density (persons per unit area) +/// - :math:`k` is the constant from Table 59.2 +/// - :math:`W_e` is the effective width of the component (ft or m) +/// +/// Fc is in persons/min when k = k1 (from Table 59.2), D is in persons/ft², and We is in ft. +/// Fc is in persons/s when k = k2 (from Table 59.2), D is in persons/m², and We is in m. /// /// Args: -/// TODO: Add arguments +/// a (float): Unit constant (0.266 for SI units, 2.86 for imperial units) +/// d (float): Population density (persons/m² or persons/ft²) +/// k (float): Constant from Table 59.2 +/// we (float): Effective width of the component being traversed (ft or m) /// /// Returns: -/// float: TODO: Add return description +/// float: Calculated flow Fc (persons/min or persons/s) /// /// Example: /// >>> import ofire -/// >>> # TODO: Add example when equation is implemented -fn equation_59_9_placeholder() -> PyResult { - Err(pyo3::exceptions::PyNotImplementedError::new_err( - "Equation 59.9 is not yet implemented", - )) +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_9.calculated_flow(0.266, 0.5, 1.40, 1.2) +/// >>> print(f"{result:.4f} persons/s") +fn calculated_flow(a: f64, d: f64, k: f64, we: f64) -> PyResult { + Ok(rust_equation_59_9::calculated_flow(a, d, k, we)) } #[pymodule] pub fn equation_59_9(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(pyo3::wrap_pyfunction!(equation_59_9_placeholder, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(calculated_flow, m)?)?; Ok(()) } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs index 43664f29..06fbd030 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs @@ -1,13 +1,29 @@ -/// Placeholder for equation 59.9 calculation. +/// Calculates flow rate by combining Equations 59.7 and 59.8 (Equation 59.9). /// -/// TODO: Implement the actual equation logic. -pub fn equation_59_9_placeholder() -> f64 { - todo!("Implement equation 59.9") +/// This equation combines the specific flow equation (59.7) with the calculated +/// flow equation (59.8) to express flow rate directly in terms of the fundamental +/// parameters. +/// +/// # Arguments +/// +/// * `a` - Unit conversion constant (0.266 for SI units, 2.86 for imperial units) +/// * `d` - Population density in persons per unit area +/// * `k` - Speed constant from Table 59.2 +/// * `we` - Effective width of the component being traversed +/// +/// # Returns +/// +/// Calculated flow Fc (persons/min when using imperial units, persons/s when using SI units) +pub fn calculated_flow(a: f64, d: f64, k: f64, we: f64) -> f64 { + (1.0 - a * d) * k * d * we } #[cfg(not(coverage))] -pub fn equation_59_9_equation() -> &'static str { - "TODO: Add equation string" +pub fn calculated_flow_equation(fc: String, a: String, d: String, k: String, we: String) -> String { + format!( + "{} = (1 - {} \\cdot {}) \\cdot {} \\cdot {} \\cdot {}", + fc, a, d, k, d, we + ) } #[cfg(test)] @@ -15,8 +31,24 @@ mod tests { use super::*; #[test] - #[ignore = "placeholder - implement when equation is added"] - fn test_equation_59_9() { - // TODO: Add test when equation is implemented + fn test_calculated_flow() { + // Using SI units: a = 0.266, D = 0.5 persons/m², k = 1.40 m/s, We = 1.2 m + // Fc = (1 - 0.266 * 0.5) * 1.40 * 0.5 * 1.2 = 0.867 * 0.84 = 0.72828 + let result = calculated_flow(0.266, 0.5, 1.40, 1.2); + assert!((result - 0.72828).abs() < 1e-5); + } + + #[test] + fn test_calculated_flow_zero_density() { + // When D = 0, Fc = 0 + let result = calculated_flow(0.266, 0.0, 1.40, 1.2); + assert!((result - 0.0).abs() < 1e-6); + } + + #[test] + fn test_calculated_flow_zero_width() { + // When We = 0, Fc = 0 + let result = calculated_flow(0.266, 0.5, 1.40, 0.0); + assert!((result - 0.0).abs() < 1e-6); } } From fc49996e8e26d4f8e3d694f450e7f4feb06049c5 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 18 Mar 2026 19:26:52 +0000 Subject: [PATCH 05/11] Implements equation 59.10 --- .../chapter_59/equation_59_10.rs | 33 +++++++++++-------- .../src/chapter_59/equation_59_10.rs | 28 +++++++++++----- .../src/chapter_59/equation_59_7.rs | 6 ++-- .../src/chapter_59/equation_59_8.rs | 1 - 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs index a5ef16c7..20705c5d 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs @@ -1,36 +1,43 @@ use pyo3::prelude::*; +use openfire::sfpe_handbook::chapter_59::equation_59_10 as rust_equation_59_10; + #[pyfunction] -/// Placeholder for equation 59.10. +/// Calculates the time for passage (Equation 59.10). /// -/// This function is a placeholder and will be implemented with the actual -/// equation logic. +/// The time for passage is the time for a group of persons to pass a point +/// in an exit route. /// /// .. math:: /// -/// \text{TODO: Add equation} +/// t_p = \frac{P}{F_c} /// /// where: /// -/// - TODO: Add variable definitions +/// - :math:`t_p` is the time for passage (minutes or seconds) +/// - :math:`P` is the population size (persons) +/// - :math:`F_c` is the calculated flow (persons/min or persons/s) +/// +/// The units of tp depend on the units of Fc: tp is in minutes when Fc is in +/// persons/min; tp is in seconds when Fc is in persons/s. /// /// Args: -/// TODO: Add arguments +/// p (float): Population size (persons) +/// fc (float): Calculated flow (persons/min or persons/s) /// /// Returns: -/// float: TODO: Add return description +/// float: Time for passage tp (minutes or seconds depending on Fc units) /// /// Example: /// >>> import ofire -/// >>> # TODO: Add example when equation is implemented -fn equation_59_10_placeholder() -> PyResult { - Err(pyo3::exceptions::PyNotImplementedError::new_err( - "Equation 59.10 is not yet implemented", - )) +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_10.time_for_passage(100.0, 20.0) +/// >>> print(f"{result:.1f} minutes") +fn time_for_passage(p: f64, fc: f64) -> PyResult { + Ok(rust_equation_59_10::time_for_passage(p, fc)) } #[pymodule] pub fn equation_59_10(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(pyo3::wrap_pyfunction!(equation_59_10_placeholder, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(time_for_passage, m)?)?; Ok(()) } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs index 7d00921e..f2961acc 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs @@ -1,13 +1,23 @@ -/// Placeholder for equation 59.10 calculation. +/// Calculates the time for passage (Equation 59.10). /// -/// TODO: Implement the actual equation logic. -pub fn equation_59_10_placeholder() -> f64 { - todo!("Implement equation 59.10") +/// The time for passage is the time for a group of persons to pass a point +/// in an exit route. +/// +/// # Arguments +/// +/// * `p` - Population size (persons) +/// * `fc` - Calculated flow (persons/min or persons/s) +/// +/// # Returns +/// +/// Time for passage tp (minutes when Fc is in persons/min; seconds when Fc is in persons/s) +pub fn time_for_passage(p: f64, fc: f64) -> f64 { + p / fc } #[cfg(not(coverage))] -pub fn equation_59_10_equation() -> &'static str { - "TODO: Add equation string" +pub fn time_for_passage_equation(tp: String, p: String, fc: String) -> String { + format!("{} = \\frac{{{}}}{{{}}}", tp, p, fc) } #[cfg(test)] @@ -15,8 +25,8 @@ mod tests { use super::*; #[test] - #[ignore = "placeholder - implement when equation is added"] - fn test_equation_59_10() { - // TODO: Add test when equation is implemented + fn test_time_for_passage() { + let result = time_for_passage(100.0, 1.0); + assert!((result - 100.0).abs() < 1e-6); } } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs index 52edb317..7ed8782f 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs @@ -18,7 +18,10 @@ pub fn specific_flow(a: f64, d: f64, k: f64) -> f64 { #[cfg(not(coverage))] pub fn specific_flow_equation(fs: String, a: String, d: String, k: String) -> String { - format!("{} = (1 - {} \\cdot {}) \\cdot {} \\cdot {}", fs, a, d, k, d) + format!( + "{} = (1 - {} \\cdot {}) \\cdot {} \\cdot {}", + fs, a, d, k, d + ) } #[cfg(test)] @@ -30,5 +33,4 @@ mod tests { let result = specific_flow(0.266, 0.5, 1.4); assert!((result - 0.6069).abs() < 1e-6); } - } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs index a938007a..426c3796 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs @@ -31,5 +31,4 @@ mod tests { let result = calculated_flow(1.3, 1.5); assert!((result - 1.95).abs() < 1e-6); } - } From f0239f8044ab63bec0cbff099abc5280a58595ef Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 18 Mar 2026 19:34:49 +0000 Subject: [PATCH 06/11] Implmeents 59.11 --- .../chapter_59/equation_59_11.rs | 40 ++++++++++++------ .../src/chapter_59/equation_59_11.rs | 42 +++++++++++++++---- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs index f8c4b3be..3e68cb87 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs @@ -1,36 +1,50 @@ use pyo3::prelude::*; +use openfire::sfpe_handbook::chapter_59::equation_59_11 as rust_equation_59_11; + #[pyfunction] -/// Placeholder for equation 59.11. +/// Calculates the time for passage combining Equations 59.9 and 59.10 (Equation 59.11). /// -/// This function is a placeholder and will be implemented with the actual -/// equation logic. +/// This equation combines the calculated flow equation (59.9) with the time for +/// passage equation (59.10) to express time directly in terms of the fundamental +/// parameters. /// /// .. math:: /// -/// \text{TODO: Add equation} +/// t_p = \frac{P}{(1 - aD) \cdot k \cdot D \cdot W_e} /// /// where: /// -/// - TODO: Add variable definitions +/// - :math:`t_p` is the time for passage (minutes or seconds) +/// - :math:`P` is the population size (persons) +/// - :math:`a` is constant (0.266 for SI, 2.86 for imperial) +/// - :math:`D` is the population density (persons per unit area) +/// - :math:`k` is the constant from Table 59.2 +/// - :math:`W_e` is the effective width of the component (ft or m) +/// +/// tp is in minutes when k = k1 (from Table 59.2), D is in persons/ft², and We is in ft. +/// tp is in seconds when k = k2 (from Table 59.2), D is in persons/m², and We is in m. /// /// Args: -/// TODO: Add arguments +/// p (float): Population size (persons) +/// a (float): Unit constant (0.266 for SI units, 2.86 for imperial units) +/// d (float): Population density (persons/m² or persons/ft²) +/// k (float): Constant from Table 59.2 +/// we (float): Effective width of the component being traversed (ft or m) /// /// Returns: -/// float: TODO: Add return description +/// float: Time for passage tp (minutes or seconds depending on units) /// /// Example: /// >>> import ofire -/// >>> # TODO: Add example when equation is implemented -fn equation_59_11_placeholder() -> PyResult { - Err(pyo3::exceptions::PyNotImplementedError::new_err( - "Equation 59.11 is not yet implemented", - )) +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_11.time_for_passage(100.0, 0.266, 0.5, 1.40, 1.2) +/// >>> print(f"{result:.1f} seconds") +fn time_for_passage(p: f64, a: f64, d: f64, k: f64, we: f64) -> PyResult { + Ok(rust_equation_59_11::time_for_passage(p, a, d, k, we)) } #[pymodule] pub fn equation_59_11(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(pyo3::wrap_pyfunction!(equation_59_11_placeholder, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(time_for_passage, m)?)?; Ok(()) } diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs index 3cb8ba61..13d7a6fe 100644 --- a/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs @@ -1,13 +1,37 @@ -/// Placeholder for equation 59.11 calculation. +/// Calculates the time for passage combining Equations 59.9 and 59.10 (Equation 59.11). /// -/// TODO: Implement the actual equation logic. -pub fn equation_59_11_placeholder() -> f64 { - todo!("Implement equation 59.11") +/// This equation combines the calculated flow equation (59.9) with the time for +/// passage equation (59.10) to express time directly in terms of the fundamental +/// parameters. +/// +/// # Arguments +/// +/// * `p` - Population size (persons) +/// * `a` - Unit conversion constant (0.266 for SI units, 2.86 for imperial units) +/// * `d` - Population density in persons per unit area +/// * `k` - Speed constant from Table 59.2 +/// * `we` - Effective width of the component being traversed +/// +/// # Returns +/// +/// Time for passage tp (minutes when using imperial units, seconds when using SI units) +pub fn time_for_passage(p: f64, a: f64, d: f64, k: f64, we: f64) -> f64 { + p / ((1.0 - a * d) * k * d * we) } #[cfg(not(coverage))] -pub fn equation_59_11_equation() -> &'static str { - "TODO: Add equation string" +pub fn time_for_passage_equation( + tp: String, + p: String, + a: String, + d: String, + k: String, + we: String, +) -> String { + format!( + "{} = \\frac{{{}}}{{(1 - {} \\cdot {}) \\cdot {} \\cdot {} \\cdot {}}}", + tp, p, a, d, k, d, we + ) } #[cfg(test)] @@ -15,8 +39,8 @@ mod tests { use super::*; #[test] - #[ignore = "placeholder - implement when equation is added"] - fn test_equation_59_11() { - // TODO: Add test when equation is implemented + fn test_time_for_passage() { + let result = time_for_passage(100.0, 0.266, 0.5, 1.40, 1.2); + assert!((result - 137.309825891).abs() < 1e-6); } } From 285291ec78ecb007eace6556e377c613567e52b7 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 18 Mar 2026 19:39:51 +0000 Subject: [PATCH 07/11] Adds equation 59.12 --- .../src/sfpe_handbook/chapter_59.rs | 2 + .../chapter_59/equation_59_12.rs | 43 +++++++++++++++++++ crates/sfpe_handbook/src/chapter_59.rs | 1 + .../src/chapter_59/equation_59_12.rs | 42 ++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_12.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_12.rs diff --git a/crates/python_api/src/sfpe_handbook/chapter_59.rs b/crates/python_api/src/sfpe_handbook/chapter_59.rs index c09cd0e1..e6f07fc7 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59.rs @@ -1,5 +1,6 @@ pub mod equation_59_10; pub mod equation_59_11; +pub mod equation_59_12; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; @@ -21,5 +22,6 @@ pub fn chapter_59(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_59_9::equation_59_9))?; m.add_wrapped(wrap_pymodule!(equation_59_10::equation_59_10))?; m.add_wrapped(wrap_pymodule!(equation_59_11::equation_59_11))?; + m.add_wrapped(wrap_pymodule!(equation_59_12::equation_59_12))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_12.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_12.rs new file mode 100644 index 00000000..f9142cf3 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_12.rs @@ -0,0 +1,43 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_12 as rust_equation_59_12; + +#[pyfunction] +/// Calculates the specific flow departing from a transition point (Equation 59.12). +/// +/// For cases involving one flow into and one flow out of a transition point, +/// this equation determines the specific flow of the route departing from +/// the transition point. +/// +/// .. math:: +/// +/// F_{s(out)} = \frac{F_{s(in)} \cdot W_{e(in)}}{W_{e(out)}} +/// +/// where: +/// +/// - :math:`F_{s(out)}` is the specific flow departing from transition point (persons/min/ft or persons/s/m) +/// - :math:`F_{s(in)}` is the specific flow arriving at transition point (persons/min/ft or persons/s/m) +/// - :math:`W_{e(in)}` is the effective width prior to transition point (ft or m) +/// - :math:`W_{e(out)}` is the effective width after passing transition point (ft or m) +/// +/// Args: +/// fs_in (float): Specific flow arriving at transition point (persons/min/ft or persons/s/m) +/// we_in (float): Effective width prior to transition point (ft or m) +/// we_out (float): Effective width after passing transition point (ft or m) +/// +/// Returns: +/// float: Specific flow departing from transition point Fs(out) (persons/min/ft or persons/s/m) +/// +/// Example: +/// >>> import ofire +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_12.specific_flow_out(1.0, 2.0, 1.0) +/// >>> print(f"{result:.1f} persons/s/m") +fn specific_flow_out(fs_in: f64, we_in: f64, we_out: f64) -> PyResult { + Ok(rust_equation_59_12::specific_flow_out(fs_in, we_in, we_out)) +} + +#[pymodule] +pub fn equation_59_12(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(specific_flow_out, m)?)?; + Ok(()) +} diff --git a/crates/sfpe_handbook/src/chapter_59.rs b/crates/sfpe_handbook/src/chapter_59.rs index ee9a40c2..4e7d07cd 100644 --- a/crates/sfpe_handbook/src/chapter_59.rs +++ b/crates/sfpe_handbook/src/chapter_59.rs @@ -1,5 +1,6 @@ pub mod equation_59_10; pub mod equation_59_11; +pub mod equation_59_12; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_12.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_12.rs new file mode 100644 index 00000000..dc46d0fa --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_12.rs @@ -0,0 +1,42 @@ +/// Calculates the specific flow departing from a transition point (Equation 59.12). +/// +/// For cases involving one flow into and one flow out of a transition point, +/// this equation determines the specific flow of the route departing from +/// the transition point. +/// +/// # Arguments +/// +/// * `fs_in` - Specific flow arriving at transition point (persons/min/ft or persons/s/m) +/// * `we_in` - Effective width prior to transition point (ft or m) +/// * `we_out` - Effective width after passing transition point (ft or m) +/// +/// # Returns +/// +/// Specific flow departing from transition point Fs(out) (persons/min/ft or persons/s/m) +pub fn specific_flow_out(fs_in: f64, we_in: f64, we_out: f64) -> f64 { + fs_in * we_in / we_out +} + +#[cfg(not(coverage))] +pub fn specific_flow_out_equation( + fs_out: String, + fs_in: String, + we_in: String, + we_out: String, +) -> String { + format!( + "{} = \\frac{{{} \\cdot {}}}{{{}}}", + fs_out, fs_in, we_in, we_out + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_specific_flow_out_narrowing() { + let result = specific_flow_out(1.0, 2.0, 1.0); + assert!((result - 2.0).abs() < 1e-6); + } +} From c1cf4318277c1c5546410030ce4e72dd121eb175 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 18 Mar 2026 19:51:48 +0000 Subject: [PATCH 08/11] Implements 59.13 --- .../src/sfpe_handbook/chapter_59.rs | 5 +- .../chapter_59/equation_59_13.rs | 55 +++++++++++++++++++ crates/sfpe_handbook/src/chapter_59.rs | 1 + .../src/chapter_59/equation_59_13.rs | 52 ++++++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_13.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_13.rs diff --git a/crates/python_api/src/sfpe_handbook/chapter_59.rs b/crates/python_api/src/sfpe_handbook/chapter_59.rs index e6f07fc7..cacfb037 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59.rs @@ -1,6 +1,7 @@ pub mod equation_59_10; pub mod equation_59_11; pub mod equation_59_12; +pub mod equation_59_13; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; @@ -11,9 +12,6 @@ use pyo3::prelude::*; use pyo3::wrap_pymodule; #[pymodule] -/// Chapter 59 - Placeholder equations. -/// -/// This chapter contains placeholder equations that will be implemented. pub fn chapter_59(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_59_5::equation_59_5))?; m.add_wrapped(wrap_pymodule!(equation_59_6::equation_59_6))?; @@ -23,5 +21,6 @@ pub fn chapter_59(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_59_10::equation_59_10))?; m.add_wrapped(wrap_pymodule!(equation_59_11::equation_59_11))?; m.add_wrapped(wrap_pymodule!(equation_59_12::equation_59_12))?; + m.add_wrapped(wrap_pymodule!(equation_59_13::equation_59_13))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_13.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_13.rs new file mode 100644 index 00000000..69c85947 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_13.rs @@ -0,0 +1,55 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_13 as rust_equation_59_13; + +#[pyfunction] +/// Calculates the specific flow departing from a transition point with two incoming flows (Equation 59.13). +/// +/// For cases involving two incoming flows and one outflow from a transition point, +/// such as that which occurs with the merger of a flow down a stair and the +/// entering flow at a floor. +/// +/// .. math:: +/// +/// F_{s(out)} = \frac{F_{s(in1)} \cdot W_{e(in1)} + F_{s(in2)} \cdot W_{e(in2)}}{W_{e(out)}} +/// +/// where: +/// +/// - :math:`F_{s(out)}` is the specific flow departing from transition point (persons/min/ft or persons/s/m) +/// - :math:`F_{s(in1)}` is the specific flow of first incoming stream (persons/min/ft or persons/s/m) +/// - :math:`W_{e(in1)}` is the effective width of first incoming stream (ft or m) +/// - :math:`F_{s(in2)}` is the specific flow of second incoming stream (persons/min/ft or persons/s/m) +/// - :math:`W_{e(in2)}` is the effective width of second incoming stream (ft or m) +/// - :math:`W_{e(out)}` is the effective width after passing transition point (ft or m) +/// +/// Args: +/// fs_in1 (float): Specific flow of first incoming stream (persons/min/ft or persons/s/m) +/// we_in1 (float): Effective width of first incoming stream (ft or m) +/// fs_in2 (float): Specific flow of second incoming stream (persons/min/ft or persons/s/m) +/// we_in2 (float): Effective width of second incoming stream (ft or m) +/// we_out (float): Effective width after passing transition point (ft or m) +/// +/// Returns: +/// float: Specific flow departing from transition point Fs(out) (persons/min/ft or persons/s/m) +/// +/// Example: +/// >>> import ofire +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_13.specific_flow_out_two_inflows(1.0, 1.0, 1.0, 1.0, 2.0) +/// >>> print(f"{result:.1f} persons/s/m") +fn specific_flow_out_two_inflows( + fs_in1: f64, + we_in1: f64, + fs_in2: f64, + we_in2: f64, + we_out: f64, +) -> PyResult { + Ok(rust_equation_59_13::specific_flow_out_two_inflows( + fs_in1, we_in1, fs_in2, we_in2, we_out, + )) +} + +#[pymodule] +pub fn equation_59_13(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(specific_flow_out_two_inflows, m)?)?; + Ok(()) +} diff --git a/crates/sfpe_handbook/src/chapter_59.rs b/crates/sfpe_handbook/src/chapter_59.rs index 4e7d07cd..35827449 100644 --- a/crates/sfpe_handbook/src/chapter_59.rs +++ b/crates/sfpe_handbook/src/chapter_59.rs @@ -1,6 +1,7 @@ pub mod equation_59_10; pub mod equation_59_11; pub mod equation_59_12; +pub mod equation_59_13; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_13.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_13.rs new file mode 100644 index 00000000..81a9b556 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_13.rs @@ -0,0 +1,52 @@ +/// Calculates the specific flow departing from a transition point with two incoming flows (Equation 59.13). +/// +/// For cases involving two incoming flows and one outflow from a transition point, +/// such as that which occurs with the merger of a flow down a stair and the +/// entering flow at a floor. +/// +/// # Arguments +/// +/// * `fs_in1` - Specific flow of first incoming stream (persons/min/ft or persons/s/m) +/// * `we_in1` - Effective width of first incoming stream (ft or m) +/// * `fs_in2` - Specific flow of second incoming stream (persons/min/ft or persons/s/m) +/// * `we_in2` - Effective width of second incoming stream (ft or m) +/// * `we_out` - Effective width after passing transition point (ft or m) +/// +/// # Returns +/// +/// Specific flow departing from transition point Fs(out) (persons/min/ft or persons/s/m) +pub fn specific_flow_out_two_inflows( + fs_in1: f64, + we_in1: f64, + fs_in2: f64, + we_in2: f64, + we_out: f64, +) -> f64 { + (fs_in1 * we_in1 + fs_in2 * we_in2) / we_out +} + +#[cfg(not(coverage))] +pub fn specific_flow_out_two_inflows_equation( + fs_out: String, + fs_in1: String, + we_in1: String, + fs_in2: String, + we_in2: String, + we_out: String, +) -> String { + format!( + "{} = \\frac{{{} \\cdot {} + {} \\cdot {}}}{{{}}}", + fs_out, fs_in1, we_in1, fs_in2, we_in2, we_out + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_specific_flow_out_two_inflows_equal() { + let result = specific_flow_out_two_inflows(1.2, 1.0, 0.8, 1.2, 2.0); + assert!((result - 1.08).abs() < 1e-6); + } +} From 5394aa698f10f12051c909ffa47b2500b1fec59f Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 18 Mar 2026 19:57:52 +0000 Subject: [PATCH 09/11] Improvements to python api docs --- crates/python_api/docs/api/sfpe-handbook.rst | 44 +++++++++++++------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index dbeb1d97..24ef0c93 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -147,58 +147,74 @@ Chapter 59 :undoc-members: :show-inheritance: -Equation 59.5 -~~~~~~~~~~~~~ +Equation 59.5 - Pedestrian Travel Speed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_5 :members: :undoc-members: :show-inheritance: -Equation 59.6 -~~~~~~~~~~~~~ +Equation 59.6 - Specific Flow of Evacuating Persons +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_6 :members: :undoc-members: :show-inheritance: -Equation 59.7 -~~~~~~~~~~~~~ +Equation 59.7 - Combined Specific Flow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_7 :members: :undoc-members: :show-inheritance: -Equation 59.8 -~~~~~~~~~~~~~ +Equation 59.8 - Flow Rate of Persons +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_8 :members: :undoc-members: :show-inheritance: -Equation 59.9 -~~~~~~~~~~~~~ +Equation 59.9 - Combined Flow Rate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_9 :members: :undoc-members: :show-inheritance: -Equation 59.10 -~~~~~~~~~~~~~~ +Equation 59.10 - Time for Passage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_10 :members: :undoc-members: :show-inheritance: -Equation 59.11 -~~~~~~~~~~~~~~ +Equation 59.11 - Combined Time for Passage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_11 :members: :undoc-members: :show-inheritance: + +Equation 59.12 - Specific Flow at Transition Point +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_12 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.13 - Specific Flow with Two Incoming Flows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_13 + :members: + :undoc-members: + :show-inheritance: From 8ab7c2a0cad96325293a1243380fb4373aa63ca8 Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 18 Mar 2026 20:06:04 +0000 Subject: [PATCH 10/11] Implementes equation 59.15 --- .../src/sfpe_handbook/chapter_59.rs | 2 + .../chapter_59/equation_59_15.rs | 47 +++++++++++++++++++ crates/sfpe_handbook/src/chapter_59.rs | 1 + .../src/chapter_59/equation_59_15.rs | 43 +++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_15.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_15.rs diff --git a/crates/python_api/src/sfpe_handbook/chapter_59.rs b/crates/python_api/src/sfpe_handbook/chapter_59.rs index cacfb037..0c284f67 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59.rs @@ -2,6 +2,7 @@ pub mod equation_59_10; pub mod equation_59_11; pub mod equation_59_12; pub mod equation_59_13; +pub mod equation_59_15; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; @@ -22,5 +23,6 @@ pub fn chapter_59(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_59_11::equation_59_11))?; m.add_wrapped(wrap_pymodule!(equation_59_12::equation_59_12))?; m.add_wrapped(wrap_pymodule!(equation_59_13::equation_59_13))?; + m.add_wrapped(wrap_pymodule!(equation_59_15::equation_59_15))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_15.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_15.rs new file mode 100644 index 00000000..ec9e5c1d --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_15.rs @@ -0,0 +1,47 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_15 as rust_equation_59_15; + +#[pyfunction] +/// Calculates evacuation time when congestion dominates (Equation 59.15). +/// +/// In Scenario 1 it is assumed that congestion dominates the results produced. +/// In such situations the time required for the evacuation of an enclosure +/// depends on the pre-evacuation time and unrestricted walking time of the +/// first few occupants to start to leave; these determine the time for +/// congestion to develop. +/// +/// .. math:: +/// +/// t_{esc} = t_{pe1} + t_{trav} + t_{flow} +/// +/// where: +/// +/// - :math:`t_{esc}` is the evacuation time for an enclosure (time units) +/// - :math:`t_{pe1}` is the pre-evacuation time associated with the first people to respond (time units) +/// - :math:`t_{trav}` is the time taken to traverse the average distance to a place of safety (time units) +/// - :math:`t_{flow}` is the time of total occupant population to flow through the most restrictive components (time units) +/// +/// Args: +/// t_pe1 (float): Pre-evacuation time associated with the first people to respond (time units) +/// t_trav (float): Time taken to traverse the average distance to a place of safety (time units) +/// t_flow (float): Time of total occupant population to flow through the most restrictive components (time units) +/// +/// Returns: +/// float: Evacuation time for an enclosure tesc (time units) +/// +/// Example: +/// >>> import ofire +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_15.evacuation_time_congestion(30.0, 60.0, 120.0) +/// >>> print(f"{result:.0f} seconds") +fn evacuation_time_congestion(t_pe1: f64, t_trav: f64, t_flow: f64) -> PyResult { + Ok(rust_equation_59_15::evacuation_time_congestion( + t_pe1, t_trav, t_flow, + )) +} + +#[pymodule] +pub fn equation_59_15(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(evacuation_time_congestion, m)?)?; + Ok(()) +} diff --git a/crates/sfpe_handbook/src/chapter_59.rs b/crates/sfpe_handbook/src/chapter_59.rs index 35827449..920d8fe2 100644 --- a/crates/sfpe_handbook/src/chapter_59.rs +++ b/crates/sfpe_handbook/src/chapter_59.rs @@ -2,6 +2,7 @@ pub mod equation_59_10; pub mod equation_59_11; pub mod equation_59_12; pub mod equation_59_13; +pub mod equation_59_15; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_15.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_15.rs new file mode 100644 index 00000000..1adc6e4c --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_15.rs @@ -0,0 +1,43 @@ +/// Calculates evacuation time when congestion dominates (Equation 59.15). +/// +/// In Scenario 1 it is assumed that congestion dominates the results produced. +/// In such situations the time required for the evacuation of an enclosure +/// depends on the pre-evacuation time and unrestricted walking time of the +/// first few occupants to start to leave; these determine the time for +/// congestion to develop. Once queues have formed at the "constraining" +/// component, the time to clear the building becomes a function of the +/// number of occupants and the evacuation flow rate capacity of these components. +/// +/// # Arguments +/// +/// * `t_pe1` - Pre-evacuation time associated with the first people to respond (time units) +/// * `t_trav` - Time taken to traverse the average distance to a place of safety (time units) +/// * `t_flow` - Time of total occupant population to flow through the most restrictive components (time units) +/// +/// # Returns +/// +/// Evacuation time for an enclosure tesc (time units) +pub fn evacuation_time_congestion(t_pe1: f64, t_trav: f64, t_flow: f64) -> f64 { + t_pe1 + t_trav + t_flow +} + +#[cfg(not(coverage))] +pub fn evacuation_time_congestion_equation( + t_esc: String, + t_pe1: String, + t_trav: String, + t_flow: String, +) -> String { + format!("{} = {} + {} + {}", t_esc, t_pe1, t_trav, t_flow) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_evacuation_time_congestion() { + let result = evacuation_time_congestion(30.0, 60.0, 120.0); + assert!((result - 210.0).abs() < 1e-6); + } +} From 642160af2b1d4f7407f57d19edf8b70b510cb44a Mon Sep 17 00:00:00 2001 From: simonsantama Date: Wed, 18 Mar 2026 20:11:29 +0000 Subject: [PATCH 11/11] implements equation 59-16 --- crates/python_api/docs/api/sfpe-handbook.rst | 16 +++++++ .../src/sfpe_handbook/chapter_59.rs | 2 + .../chapter_59/equation_59_16.rs | 46 +++++++++++++++++++ crates/sfpe_handbook/src/chapter_59.rs | 1 + .../src/chapter_59/equation_59_16.rs | 42 +++++++++++++++++ 5 files changed, 107 insertions(+) create mode 100644 crates/python_api/src/sfpe_handbook/chapter_59/equation_59_16.rs create mode 100644 crates/sfpe_handbook/src/chapter_59/equation_59_16.rs diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index 24ef0c93..fcec22eb 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -218,3 +218,19 @@ Equation 59.13 - Specific Flow with Two Incoming Flows :members: :undoc-members: :show-inheritance: + +Equation 59.15 - Evacuation Time (Congestion Dominated) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_15 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.16 - Evacuation Time (No Congestion) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_16 + :members: + :undoc-members: + :show-inheritance: diff --git a/crates/python_api/src/sfpe_handbook/chapter_59.rs b/crates/python_api/src/sfpe_handbook/chapter_59.rs index 0c284f67..48bb2a96 100644 --- a/crates/python_api/src/sfpe_handbook/chapter_59.rs +++ b/crates/python_api/src/sfpe_handbook/chapter_59.rs @@ -3,6 +3,7 @@ pub mod equation_59_11; pub mod equation_59_12; pub mod equation_59_13; pub mod equation_59_15; +pub mod equation_59_16; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; @@ -24,5 +25,6 @@ pub fn chapter_59(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(equation_59_12::equation_59_12))?; m.add_wrapped(wrap_pymodule!(equation_59_13::equation_59_13))?; m.add_wrapped(wrap_pymodule!(equation_59_15::equation_59_15))?; + m.add_wrapped(wrap_pymodule!(equation_59_16::equation_59_16))?; Ok(()) } diff --git a/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_16.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_16.rs new file mode 100644 index 00000000..28ab1ced --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_16.rs @@ -0,0 +1,46 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_16 as rust_equation_59_16; + +#[pyfunction] +/// Calculates evacuation time when congestion does not dominate (Equation 59.16). +/// +/// In Scenario 2 it is assumed that congestion does not dominate the results; +/// the results are primarily influenced by the time taken to reach safety and +/// the time to respond. The impact of any congestion is dominated by the +/// extensive pre-evacuation phase and the distances that need to be traversed. +/// +/// .. math:: +/// +/// t_{esc} = t_{pe1} + t_{pe99} + t_{trav} +/// +/// where: +/// +/// - :math:`t_{esc}` is the egress time for an enclosure (time units) +/// - :math:`t_{pe1}` is the pre-evacuation time for the first people to respond, i.e. 1st percentile (time units) +/// - :math:`t_{pe99}` is the pre-evacuation time for the last few occupants to respond, i.e. 99th percentile (time units) +/// - :math:`t_{trav}` is the time taken to traverse the distance to a place of safety (time units) +/// +/// Args: +/// t_pe1 (float): Pre-evacuation time for the first people to respond, i.e. 1st percentile (time units) +/// t_pe99 (float): Pre-evacuation time for the last few occupants to respond, i.e. 99th percentile (time units) +/// t_trav (float): Time taken to traverse the distance to a place of safety (time units) +/// +/// Returns: +/// float: Egress time for an enclosure tesc (time units) +/// +/// Example: +/// >>> import ofire +/// >>> result = ofire.sfpe_handbook.chapter_59.equation_59_16.evacuation_time_no_congestion(15.0, 180.0, 90.0) +/// >>> print(f"{result:.0f} seconds") +fn evacuation_time_no_congestion(t_pe1: f64, t_pe99: f64, t_trav: f64) -> PyResult { + Ok(rust_equation_59_16::evacuation_time_no_congestion( + t_pe1, t_pe99, t_trav, + )) +} + +#[pymodule] +pub fn equation_59_16(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(evacuation_time_no_congestion, m)?)?; + Ok(()) +} diff --git a/crates/sfpe_handbook/src/chapter_59.rs b/crates/sfpe_handbook/src/chapter_59.rs index 920d8fe2..d10a0039 100644 --- a/crates/sfpe_handbook/src/chapter_59.rs +++ b/crates/sfpe_handbook/src/chapter_59.rs @@ -3,6 +3,7 @@ pub mod equation_59_11; pub mod equation_59_12; pub mod equation_59_13; pub mod equation_59_15; +pub mod equation_59_16; pub mod equation_59_5; pub mod equation_59_6; pub mod equation_59_7; diff --git a/crates/sfpe_handbook/src/chapter_59/equation_59_16.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_16.rs new file mode 100644 index 00000000..b30ee56a --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_16.rs @@ -0,0 +1,42 @@ +/// Calculates evacuation time when congestion does not dominate (Equation 59.16). +/// +/// In Scenario 2 it is assumed that congestion does not dominate the results; +/// the results are primarily influenced by the time taken to reach safety (ttrav) +/// and the time to respond (tpe). This scenario is not necessarily based on the +/// assumption that congestion does not develop; only that the impact of the +/// congestion is dominated by the extensive pre-evacuation phase and the distances +/// that need to be traversed, and is not a factor in the calculation. +/// +/// # Arguments +/// +/// * `t_pe1` - Pre-evacuation time for the first people to respond, i.e. 1st percentile (time units) +/// * `t_pe99` - Pre-evacuation time for the last few occupants to respond, i.e. 99th percentile (time units) +/// * `t_trav` - Time taken to traverse the distance to a place of safety (time units) +/// +/// # Returns +/// +/// Egress time for an enclosure tesc (time units) +pub fn evacuation_time_no_congestion(t_pe1: f64, t_pe99: f64, t_trav: f64) -> f64 { + t_pe1 + t_pe99 + t_trav +} + +#[cfg(not(coverage))] +pub fn evacuation_time_no_congestion_equation( + t_esc: String, + t_pe1: String, + t_pe99: String, + t_trav: String, +) -> String { + format!("{} = {} + {} + {}", t_esc, t_pe1, t_pe99, t_trav) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_evacuation_time_no_congestion() { + let result = evacuation_time_no_congestion(15.0, 180.0, 90.0); + assert!((result - 285.0).abs() < 1e-6); + } +}