Skip to content
Snippets Groups Projects
setup.rs 7.47 KiB
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;

use std::ops::{Index, IndexMut};

use nom::Parser;

use crate::rust_cfd;

pub const GAMMA: f32 = 1.4;
pub const ff_mach: f32 = 1.2;
pub const NDIM: usize = 3;
pub const NNB: usize = 4;
pub const RK: usize = 3;
pub const VAR_DENSITY: usize = 0;
pub const VAR_MOMENTUM: usize = 1;
pub const VAR_DENSITY_ENERGY: usize = VAR_MOMENTUM + NDIM;
pub const NVAR: usize = VAR_DENSITY_ENERGY + 1;
pub const deg_angle_of_attack: f32 = 0.0;

#[repr(align(64))]
struct Alignment([u8; 64]);

// An aligned slice is stored as a boxed slice and an offset number of elements
// that we skip over to get the desired alignment (of 64 bytes)
pub struct AlignedSlice<T> {
    slice: Box<[T]>,
    offset: usize,
    length: usize,
}

impl<T> AlignedSlice<T>
where
    T: Default,
{
    pub fn of_len(len: usize) -> Self {
        // The maximum number of elements that may be waisted in getting the alignment we need is
        // (64 - alignment of T) / size of T
        let extra_elements = (64 - std::mem::align_of::<T>()) / std::mem::size_of::<T>();
        let slice: Box<[T]> = (0..len + extra_elements)
            .map(|_| Default::default())
            .collect();
        let offset = unsafe { slice.align_to::<Alignment>().0.len() };
        assert!(offset <= extra_elements);

        AlignedSlice {
            slice,
            offset,
            length: len,
        }
    }
}

impl<T> AlignedSlice<T> {
    pub fn as_slice(&self) -> &[T] {
        &self.slice[self.offset..self.offset + self.length]
    }

    pub fn as_mut_slice(&mut self) -> &mut [T] {
        &mut self.slice[self.offset..self.offset + self.length]
    }
}

impl<T> Index<usize> for AlignedSlice<T> {
    type Output = T;

    fn index(&self, index: usize) -> &Self::Output {
        &self.slice[index + self.offset]
    }
}

impl<T> IndexMut<usize> for AlignedSlice<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.slice[index + self.offset]
    }
}

// TODO: I don't think Default should be needed here, but it would require some
// other constructor probably one that uses Clone an an element rather than Default
impl<T> Clone for AlignedSlice<T>
where
    T: Clone + Default,
{
    fn clone(&self) -> Self {
        let mut new_slice = Self::of_len(self.length);
        new_slice.as_mut_slice().clone_from_slice(self.as_slice());
        new_slice
    }
}

