physics_in_parallel 3.0.3

High-performance infrastructure for numerical simulations in physics
Documentation
use physics_in_parallel::math::tensor::rank_2::vector_list::VectorList;
use physics_in_parallel::models::particles::attrs::{
    ATTR_M_INV, ATTR_V, ParticleSelection, set_alive,
};
use physics_in_parallel::models::particles::create_state::create_template;
use physics_in_parallel::models::particles::observe::{
    KineticEnergyObserver, ObserveError, Observer, TemperatureObserver,
};

#[test]
fn kinetic_energy_uses_inverse_mass_and_alive_mask() {
    let mut obj = create_template(2, 3).unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_V, 0, &[3.0, 4.0])
        .unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_V, 1, &[1.0, 0.0])
        .unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_V, 2, &[2.0, 0.0])
        .unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_M_INV, 0, &[0.5])
        .unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_M_INV, 1, &[1.0])
        .unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_M_INV, 2, &[2.0])
        .unwrap();
    set_alive(&mut obj, 1, false).unwrap();

    let ke = KineticEnergyObserver::default().observe(&obj).unwrap();
    assert_eq!(ke, 0.5 * 25.0 / 0.5 + 0.5 * 4.0 / 2.0);

    let ke_all = KineticEnergyObserver::new(ParticleSelection::All)
        .observe(&obj)
        .unwrap();
    assert_eq!(ke_all, ke + 0.5);
}

#[test]
fn temperature_uses_active_particle_degrees_of_freedom() {
    let mut obj = create_template(2, 2).unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_V, 0, &[1.0, 0.0])
        .unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_V, 1, &[0.0, 2.0])
        .unwrap();
    set_alive(&mut obj, 1, false).unwrap();

    let temp = TemperatureObserver::default().observe(&obj).unwrap();
    assert_eq!(temp, 2.0 * 0.5 / 2.0);

    let temp_all = TemperatureObserver::new(ParticleSelection::All)
        .observe(&obj)
        .unwrap();
    assert_eq!(temp_all, 2.0 * 2.5 / 4.0);
}

#[test]
fn temperature_returns_zero_when_no_particles_are_active() {
    let mut obj = create_template(2, 2).unwrap();
    set_alive(&mut obj, 0, false).unwrap();
    set_alive(&mut obj, 1, false).unwrap();

    let temp = TemperatureObserver::default().observe(&obj).unwrap();
    assert_eq!(temp, 0.0);
}

#[test]
fn observer_reports_invalid_numeric_state() {
    let mut obj = create_template(1, 2).unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_M_INV, 0, &[0.0])
        .unwrap();

    assert_eq!(
        KineticEnergyObserver::default().observe(&obj).unwrap_err(),
        ObserveError::InvalidState {
            field: ATTR_M_INV,
            value: 0.0,
        }
    );

    obj.core
        .set_vector_of::<f64>(ATTR_M_INV, 0, &[1.0])
        .unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_V, 1, &[f64::INFINITY])
        .unwrap();

    assert_eq!(
        KineticEnergyObserver::default().observe(&obj).unwrap_err(),
        ObserveError::InvalidState {
            field: ATTR_V,
            value: f64::INFINITY,
        }
    );
}

#[test]
fn alive_only_ignores_invalid_velocity_on_dead_particle_but_all_reports_it() {
    let mut obj = create_template(1, 2).unwrap();
    obj.core.set_vector_of::<f64>(ATTR_V, 0, &[1.0]).unwrap();
    obj.core
        .set_vector_of::<f64>(ATTR_V, 1, &[f64::INFINITY])
        .unwrap();
    set_alive(&mut obj, 1, false).unwrap();

    let ke = KineticEnergyObserver::default().observe(&obj).unwrap();
    assert_eq!(ke, 0.5);

    assert_eq!(
        KineticEnergyObserver::new(ParticleSelection::All)
            .observe(&obj)
            .unwrap_err(),
        ObserveError::InvalidState {
            field: ATTR_V,
            value: f64::INFINITY,
        }
    );
}

#[test]
fn observer_reports_shape_errors() {
    let mut obj = create_template(1, 2).unwrap();
    obj.core.remove(ATTR_M_INV).unwrap();
    obj.core
        .insert(ATTR_M_INV, VectorList::<f64>::empty(2, 2))
        .unwrap();

    assert_eq!(
        KineticEnergyObserver::default().observe(&obj).unwrap_err(),
        ObserveError::InvalidAttrShape {
            label: ATTR_M_INV,
            expected_dim: 1,
            got_dim: 2,
        }
    );
}