Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion crates/python_api/docs/api/sfpe-handbook.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
: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
~~~~~~~~~~~~~~
Comment on lines +150 to +199
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new Chapter 59 section uses headings like “Equation 59.5” without short descriptions, while the rest of this file uses the “Equation X.Y - Description” heading style. Consider adding brief descriptions to these Chapter 59 equation headings (and adjust underline lengths accordingly) to keep the API docs consistent and scannable.

Suggested change
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
~~~~~~~~~~~~~~
Equation 59.5 - SFPE Handbook Chapter 59 correlation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_5
:members:
:undoc-members:
:show-inheritance:
Equation 59.6 - SFPE Handbook Chapter 59 correlation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_6
:members:
:undoc-members:
:show-inheritance:
Equation 59.7 - SFPE Handbook Chapter 59 correlation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_7
:members:
:undoc-members:
:show-inheritance:
Equation 59.8 - SFPE Handbook Chapter 59 correlation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_8
:members:
:undoc-members:
:show-inheritance:
Equation 59.9 - SFPE Handbook Chapter 59 correlation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_9
:members:
:undoc-members:
:show-inheritance:
Equation 59.10 - SFPE Handbook Chapter 59 correlation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_10
:members:
:undoc-members:
:show-inheritance:
Equation 59.11 - SFPE Handbook Chapter 59 correlation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Copilot uses AI. Check for mistakes.

.. automodule:: ofire.sfpe_handbook.chapter_59.equation_59_11
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions crates/python_api/src/sfpe_handbook.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod chapter_14;
pub mod chapter_50;
pub mod chapter_59;

use pyo3::prelude::*;
use pyo3::wrap_pymodule;
Expand All @@ -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(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ fn stairwell_temperature(t_0: f64, eta: f64, t_b: f64) -> PyResult<f64> {
pub fn equation_50_17(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(stairwell_temperature, m)?)?;
Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ fn visibility(k: f64, l: f64, lambda: f64) -> PyResult<f64> {
pub fn equation_50_20(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(visibility, m)?)?;
Ok(())
}
}
25 changes: 25 additions & 0 deletions crates/python_api/src/sfpe_handbook/chapter_59.rs
Original file line number Diff line number Diff line change
@@ -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.
Comment on lines +13 to +15
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module-level docstring says this chapter contains “placeholder equations”, but equations 59.5–59.9 are implemented and documented. Consider updating the chapter description to reflect the hydraulic/egress flow model and explicitly note which equations are still unimplemented (59.10/59.11).

Suggested change
/// Chapter 59 - Placeholder equations.
///
/// This chapter contains placeholder equations that will be implemented.
/// Chapter 59 - Hydraulic egress flow model.
///
/// This chapter exposes equations for the hydraulic model of occupant flow
/// (Equations 59.5–59.9). Equations 59.10 and 59.11 are currently provided
/// as placeholders and may not be fully implemented.

Copilot uses AI. Check for mistakes.
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(())
}
36 changes: 36 additions & 0 deletions crates/python_api/src/sfpe_handbook/chapter_59/equation_59_10.rs
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +4 to +25
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module is exposed via autodoc but the docstring is mostly TODO placeholders (equation/variables/args/returns). Consider removing Equation 59.10 from the Python module/docs until implemented, or fully documenting the intended signature and variables now so published docs aren’t incomplete.

Suggested change
/// 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
/// SFPE Handbook Equation 59.10 (placeholder).
///
/// This function is a placeholder for Equation 59.10 from the SFPE Handbook.
/// The equation has not yet been implemented, and this function will always
/// raise :class:`NotImplementedError` when called.
///
/// Example:
/// >>> import ofire
/// >>> from ofire.sfpe_handbook.chapter_59 import equation_59_10
/// >>> try:
/// ... equation_59_10.equation_59_10_placeholder()
/// ... except NotImplementedError:
/// ... print("Equation 59.10 is not yet implemented")

