magba 0.5.0

Magnetic computation library for Rust
Documentation
/*
 * Magba is licensed under The 3-Clause BSD, see LICENSE.
 * Copyright 2025 Sira Pornsiriprasert <code@psira.me>
 */

use nalgebra::Vector3;

use crate::crate_utils::define_source;

define_source! {
    /// Cylinder magnet source.
    ///
    /// # References
    ///
    /// - Caciagli, Alessio, Roel J. Baars, Albert P. Philipse, and Bonny W. M. Kuipers. “Exact Expression for the Magnetic Field of a Finite Cylinder with Arbitrary Uniform Magnetization.” Journal of Magnetism and Magnetic Materials 456 (June 15, 2018): 423–32. <https://doi.org/10.1016/j.jmmm.2018.02.003>.
    /// - Derby, Norman, and Stanislaw Olbert. “Cylindrical Magnets and Ideal Solenoids.” American Journal of Physics 78, no. 3 (March 1, 2010): 229–35. <https://doi.org/10.1119/1.3256157>.
    /// - Ortner, Michael, and Lucas Gabriel Coliado Bandeira. “Magpylib: A Free Python Package for Magnetic Field Computation.” SoftwareX 11 (January 1, 2020): 100466. <https://doi.org/10.1016/j.softx.2020.100466>.
    CylinderMagnet
    field_fn: cylinder_B
    args: {
        polarization: Vector3<T> = Vector3::z(),
        diameter: T = T::one();
            validate diameter > T::zero();
            error "Diameter cannot be negative.",
        height: T = T::one();
            validate height > T::zero();
            error "Height cannot be negative.",
    }
    arg_display: "pol={}, d={}, h={}";
    arg_fmt: [format_vector3, format_float, format_float]
    docs: {
        new: {
            /// Construct a [CylinderMagnet].
            ///
            /// # Examples
            ///
            /// ```
            /// # use magba::magnets::CylinderMagnet;
            /// # use nalgebra::*;
            /// let magnet = CylinderMagnet::new(
            ///     [0.0, 0.0, 0.0],              // position: Center of the cylinder (m)
            ///     UnitQuaternion::identity(),   // orientation as unit quaternion
            ///     [0.0, 0.0, 1.0],              // polarization (T)
            ///     0.01,                         // diameter (m)
            ///     0.02,                         // height (m)
            /// );
            /// ```
        }
    }
}

#[cfg(all(test, feature = "std"))]
crate::testing_util::generate_tests! {
    CylinderMagnet
    filename: cylinder
    params: { polarization: vector![1.0, 2.0, 3.0], d: 0.1, h: 0.2}
    rtols: {
        static: 5e-10,
        static_small: 5e-10,
        translate: 2e-10,
        rotate: 2e-10,
    }
    p95_rtols: {
        static: 5e-10,
        static_small: 5e-10,
        translate: 5e-10,
        rotate: 5e-10,
    }
    f32_rtols: {
        static: 5e-2,
        static_small: 5e-2,
        translate: 5e-2,
        rotate: 1e-2,
    }
    f32_p95_rtols: {
        static: 5e-4,
        static_small: 5e-4,
        translate: 5e-4,
        rotate: 5e-4,
    }
}

#[cfg(test)]
mod tests {
    use crate::magnets::CylinderMagnet;

    #[test]
    #[should_panic]
    fn test_init_validation() {
        let _ = CylinderMagnet::<f64>::new(
            [0.0; 3],
            nalgebra::UnitQuaternion::identity(),
            [0.0, 0.0, 1.0],
            -1.0_f64,
            1.0_f64,
        );
    }

    #[test]
    #[should_panic]
    fn test_set_diameter_validation() {
        let mut magnet = CylinderMagnet::<f64>::default();
        magnet.set_diameter(-1.0_f64);
    }

    #[test]
    #[should_panic]
    fn test_with_diameter_validation() {
        let _: CylinderMagnet<f64> = CylinderMagnet::<f64>::default().with_diameter(-1.0_f64);
    }

    #[test]
    #[should_panic]
    fn test_set_height_validation() {
        let mut magnet = CylinderMagnet::<f64>::default();
        magnet.set_height(-1.0_f64);
    }

    #[test]
    #[should_panic]
    fn test_with_height_validation() {
        let _: CylinderMagnet<f64> = CylinderMagnet::<f64>::default().with_height(-1.0_f64);
    }
}