# funspace
## Funspace
Collection of function spaces.
## Bases
| Name | Transform-type | Orthogonal | Boundary conditions | Link |
|--------------------------|----------------|------------|--------------------------------------|------------------------------|
| ``Chebyshev`` | R2r | True | None | [`chebyshev()`] |
| ``ChebDirichlet `` | R2r | False | u(-1) = u(1) = 0 | [`cheb_dirichlet()`] |
| ``ChebNeumann`` | R2r | False | u'(-1) = u'(1) = 0 | [`cheb_neumann()`] |
| ``ChebDirichletNeumann`` | R2r | False | u(-1) = u'(1) = 0 | [`cheb_dirichlet_neumann()`] |
| ``ChebBiHarmonicA`` | R2r | False | u(-1) = u'(-1) = u(1)= u'(1) = 0 | [`cheb_biharmonic_a()`] |
| ``ChebBiHarmonicB`` | R2r | False | u(-1) = u''(-1) = u(1)= u''(1) = 0 | [`cheb_biharmonic_b()`] |
| ``FourierR2c`` | R2c | True | Periodic | [`fourier_r2c()`] |
| ``FourierC2c`` | C2c | True | Periodic | [`fourier_c2c()`] |
### Transform
A transformation describes a conversion from physical values to spectral coefficients,
and vice versa. A fourier transform, for example, of a function gives complex-valued
coefficients representing the sinusoid functions. A chebyshev transform gives
real-valued spectral coefficients, representing Chebyshev polynomials.
The transformations are implemented by the [`BaseTransform`] trait.
#### Example
Apply forward transform of 1d array in `cheb_dirichlet` space
```rust
use funspace::traits::BaseTransform;
use funspace::cheb_dirichlet;
use ndarray::{Array1, array};
let mut cd = cheb_dirichlet::(5);
let input = array![1., 2., 3., 4., 5.];
let output: Array1 = cd.forward(&input, 0);
```
### Differentiation
Differentiation in spectral space is generally efficient and very accurate.
Differentiation in Fourier space becomes a simple multiplication of the spectral
coefficients with the wavenumber vector.
Differentiation in Chebyshev space is done by a recurrence
relation and almost as fast as in Fourier space.
Each base implements a differentiation method, which is applied to
an array of coefficents. This is defined by the [`BaseGradient`] trait.
#### Example
Differentiation
```rust
use funspace::fourier_r2c;
use funspace::traits::{BaseTransform, BaseElements, BaseGradient};
use ndarray::Array1;
use num_complex::Complex;
// Define base
let mut fo = fourier_r2c(8);
// Get coordinates in physical space
let x: Vec = fo.coords().clone();
let v: Array1 = x
.iter()
.map(|x| (2. * x).sin())
.collect::>()
.into();
// Transform to physical space
let vhat: Array1> = fo.forward(&v, 0);
// Apply differentiation twice along first axis
let dvhat = fo.gradient(&vhat, 2, 0);
// Transform back to spectral space
let dv: Array1 = fo.backward(&dvhat, 0);
// Compare with correct derivative
for (exp, ist) in x
.iter()
.map(|x| -4. * (2. * x).sin())
.collect::>()
.iter()
.zip(dv.iter())
{
assert!((exp - ist).abs() < 1e-5);
}
```
### Orthogonal and composite Bases
Function spaces such as those of Fourier polynomials or Chebyshev polynomials are
are considered *orthogonal*, i.e. the dot product of every single
polynomial with any other polynomial in its set vanishes. In this cases
the mass matrix is a purely diagonal matrix.
However, other function spaces can be constructed by a linear combination
of the orthogonal basis functions, i.e. they are *composite* bases. In this way, function
spaces can be constructed that satisfy certain boundary conditions such as Dirichlet
(u(x0) = 0) or Neumann (u'(x0) = 0).
This is used to solve partial differential equations (see *Galerkin* method).
To switch from its composite form to the orthogonal form, each base implements
a [`BaseFromOrtho`] trait, which defines the transform `to_ortho` and `from_ortho`.
If the base is already orthogonal, the input will be returned, otherwise it
is transformed from the composite space to the orthogonal space (`to_ortho`), or vice versa
(`from_ortho`).
Note that the size of the composite space *m* is usually less than its orthogonal
counterpart *n*, i.e. the spectral representation has less coefficients. In other words,
the composite space is a lower dimensional subspace of the orthogonal space.
#### Example
Transform composite space `cheb_dirichlet` to its orthogonal counterpart
`chebyshev`. Note that `cheb_dirichlet` has 6 spectral coefficients,
while the `chebyshev` bases has 8.
```rust
use funspace::traits::{BaseTransform, BaseElements, BaseFromOrtho};
use funspace::{cheb_dirichlet, chebyshev};
use std::f64::consts::PI;
use ndarray::prelude::*;
use ndarray::Array1;
use num_complex::Complex;
// Define base
let ch = chebyshev(8);
let cd = cheb_dirichlet(8);
// Get coordinates
let x: Vec = ch.coords().clone();
let v: Array1 = x
.iter()
.map(|x| (PI / 2. * x).cos())
.collect::>()
.into();
// Transform to spectral space
let ch_vhat: Array1 = ch.forward(&v, 0);
let cd_vhat: Array1 = cd.forward(&v, 0);
// Send array to orthogonal space (cheb_dirichlet -> chebyshev)
let cd_vhat_ortho = cd.to_ortho(&cd_vhat, 0);
// Both arrays are equal, because field was
// initialized with dirichlet boundary conditions.
for (exp, ist) in ch_vhat.iter().zip(cd_vhat_ortho.iter()) {
assert!((exp - ist).abs() < 1e-5);
}
// However, if the function values do not satisfy
// dirichlet boundary conditions, they will
// be enforced by the transform to the cheb_dirichlet function
// space, thus the transformed values will deviate
// from a pure chebyshev transform (which does not
// enfore the boundary conditions).
let mut v: Array1 = x
.iter()
.map(|x| (PI / 2. * x).sin())
.collect::>()
.into();
let ch_vhat: Array1 = ch.forward(&v, 0);
let cd_vhat: Array1 = cd.forward(&v, 0);
let cd_vhat_ortho = cd.to_ortho(&cd_vhat, 0);
// They will deviate
println!("chebyshev : {:?}", ch_vhat);
println!("cheb_dirichlet: {:?}", cd_vhat_ortho);
```
### MPI Support (Feature)
`Funspace` comes with limited mpi support. Currently this is restricted
to 2D spaces. Under the hood it uses a fork of the rust mpi libary
which requires an existing MPI implementation
and `libclang`.
Activate the feature in your ``Cargo.toml``
```
funspace = {version = "0.4", features = ["mpi"]}
```
#### Examples
`examples/space_mpi.rs`
Install `cargo mpirun`, then
```rust
cargo mpirun --np 2 --example space_mpi --features="mpi"
```
## Versions
- v0.4.0: Major API changes + New methods
- v0.3.0: Major API changes + Performance improvements
License: MIT