Copilot uses AI. Check for mistakes.
fn equation_59_10_placeholder() -> PyResult<f64> {
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(())
}
36 changes: 36 additions & 0 deletions crates/python_api/src/sfpe_handbook/chapter_59/equation_59_11.rs
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +4 to +25
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module is exposed via autodoc but the docstring is mostly TODO placeholders (equation/variables/args/returns). Consider removing Equation 59.11 from the Python module/docs until implemented, or fully documenting the intended signature and variables now so published docs aren’t incomplete.

Suggested change
/// 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
/// Equation 59.11 placeholder – not yet implemented.
///
/// This Python binding reserves the API name for Equation 59.11 from the
/// SFPE Handbook, Chapter 59. The underlying engineering calculation has
/// not yet been implemented in this library.
///
/// Calling this function will always raise a :class:`NotImplementedError`
/// in Python to indicate that the equation is currently unavailable.
///
/// Example:
/// >>> import ofire
/// >>> from ofire.sfpe_handbook import chapter_59
/// >>> # This call will raise NotImplementedError until implemented
/// >>> chapter_59.equation_59_11.equation_59_11_placeholder()

Copilot uses AI. Check for mistakes.
fn equation_59_11_placeholder() -> PyResult<f64> {
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(())
}
39 changes: 39 additions & 0 deletions crates/python_api/src/sfpe_handbook/chapter_59/equation_59_5.rs
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +14 to +25
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the where: section, the variable definitions omit units/dimensionless markers (unlike other SFPE docstrings in this repo). Adding units for S, k, a, and D would make the equation easier to apply correctly across SI vs imperial usage.

Suggested change
/// - :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
/// - :math:`S` is the speed along the line of travel (m/s or ft/s, consistent with :math:`k`)
/// - :math:`k` is the speed constant from Table 59.2 (m/s or ft/s)
/// - :math:`a` is the unit conversion constant (m²/person for SI, ft²/person for imperial)
/// - :math:`D` is the population density (persons/m² or persons/ft²)
///
/// Args:
/// k (float): Speed constant from Table 59.2 (m/s or ft/s)
/// a (float): Unit conversion constant (m²/person for SI, ft²/person for imperial)
/// d (float): Population density (persons/m² or persons/ft²)
///
/// Returns:
/// float: Speed :math:`S` along the line of travel (m/s or ft/s, consistent with :math:`k`)

Copilot uses AI. Check for mistakes.
///
/// 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<f64> {
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(())
}
40 changes: 40 additions & 0 deletions crates/python_api/src/sfpe_handbook/chapter_59/equation_59_6.rs
Original file line number Diff line number Diff line change
@@ -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<f64> {
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(())
}
42 changes: 42 additions & 0 deletions crates/python_api/src/sfpe_handbook/chapter_59/equation_59_7.rs
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +18 to +25
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The where: section omits units/dimensionless markers for a, D, and k. Adding units (or explicitly stating dimensionless where applicable) would make the doc consistent with other SFPE equation docstrings and reduce ambiguity between SI vs imperial inputs.

Suggested change
/// - :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
/// - :math:`a` is the unit conversion constant (m²/person or ft²/person; 0.266 for SI, 2.86 for imperial)
/// - :math:`D` is the population density (persons/m² or persons/ft²)
/// - :math:`k` is the speed constant from Table 59.2 (m/s or ft/min, consistent with the chosen unit system)
///
/// Args:
/// a (float): Unit conversion constant (m²/person or ft²/person; 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 (m/s or ft/min, consistent with the chosen unit system)

Copilot uses AI. Check for mistakes.
///
/// 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<f64> {
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(())
}
41 changes: 41 additions & 0 deletions crates/python_api/src/sfpe_handbook/chapter_59/equation_59_8.rs
Original file line number Diff line number Diff line change
@@ -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<f64> {
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(())
}
Loading
Loading