ndspec/
lib.rs

1//! # ndspec
2//!
3//! `ndspec` is a crate for working with energy density spectra with a focus on ocean waves, wind, and related response spectra.
4//!
5//! The crate is organised into the following modules:
6//!
7//! * `core` - Core functionality for working with energy density spectra.
8//! * `waves` - Functions for calculating energy density spectra for ocean waves.
9//! * `wind` - Functions for calculating energy density spectra for wind.
10//! * `spectrum` - provides the Types `Spectrum1` and 'Spectrum2' for one-dimensional and two-dimensional spectra respectively.
11//!
12//! All wave and wind spectra can be converted into either a `Spectrum1` or
13//! `Spectrum2` Type. These types provide various traits for working
14//! with and evaluating energy density spectra.
15//!
16//! The crate is designed to be used in conjunction with the `ndarray`
17//! crate. The `ndarray::Array1` and `ndarray::Array2` types are
18//! underlying data structures adopted throughout.
19//!
20//! The crate also provides a Python extension that can be compiled and installed seperately.
21//!
22//! ## Installation
23//!
24//! Add the following to your `Cargo.toml` file:
25//!
26//! ```toml
27//! [dependencies]
28//! ndspec = "0.1.0"
29//! ```
30//!
31//! ## Usage
32//!
33//! The following example demonstrates how to calculate the energy density spectrum for a Bretschneider wave spectrum:
34//!
35//! ```
36//! use ndspec::prelude::*;
37//!
38//! let hs = 1.5;
39//! let tp = 18.0;
40//! let mut bretschneider = Bretschneider::new(hs, tp);
41//! let omega = Array1::linspace(0.1, PI, 100);
42//! let energy = bretschneider.set_omega(omega).energy();
43//! ```
44//!
45//! ## Python Extension
46//!
47//! The crate provides a Python extension that can be compiled and
48//! installed seperately. To build the extension, `maturin` is
49//! required and the `python-extension` feature must be enabled:
50//!
51//! ```bash
52//! maturin build --release --features python-extension
53//! ```
54//!
55//! and install with `pip`.
56//!
57//! ### Examples
58//!
59//! Define a Jonswap energy density spectrum from only Hs and Tp,
60//! convert to a `Spectrum1D` type, and then evaluate the most probable
61//! maximum amplitude over a 3 hour (10,800 s) time window:
62//! ```python
63//! import ndspec
64//! S = ndspec.Jonswap(hs=1.5, tp=10.0).to_spec1d()
65//! print(S.Ampm(10_800))
66//! ```
67//!
68//! Print out the help for the Jonswap class in Python::
69//! ```python
70//! import ndspec
71//! help(ndspec.Jonswap)
72//! ```
73//!
74#![cfg_attr(
75    feature = "doc-images",
76    cfg_attr(all(),
77             doc = ::embed_doc_image::embed_image!("label_matrix", "./assets/matrix.png")))
78]
79//!
80//! ![energy density data structure][label_matrix]
81//!
82
83//#![warn(missing_docs)]
84//#![warn(missing_doc_code_examples)]
85
86//
87pub mod azimuth;
88pub mod core;
89pub mod spectrums;
90pub mod waves;
91pub mod wind;
92
93//use embed_doc_image::embed_doc_image;
94
95#[cfg(feature = "python-extension")]
96use pyo3::prelude::*;
97
98/// ## The prelude
99///
100/// The purpose of this module is to:
101/// * define the namespace
102/// * keep all imports in a central location
103/// * help avoid the direct imports of traits defined by this crate in the parent module
104/// * provide a convenient and consistent way to import the main types, traits, and functions with a single import
105/// * provide access to useful external crate types and traits from a single import
106///
107/// ## Example:
108///
109/// ```
110/// use ndspec::prelude::*;
111/// ```
112pub mod prelude {
113
114    // constants
115    pub use crate::core::constants::{GRAVITY, KNOT, PI, RHO_AIR, RHO_SEA_WATER, TWO_PI};
116
117    // re-export of the core modules
118    pub use crate::core::ndarray_ext;
119    pub use crate::core::*;
120    pub use crate::spectrums::*;
121    pub use crate::waves::jonswap::{
122        convert_t1_to_tp, convert_tp_to_t1, convert_tp_to_tz, convert_tz_to_tp, lewis_allos, maxhs,
123        Jonswap,
124    };
125    pub use crate::waves::spreading::{spread_cos_2s, spread_cos_n, Spreading};
126    pub use crate::waves::{bretschneider, gaussian::gaussian_spectrum, jonswap, PiersonMoskowitz};
127    pub use crate::wind::*;
128
129    // re-export of external crate types and traits for convenience
130    pub use ndarray::{Array1, Array2};
131    pub use ndarray_stats::QuantileExt;
132
133    pub use crate::azimuth;
134}
135
136// convenience re-export of the prelude
137#[doc(hidden)]
138pub use crate::prelude::*;
139
140#[cfg(feature = "python-extension")]
141mod py_interface;
142
143#[cfg(feature = "python-extension")]
144#[pymodule]
145#[pyo3(name = "_libndspec")]
146fn ndspec(_py: Python, m: &PyModule) -> PyResult<()> {
147    m.add_function(wrap_pyfunction!(py_interface::version, m)?)?;
148
149    m.add_function(wrap_pyfunction!(py_interface::interp1, m)?)?;
150    m.add_function(wrap_pyfunction!(py_interface::interp2, m)?)?;
151
152    // py_azimuth.rs
153    m.add_function(wrap_pyfunction!(py_interface::convert_branch_180, m)?)?;
154    m.add_function(wrap_pyfunction!(py_interface::convert_branch_360, m)?)?;
155    m.add_function(wrap_pyfunction!(py_interface::rotation_matrix_2d, m)?)?;
156    m.add_function(wrap_pyfunction!(py_interface::convert_branch_180_vec, m)?)?;
157
158    // py_spectrums.rs
159    m.add_function(wrap_pyfunction!(py_interface::maxhs, m)?)?;
160    m.add_function(wrap_pyfunction!(py_interface::lewis_allos, m)?)?;
161    m.add_function(wrap_pyfunction!(py_interface::bretschneider, m)?)?;
162    m.add_function(wrap_pyfunction!(py_interface::jonswap, m)?)?;
163    m.add_function(wrap_pyfunction!(py_interface::gaussian, m)?)?;
164    m.add_function(wrap_pyfunction!(py_interface::convert_tp_to_tz, m)?)?;
165    m.add_function(wrap_pyfunction!(py_interface::convert_tz_to_tp, m)?)?;
166    m.add_function(wrap_pyfunction!(py_interface::convert_tp_to_t1, m)?)?;
167    m.add_function(wrap_pyfunction!(py_interface::convert_t1_to_tp, m)?)?;
168    m.add_function(wrap_pyfunction!(py_interface::spread_cos_n, m)?)?;
169    m.add_function(wrap_pyfunction!(py_interface::spread_cos_2s, m)?)?;
170    m.add_function(wrap_pyfunction!(py_interface::froya_10min, m)?)?;
171    m.add_function(wrap_pyfunction!(py_interface::froya_1hr, m)?)?;
172
173    m.add_class::<py_interface::Jonswap>()?;
174    m.add_class::<py_interface::Spreading>()?;
175    m.add_class::<py_interface::FrequencyResponse>()?;
176    m.add_class::<py_interface::Spectrum1D>()?;
177    m.add_class::<py_interface::Spectrum2D>()?;
178    Ok(())
179}