conspire 0.6.0

The Rust interface to conspire.
Documentation
#[cfg(test)]
mod test;

use crate::{
    fem::block::element::{
        ElementNodalEitherCoordinates, FRAC_SQRT_3_5, FiniteElement, ParametricCoordinate,
        ParametricCoordinates, ParametricReference, ShapeFunctions, ShapeFunctionsGradients,
        quadratic::{M, QuadraticElement, QuadraticFiniteElement},
    },
    math::{Scalar, ScalarList, TensorRank1},
};

const G: usize = 27;
const N: usize = 13;
const P: usize = N;

pub type Pyramid = QuadraticElement<G, N>;

impl FiniteElement<G, M, N, P> for Pyramid {
    fn integration_points() -> ParametricCoordinates<G, M> {
        const X: [f64; 3] = [0.294997790111502, 0.652996233961648, 0.927005975926850];
        let u1_2d = [
            -FRAC_SQRT_3_5,
            0.0,
            FRAC_SQRT_3_5,
            -FRAC_SQRT_3_5,
            0.0,
            FRAC_SQRT_3_5,
            -FRAC_SQRT_3_5,
            0.0,
            FRAC_SQRT_3_5,
        ];
        let u2_2d = [
            -FRAC_SQRT_3_5,
            -FRAC_SQRT_3_5,
            -FRAC_SQRT_3_5,
            0.0,
            0.0,
            0.0,
            FRAC_SQRT_3_5,
            FRAC_SQRT_3_5,
            FRAC_SQRT_3_5,
        ];
        X.into_iter()
            .flat_map(|x| {
                u1_2d
                    .into_iter()
                    .zip(u2_2d)
                    .map(move |(u1, u2)| TensorRank1::from([x * u1, x * u2, 1.0 - x]))
            })
            .collect()
    }
    fn integration_weights(&self) -> &ScalarList<G> {
        &self.integration_weights
    }
    fn parametric_reference() -> ParametricReference<M, N> {
        [
            [-1.0, -1.0, 0.0],
            [1.0, -1.0, 0.0],
            [1.0, 1.0, 0.0],
            [-1.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [0.0, -1.0, 0.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [-1.0, 0.0, 0.0],
            [-0.5, -0.5, 0.5],
            [0.5, -0.5, 0.5],
            [0.5, 0.5, 0.5],
            [-0.5, 0.5, 0.5],
        ]
        .into()
    }
    fn parametric_weights() -> ScalarList<G> {
        const B: [f64; 3] = [0.029950703008581, 0.146246269259866, 0.157136361064887];
        const W1: f64 = 5.0 / 9.0;
        const W2: f64 = 8.0 / 9.0;
        let w_2d = [
            W1 * W1,
            W2 * W1,
            W1 * W1,
            W1 * W2,
            W2 * W2,
            W1 * W2,
            W1 * W1,
            W2 * W1,
            W1 * W1,
        ];
        B.into_iter()
            .flat_map(|b| w_2d.into_iter().map(move |w| w * b))
            .collect()
    }
    fn scaled_jacobians<const I: usize>(
        _nodal_coordinates: ElementNodalEitherCoordinates<I, N>,
    ) -> ScalarList<P> {
        todo!()
    }
    fn shape_functions(parametric_coordinate: ParametricCoordinate<M>) -> ShapeFunctions<N> {
        let [xi_1, xi_2, xi_3] = parametric_coordinate.into();
        let bottom = bottom(xi_3);
        [
            0.25 * (-xi_1 - xi_2 - 1.0)
                * ((1.0 - xi_1) * (1.0 - xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom),
            0.25 * (xi_1 - xi_2 - 1.0)
                * ((1.0 + xi_1) * (1.0 - xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom),
            0.25 * (xi_1 + xi_2 - 1.0)
                * ((1.0 + xi_1) * (1.0 + xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom),
            0.25 * (-xi_1 + xi_2 - 1.0)
                * ((1.0 - xi_1) * (1.0 + xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom),
            xi_3 * (2.0 * xi_3 - 1.0),
            0.5 * (1.0 + xi_1 - xi_3) * (1.0 - xi_1 - xi_3) * (1.0 - xi_2 - xi_3) / bottom,
            0.5 * (1.0 + xi_2 - xi_3) * (1.0 - xi_2 - xi_3) * (1.0 + xi_1 - xi_3) / bottom,
            0.5 * (1.0 + xi_1 - xi_3) * (1.0 - xi_1 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
            0.5 * (1.0 + xi_2 - xi_3) * (1.0 - xi_2 - xi_3) * (1.0 - xi_1 - xi_3) / bottom,
            xi_3 * (1.0 - xi_1 - xi_3) * (1.0 - xi_2 - xi_3) / bottom,
            xi_3 * (1.0 + xi_1 - xi_3) * (1.0 - xi_2 - xi_3) / bottom,
            xi_3 * (1.0 + xi_1 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
            xi_3 * (1.0 - xi_1 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
        ]
        .into()
    }
    fn shape_functions_gradients(
        parametric_coordinate: ParametricCoordinate<M>,
    ) -> ShapeFunctionsGradients<M, N> {
        let [xi_1, xi_2, xi_3] = parametric_coordinate.into();
        let bottom = bottom(xi_3);
        let bottom_squared = bottom * bottom;
        [
            [
                0.25 * ((-xi_1 - xi_2 - 1.0) * (-1.0 + xi_2 + xi_2 * xi_3 / bottom)
                    - ((1.0 - xi_1) * (1.0 - xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * ((-xi_1 - xi_2 - 1.0) * (-1.0 + xi_1 + xi_1 * xi_3 / bottom)
                    - ((1.0 - xi_1) * (1.0 - xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * (-xi_1 - xi_2 - 1.0)
                    * (-1.0 + xi_1 * xi_2 / bottom + xi_1 * xi_2 * xi_3 / bottom_squared),
            ],
            [
                0.25 * ((xi_1 - xi_2 - 1.0) * (1.0 - xi_2 - xi_2 * xi_3 / bottom)
                    + ((1.0 + xi_1) * (1.0 - xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * ((xi_1 - xi_2 - 1.0) * (-1.0 - xi_1 - xi_1 * xi_3 / bottom)
                    - ((1.0 + xi_1) * (1.0 - xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * (xi_1 - xi_2 - 1.0)
                    * (-1.0 - xi_1 * xi_2 / bottom - xi_1 * xi_2 * xi_3 / bottom_squared),
            ],
            [
                0.25 * ((xi_1 + xi_2 - 1.0) * (1.0 + xi_2 + xi_2 * xi_3 / bottom)
                    + ((1.0 + xi_1) * (1.0 + xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * ((xi_1 + xi_2 - 1.0) * (1.0 + xi_1 + xi_1 * xi_3 / bottom)
                    + ((1.0 + xi_1) * (1.0 + xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * (xi_1 + xi_2 - 1.0)
                    * (-1.0 + xi_1 * xi_2 / bottom + xi_1 * xi_2 * xi_3 / bottom_squared),
            ],
            [
                0.25 * ((-xi_1 + xi_2 - 1.0) * (-1.0 - xi_2 - xi_2 * xi_3 / bottom)
                    - ((1.0 - xi_1) * (1.0 + xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * ((-xi_1 + xi_2 - 1.0) * (1.0 - xi_1 - xi_1 * xi_3 / bottom)
                    + ((1.0 - xi_1) * (1.0 + xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
                0.25 * (-xi_1 + xi_2 - 1.0)
                    * (-1.0 - xi_1 * xi_2 / bottom - xi_1 * xi_2 * xi_3 / bottom_squared),
            ],
            [0.0, 0.0, 4.0 * xi_3 - 1.0],
            [
                -xi_1 * (1.0 - xi_2 - xi_3) / bottom,
                -0.5 * (1.0 - xi_1 - xi_3) * (1.0 + xi_1 - xi_3) / bottom,
                0.5 * (xi_1 * xi_1 * xi_2 / bottom_squared + xi_2) - 1.0 + xi_3,
            ],
            [
                0.5 * (1.0 - xi_2 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
                -xi_2 * (1.0 + xi_1 - xi_3) / bottom,
                -0.5 * (xi_1 * xi_2 * xi_2 / bottom_squared + xi_1) - 1.0 + xi_3,
            ],
            [
                -xi_1 * (1.0 + xi_2 - xi_3) / bottom,
                0.5 * (1.0 - xi_1 - xi_3) * (1.0 + xi_1 - xi_3) / bottom,
                -0.5 * (xi_1 * xi_1 * xi_2 / bottom_squared + xi_2) - 1.0 + xi_3,
            ],
            [
                -0.5 * (1.0 - xi_2 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
                -xi_2 * (1.0 - xi_1 - xi_3) / bottom,
                0.5 * (xi_1 * xi_2 * xi_2 / bottom_squared + xi_1) - 1.0 + xi_3,
            ],
            [
                -(1.0 - xi_2 - xi_3) * xi_3 / bottom,
                -(1.0 - xi_1 - xi_3) * xi_3 / bottom,
                xi_1 * xi_2 / bottom_squared + 1.0 - xi_1 - xi_2 - 2.0 * xi_3,
            ],
            [
                (1.0 - xi_2 - xi_3) * xi_3 / bottom,
                -(1.0 + xi_1 - xi_3) * xi_3 / bottom,
                -xi_1 * xi_2 / bottom_squared + 1.0 + xi_1 - xi_2 - 2.0 * xi_3,
            ],
            [
                (1.0 + xi_2 - xi_3) * xi_3 / bottom,
                (1.0 + xi_1 - xi_3) * xi_3 / bottom,
                xi_1 * xi_2 / bottom_squared + 1.0 + xi_1 + xi_2 - 2.0 * xi_3,
            ],
            [
                -(1.0 + xi_2 - xi_3) * xi_3 / bottom,
                (1.0 - xi_1 - xi_3) * xi_3 / bottom,
                -xi_1 * xi_2 / bottom_squared + 1.0 - xi_1 + xi_2 - 2.0 * xi_3,
            ],
        ]
        .into()
    }
}

fn bottom(xi_3: Scalar) -> Scalar {
    const SMALL: Scalar = 4e1 * f64::EPSILON;
    if (1.0 - xi_3).abs() > SMALL {
        1.0 - xi_3
    } else {
        SMALL
    }
}

impl QuadraticFiniteElement<G, N> for Pyramid {}