#[repr(C)]
pub struct Float3 {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

pub struct FarFieldConditions {
    pub ff_variable: AlignedSlice<f32>,
    pub ff_fc_momentum_x: Float3,
    pub ff_fc_momentum_y: Float3,
    pub ff_fc_momentum_z: Float3,
    pub ff_fc_density_energy: Float3,
}

pub fn set_far_field_conditions() -> FarFieldConditions {
    let mut ff_variable: AlignedSlice<f32> = AlignedSlice::of_len(NVAR);

    let angle_of_attack = std::f32::consts::PI / 180.0 * deg_angle_of_attack;

    ff_variable[VAR_DENSITY] = 1.4;

    let ff_pressure = 1.0;
    let ff_speed_of_sound = (GAMMA * ff_pressure / ff_variable[VAR_DENSITY]).sqrt();
    let ff_speed = ff_mach * ff_speed_of_sound;

    let ff_velocity = Float3 {
        x: ff_speed * angle_of_attack.cos(),
        y: ff_speed * angle_of_attack.sin(),
        z: 0.0,
    };

    ff_variable[VAR_MOMENTUM + 0] = ff_variable[VAR_DENSITY] * ff_velocity.x;
    ff_variable[VAR_MOMENTUM + 1] = ff_variable[VAR_DENSITY] * ff_velocity.y;
    ff_variable[VAR_MOMENTUM + 2] = ff_variable[VAR_DENSITY] * ff_velocity.z;

    ff_variable[VAR_DENSITY_ENERGY] =
        ff_variable[VAR_DENSITY] * (0.5 * (ff_speed * ff_speed)) + (ff_pressure / (GAMMA - 1.0));

    let ff_momentum = Float3 {
        x: ff_variable[VAR_MOMENTUM + 0],
        y: ff_variable[VAR_MOMENTUM + 1],
        z: ff_variable[VAR_MOMENTUM + 2],
    };
    let (ff_fc_momentum_x, ff_fc_momentum_y, ff_fc_momentum_z, ff_fc_density_energy) =
        rust_cfd::compute_flux_contribution(
            ff_variable[VAR_DENSITY],
            &ff_momentum,
            ff_variable[VAR_DENSITY_ENERGY],
            ff_pressure,
            &ff_velocity,
        );

    FarFieldConditions {
        ff_variable,
        ff_fc_momentum_x,
        ff_fc_momentum_y,
        ff_fc_momentum_z,
        ff_fc_density_energy,
    }
}

pub struct GeometryData {
    pub nelr: usize,
    pub areas: AlignedSlice<f32>,
    pub elements_surrounding_elements: AlignedSlice<i32>,
    pub normals: AlignedSlice<f32>,
}

pub fn read_domain_geometry<P>(path: P, block_size: usize) -> GeometryData
where
    P: AsRef<Path>,
{
    let mut file = File::open(path).expect("Error opening input file");
    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .expect("Error reading input file");

    let mut parser = nom::combinator::all_consuming(|s| cfd_parser(block_size, s));
    let (_, result) = parser.parse(&contents).expect("Parser error");

    result
}

fn cfd_parser<'a>(block_size: usize, text: &'a str) -> nom::IResult<&'a str, GeometryData> {
    let text = nom::character::complete::multispace0(text)?.0;
    let (text, nel) = nom::character::complete::digit1(text)?;

    let nel = usize::from_str(nel).unwrap();
    let nelr = block_size * ((nel / block_size) + usize::min(1, nel % block_size));

    let mut areas: AlignedSlice<f32> = AlignedSlice::of_len(nelr);
    let mut elements_surrounding_elements: AlignedSlice<i32> = AlignedSlice::of_len(nelr * NNB);
    let mut normals: AlignedSlice<f32> = AlignedSlice::of_len(NDIM * NNB * nelr);

    // read in data
    let mut text = text;
    for i in 0..nel {
        let t = nom::character::complete::multispace0(text)?.0;
        let (t, val) = nom::number::complete::float(t)?;
        text = t;

        areas[i] = val;

        for j in 0..NNB {
            let t = nom::character::complete::multispace0(text)?.0;
            let (t, neg) = if let Ok((t, _)) =
                nom::character::complete::char::<&str, nom::error::Error<&str>>('-')(t)
            {
                (t, true)
            } else {
                (t, false)
            };
            let (t, val) = nom::character::complete::digit1(t)?;
            text = t;

            let val = i32::from_str(val).unwrap();
            let val = if neg { -val } else { val };
            elements_surrounding_elements[i + j * nelr] = if val < 0 { -1 } else { val } - 1; // it's coming in with Fortran numbering

            for k in 0..NDIM {
                let t = nom::character::complete::multispace0(text)?.0;
                let (t, val) = nom::number::complete::float(t)?;
                text = t;

                normals[i + (j + k * NNB) * nelr] = -val;
            }
        }
    }

    // fill in remaining data
    let last = nel - 1;
    for i in nel..nelr {
        areas[i] = areas[last];
        for j in 0..NNB {
            elements_surrounding_elements[i + j * nelr] =
                elements_surrounding_elements[last + j * nelr];
            for k in 0..NDIM {
                normals[i + (j + k * NNB) * nelr] = normals[last + (j + k * NNB) * nelr];
            }
        }
    }

    let text = nom::character::complete::multispace0(text)?.0;
    Ok((
        text,
        GeometryData {
            nelr,
            areas,
            elements_surrounding_elements,
            normals,
        },
    ))
}

pub fn initialize_variables(nelr: usize, ff_variable: &[f32]) -> AlignedSlice<f32> {
    let mut variables = AlignedSlice::of_len(nelr * NVAR);

    for i in 0..nelr {
        for j in 0..NVAR {
            variables[i + j * nelr] = ff_variable[j];
        }
    }

    variables
}