hfss_fld 0.1.2

Ansys HFSS `.fld` file parser
Documentation
mod fld_file_loader;

pub use fld_file_loader::{DataCoordinates, DataDimensionality, FileData};
use std::collections::{BTreeMap, BTreeSet};

// pub fn load_b1_file(path_and_name: &str) -> FileData {
//     match FileData::load_file(path_and_name, 1) {
//         Ok(file_data) => file_data,
//         Err(msg) => panic!("Unable to read file! \n {}", msg),
//     }
// }

// pub fn load_complex_h_file(path_and_name: &str) -> FileData {
//     match FileData::load_file(path_and_name, 6) {
//         Ok(file_data) => file_data,
//         Err(msg) => panic!("Unable to read file! \n {}", msg),
//     }
// }

// pub fn load_complex_h_file_group(paths_and_names: &[&str]) -> Vec<FileData> {
//     paths_and_names
//         .iter()
//         .map(|pn| load_complex_h_file(&pn))
//         .collect()
// }

#[derive(Clone, Debug)]
pub struct B1Data {
    pub file_name: String,
    pub geo_keys: Vec<[i32; 3]>,
    pub b1_vals: Vec<f64>,
}

impl B1Data {
    pub fn from_file(path_and_name: String) -> Self {
        let file_data = match FileData::load_file(path_and_name, 1) {
            Ok(fd) => fd,
            Err(msg) => panic!("{}", msg),
        };

        let file_name = file_data.file_name;
        let geo_keys = file_data
            .geo_points
            .iter()
            .map(|geo_floats| gen_geo_key(geo_floats))
            .collect();

        let b1_vals = file_data.fld_points.iter().flatten().copied().collect();

        Self {
            file_name,
            geo_keys,
            b1_vals,
        }
    }
}

#[derive(Clone, Debug)]
pub struct HDataGroup {
    pub file_names: Vec<String>,
    pub geo_keys: Vec<[i32; 3]>,
    pub ch_h_vals: Vec<Vec<[f64; 6]>>,
    pub num_points: usize,
}

impl HDataGroup {
    pub fn from_files(paths_and_names: Vec<String>, assert_geo_equality: bool) -> Self {
        let file_datas: Vec<FileData> = paths_and_names
            .iter()
            .cloned()
            .map(|pn| match FileData::load_file(pn, 6) {
                Ok(fd) => fd,
                Err(msg) => panic!("{}", msg),
            })
            .collect();

        let num_points = file_datas[0].num_points;
        for fd in file_datas.iter().skip(1) {
            assert_eq!(
                num_points, fd.num_points,
                "H files do not have the same number of points!"
            );
        }

        let file_names = file_datas.iter().map(|fd| fd.file_name.clone()).collect();
        let geo_keys: Vec<[i32; 3]> = file_datas[0]
            .geo_points
            .iter()
            .map(|geo_floats| gen_geo_key(geo_floats))
            .collect();

        if assert_geo_equality {
            for fd in file_datas.iter().skip(1) {
                for (key, geo_pt) in geo_keys.iter().zip(fd.geo_points.iter()) {
                    assert_eq!(
                        key,
                        &gen_geo_key(geo_pt),
                        "H files do not cover the same geometry!"
                    );
                }
            }
        }

        let ch_h_vals = file_datas
            .iter()
            .map(|fd| {
                fd.fld_points
                    .iter()
                    .map(|fld_pt| {
                        [
                            fld_pt[0], fld_pt[1], fld_pt[2], fld_pt[3], fld_pt[4], fld_pt[5],
                        ]
                    })
                    .collect()
            })
            .collect();

        Self {
            file_names,
            geo_keys,
            ch_h_vals,
            num_points,
        }
    }

    pub fn single_dir_points(&self, direction: u8) -> Vec<Vec<[f64; 2]>> {
        assert!(direction < 3, "Direction must be 0, 1, or 2");

        let d_re = (direction * 2) as usize;
        let d_im = (direction * 2 + 1) as usize;

        self.ch_h_vals
            .iter()
            .map(|ch| ch.iter().map(|point| [point[d_re], point[d_im]]).collect())
            .collect()
    }

    pub fn double_dir_points(&self, directions: [u8; 2]) -> Vec<Vec<[f64; 4]>> {
        assert!(directions[0] < 3, "First Direction must be 0, 1, or 2");
        assert!(directions[1] < 3, "Second Direction must be 0, 1, or 2");
        assert_ne!(
            directions[0], directions[1],
            "First and Second Direction cannot be the same value!"
        );

        let d1_re = (directions[0] * 2) as usize;
        let d1_im = (directions[0] * 2 + 1) as usize;

        let d2_re = (directions[1] * 2) as usize;
        let d2_im = (directions[1] * 2 + 1) as usize;

        self.ch_h_vals
            .iter()
            .map(|ch| {
                ch.iter()
                    .map(|point| [point[d1_re], point[d1_im], point[d2_re], point[d2_im]])
                    .collect()
            })
            .collect()
    }

    pub fn grouped_double_dir_data_points(
        &self,
        directions: [u8; 2],
        grouping_axis: usize,
    ) -> Vec<Vec<Vec<[f64; 4]>>> {
        assert!(directions[0] < 3, "First Direction must be 0, 1, or 2");
        assert!(directions[1] < 3, "Second Direction must be 0, 1, or 2");
        assert_ne!(
            directions[0], directions[1],
            "First and Second Direction cannot be the same value!"
        );

        let d1_re = (directions[0] * 2) as usize;
        let d1_im = (directions[0] * 2 + 1) as usize;

        let d2_re = (directions[1] * 2) as usize;
        let d2_im = (directions[1] * 2 + 1) as usize;

        assert!(grouping_axis < 3, "Grouping Axis must be 0, 1, or 2!");
        let axis = grouping_axis as usize;

        // collect all relevant keys keeping track of how many of each key there are
        let mut axis_geo_keys: BTreeMap<i32, usize> = BTreeMap::new();
        for geo_key in &self.geo_keys {
            let axis_key = geo_key[axis];
            let num_points = axis_geo_keys.entry(axis_key).or_insert(0);
            *num_points += 1;
        }

        // for each channel, collect keys into their relevant groups
        let num_channels = self.ch_h_vals.len();
        let num_unique_axis_dims = axis_geo_keys.len();
        let mut ch_geo_pts: Vec<Vec<Vec<[f64; 4]>>> = Vec::with_capacity(num_channels);

        for ch in 0..num_channels {
            ch_geo_pts.push(Vec::with_capacity(num_unique_axis_dims));

            for (group_index, (axis_key, num_points)) in axis_geo_keys.iter().enumerate() {
                ch_geo_pts.push(Vec::with_capacity(*num_points));

                for (h_pt, geo_key) in self.ch_h_vals[ch].iter().zip(&self.geo_keys) {
                    if geo_key[axis] == *axis_key {
                        ch_geo_pts[ch][group_index].push([
                            h_pt[d1_re],
                            h_pt[d1_im],
                            h_pt[d2_re],
                            h_pt[d2_im],
                        ]);
                    }
                }
            }
        }

        ch_geo_pts
    }
}

fn gen_geo_key(geo_floats: &[f64; 3]) -> [i32; 3] {
    [
        (geo_floats[0] * 1000.0) as i32,
        (geo_floats[1] * 1000.0) as i32,
        (geo_floats[2] * 1000.0) as i32,
    ]
}