diff --git a/crates/python_api/docs/api/sfpe-handbook.rst b/crates/python_api/docs/api/sfpe-handbook.rst index 8253ffd..fcec22e 100644 --- a/crates/python_api/docs/api/sfpe-handbook.rst +++ b/crates/python_api/docs/api/sfpe-handbook.rst @@ -137,4 +137,100 @@ 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 - Pedestrian Travel Speed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_5 + :members: + :undoc-members: + :show-inheritance: + +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 - Combined Specific Flow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_7 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.8 - Flow Rate of Persons +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_8 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.9 - Combined Flow Rate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_9 + :members: + :undoc-members: + :show-inheritance: + +Equation 59.10 - Time for Passage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_10 + :members: + :undoc-members: + :show-inheritance: + +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: + +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.rs b/crates/python_api/src/sfpe_handbook.rs index e7819f4..f8cf847 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 3a72c4f..3fe7135 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 2f2c6ba..f28b274 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 0000000..48bb2a9 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59.rs @@ -0,0 +1,30 @@ +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_16; +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] +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))?; + 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_10.rs b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs new file mode 100644 index 0000000..20705c5 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs @@ -0,0 +1,43 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_10 as rust_equation_59_10; + +#[pyfunction] +/// Calculates the time for passage (Equation 59.10). +/// +/// The time for passage is the time for a group of persons to pass a point +/// in an exit route. +/// +/// .. math:: +/// +/// t_p = \frac{P}{F_c} +/// +/// where: +/// +/// - :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: +/// p (float): Population size (persons) +/// fc (float): Calculated flow (persons/min or persons/s) +/// +/// Returns: +/// float: Time for passage tp (minutes or seconds depending on Fc units) +/// +/// Example: +/// >>> import ofire +/// >>> 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!(time_for_passage, 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 0000000..3e68cb8 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs @@ -0,0 +1,50 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_11 as rust_equation_59_11; + +#[pyfunction] +/// Calculates the time for passage combining Equations 59.9 and 59.10 (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. +/// +/// .. math:: +/// +/// t_p = \frac{P}{(1 - aD) \cdot k \cdot D \cdot W_e} +/// +/// where: +/// +/// - :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: +/// 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: Time for passage tp (minutes or seconds depending on units) +/// +/// Example: +/// >>> import ofire +/// >>> 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!(time_for_passage, m)?)?; + 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 0000000..f9142cf --- /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/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 0000000..69c8594 --- /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/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 0000000..ec9e5c1 --- /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/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 0000000..28ab1ce --- /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/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 0000000..fee6ca2 --- /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 0000000..45dd4a0 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs @@ -0,0 +1,40 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_6 as rust_equation_59_6; + +#[pyfunction] +/// Calculates specific flow of evacuating persons (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. +/// +/// .. math:: +/// +/// F_s = S \cdot D +/// +/// where: +/// +/// - :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: +/// s (float): Speed along the line of travel (m/s or ft/min) +/// d (float): Population density (persons/m² or persons/ft²) +/// +/// Returns: +/// float: Specific flow Fs (persons/s/m or persons/min/ft of effective width) +/// +/// Example: +/// >>> import ofire +/// >>> 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!(specific_flow, 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 0000000..6219b7b --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs @@ -0,0 +1,42 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_7 as rust_equation_59_7; + +#[pyfunction] +/// Calculates specific flow by combining Equations 59.5 and 59.6 (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. +/// +/// .. math:: +/// +/// F_s = (1 - aD) \cdot k \cdot D +/// +/// where: +/// +/// - :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: +/// 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: Specific flow Fs (persons/s/m or persons/min/ft of effective width) +/// +/// Example: +/// >>> import ofire +/// >>> 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!(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 new file mode 100644 index 0000000..0450d9a --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs @@ -0,0 +1,41 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_8 as rust_equation_59_8; + +#[pyfunction] +/// Calculates the flow rate of persons passing a point in an exit route (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. +/// +/// .. math:: +/// +/// F_c = F_s \cdot W_e +/// +/// where: +/// +/// - :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: +/// 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: Calculated flow Fc (persons/min or persons/s) +/// +/// Example: +/// >>> import ofire +/// >>> 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!(calculated_flow, 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 0000000..f2b1ea7 --- /dev/null +++ b/crates/python_api/src/sfpe_handbook/chapter_59/equation_59_9.rs @@ -0,0 +1,48 @@ +use pyo3::prelude::*; + +use openfire::sfpe_handbook::chapter_59::equation_59_9 as rust_equation_59_9; + +#[pyfunction] +/// Calculates flow rate by combining Equations 59.7 and 59.8 (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. +/// +/// .. math:: +/// +/// F_c = (1 - aD) \cdot k \cdot D \cdot W_e +/// +/// where: +/// +/// - :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: +/// 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: Calculated flow Fc (persons/min or persons/s) +/// +/// Example: +/// >>> import ofire +/// >>> 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!(calculated_flow, 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 8ca1c1c..5ee7ebf 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 79c8523..3b58b4d 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 0000000..d10a003 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59.rs @@ -0,0 +1,11 @@ +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_16; +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 0000000..f2961ac --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_10.rs @@ -0,0 +1,32 @@ +/// Calculates the time for passage (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 time_for_passage_equation(tp: String, p: String, fc: String) -> String { + format!("{} = \\frac{{{}}}{{{}}}", tp, p, fc) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + 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_11.rs b/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs new file mode 100644 index 0000000..13d7a6f --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_11.rs @@ -0,0 +1,46 @@ +/// Calculates the time for passage combining Equations 59.9 and 59.10 (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 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)] +mod tests { + use super::*; + + #[test] + 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); + } +} 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 0000000..dc46d0f --- /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); + } +} 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 0000000..81a9b55 --- /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); + } +} 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 0000000..1adc6e4 --- /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); + } +} 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 0000000..b30ee56 --- /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); + } +} 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 0000000..2e063f9 --- /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 0000000..dd916b0 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_6.rs @@ -0,0 +1,33 @@ +/// Calculates specific flow of evacuating persons (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 specific_flow_equation(fs: String, s: String, d: String) -> String { + format!("{} = {} \\cdot {}", fs, s, d) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + 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); + } +} 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 0000000..7ed8782 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_7.rs @@ -0,0 +1,36 @@ +/// Calculates specific flow by combining Equations 59.5 and 59.6 (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 specific_flow_equation(fs: String, a: String, d: String, k: String) -> String { + format!( + "{} = (1 - {} \\cdot {}) \\cdot {} \\cdot {}", + fs, a, d, k, d + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + 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 new file mode 100644 index 0000000..426c379 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_8.rs @@ -0,0 +1,34 @@ +/// Calculates the flow rate of persons passing a point in an exit route (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 calculated_flow_equation(fc: String, fs: String, we: String) -> String { + format!("{} = {} \\cdot {}", fc, fs, we) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + 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); + } +} 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 0000000..06fbd03 --- /dev/null +++ b/crates/sfpe_handbook/src/chapter_59/equation_59_9.rs @@ -0,0 +1,54 @@ +/// Calculates flow rate by combining Equations 59.7 and 59.8 (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 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)] +mod tests { + use super::*; + + #[test] + 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); + } +} diff --git a/crates/sfpe_handbook/src/lib.rs b/crates/sfpe_handbook/src/lib.rs index 3ca7985..59930f3 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;