use ndarray::{Array1, ArrayView1};
use crate::prelude::*;
use crate::waves::common::SpectralCommon;
const N_FREQ: usize = 256;
pub struct PiersonMoskowitz {
pub hs: f64,
pub omega: Array1<f64>,
}
pub fn energy(omega: ArrayView1<f64>, hs: f64) -> Array1<f64> {
let a = 8.1e-3;
omega
.iter()
.map(|w| {
a * GRAVITY.powf(2.)
* w.powf(-5.)
* (-0.032 * (GRAVITY / hs).powf(2.) * w.powf(-4.)).exp()
})
.collect()
}
impl Default for PiersonMoskowitz {
fn default() -> Self {
PiersonMoskowitz {
hs: 1.0,
omega: Array1::<f64>::linspace(0.1, PI, N_FREQ),
}
}
}
impl SpectralCommon for PiersonMoskowitz {
fn hs(&self) -> Result<f64, String> {
Ok(self.hs)
}
fn tp(&self) -> Result<f64, String> {
match self.wp() {
Ok(wp) => Ok(TWO_PI / wp),
Err(e) => Err(e),
}
}
fn wp(&self) -> Result<f64, String> {
match self.hs {
0.0 => Err("hs is zero".to_string()),
_ => Ok(0.4 * (GRAVITY / self.hs).sqrt()),
}
}
fn omega(&self) -> &Array1<f64> {
&self.omega
}
fn energy(&self) -> Array1<f64> {
energy(self.omega.view(), self.hs)
}
}
impl PiersonMoskowitz {
pub fn new(hs: f64) -> Self {
PiersonMoskowitz {
hs,
..Default::default()
}
}
pub fn set_hs(&mut self, hs: f64) -> &mut Self {
self.hs = hs;
self
}
pub fn wind_speed(&self) -> f64 {
(GRAVITY * self.hs / 0.21).sqrt()
}
pub fn set_omega(&mut self, omega: Array1<f64>) -> &mut Self {
self.omega = omega;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
const N: usize = 128;
#[test]
fn test_energy() {
let hs = 1.5;
let mut pm = PiersonMoskowitz::new(hs);
let omega = Array1::linspace(0.1, PI, N);
let energy = pm.set_omega(omega).energy();
assert_eq!(energy.len(), N);
}
#[test]
fn test_abs_error() {
let hs = 1.5;
let mut pm = PiersonMoskowitz::new(hs);
let omega = Array1::linspace(0.1, PI, N);
let abs_error = pm.set_omega(omega).abs_error();
assert!(abs_error < Ok(0.1));
}
}