Files
OneCAD-Draft/crates/stormsewer/src/idf.rs
T
Michael FlynnandClaude Opus 4.8 9b44eb6400 Add Storm Sewer module: storm-drain network analysis (Rational + Manning + HGL)
Adds a "Storm Sewer" ribbon tab backed by a new in-repo `stormsewer` engine
crate (vendored under crates/). The engine implements standard public-domain
methods — Rational-method peak-flow accumulation, Manning circular-pipe
hydraulics, time-of-concentration / IDF intensity, and an HGL backwater pass
with junction losses — with 24 unit tests and no external dependencies.

Module (src/modules/storm_sewer/):
- Drafting: SS_INLET / SS_JUNCTION / SS_OUTFALL place structure markers and
  SS_PIPE chains pipe runs (interactive CadCommands).
- Analysis: SS_ANALYZE / SS_REPORT / SS_PROFILE open a .ssn network file, run
  the engine, draw the plan and HGL long-section, and print a report.
- SVG icons; SS_* commands registered for command-line autocomplete.

Integration is additive: build.rs auto-discovers the module and one dispatch
block is added to src/app/commands.rs. 7 module integration tests.

See src/modules/storm_sewer/INTEGRATION.md for the design and the optional
follow-up (drive analysis from canvas geometry via XDATA).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:16:30 -04:00

54 lines
1.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPDX-License-Identifier: GPL-3.0-or-later
//! Rainfall intensity–duration–frequency (IDF) curves.
//!
//! Uses the common three-parameter form
//!
//! ```text
//! i = a / (t + b)^c (i in in/hr, t in minutes)
//! ```
//!
//! which fits NOAA Atlas-14 partial-duration data well over the 5–180 min
//! range used in storm-sewer design.
/// A three-parameter IDF curve for a single return period.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct IdfCurve {
pub a: f64,
pub b: f64,
pub c: f64,
}
impl IdfCurve {
pub fn new(a: f64, b: f64, c: f64) -> Self {
Self { a, b, c }
}
/// Rainfall intensity (in/hr) for a storm duration `t_min` (minutes).
pub fn intensity(&self, t_min: f64) -> f64 {
let t = t_min.max(0.0);
self.a / (t + self.b).powf(self.c)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intensity_decreases_with_duration() {
let idf = IdfCurve::new(120.0, 10.0, 0.8);
let short = idf.intensity(5.0);
let long = idf.intensity(60.0);
assert!(short > long, "short {short} long {long}");
assert!(long > 0.0);
}
#[test]
fn intensity_known_value() {
// a=120,b=10,c=0.8 ; t=15 -> 120 / 25^0.8 = 9.14 in/hr (hand calc).
let idf = IdfCurve::new(120.0, 10.0, 0.8);
assert!((idf.intensity(15.0) - 9.14).abs() < 0.05, "{}", idf.intensity(15.0));
}
}