use std::path::{Path, PathBuf};
use std::sync::Arc;
use super::dict::FoamFile;
use super::field::{
read_vol_scalar_field, read_vol_vector_field, write_vol_scalar_field, write_vol_vector_field,
Dimensions,
};
use super::poly_mesh::PolyMesh;
use super::IoError;
use crate::fields::{VolScalarField, VolVectorField};
use crate::mesh::FvMesh;
#[derive(Debug, Clone)]
pub enum CaseField {
Scalar(VolScalarField, Dimensions),
Vector(VolVectorField, Dimensions),
}
impl CaseField {
pub fn name(&self) -> &str {
match self {
CaseField::Scalar(f, _) => &f.name,
CaseField::Vector(f, _) => &f.name,
}
}
}
#[derive(Debug, Clone)]
pub struct FoamCase {
pub root: PathBuf,
pub system: Vec<(String, FoamFile)>,
pub poly_mesh: Option<PolyMesh>,
pub mesh: Option<Arc<FvMesh>>,
pub time_dir: String,
pub fields: Vec<CaseField>,
pub skipped_fields: Vec<(String, String)>,
}
impl FoamCase {
pub fn read(root: impl AsRef<Path>) -> Result<Self, IoError> {
Self::read_time(root, "0")
}
pub fn read_time(root: impl AsRef<Path>, time_dir: &str) -> Result<Self, IoError> {
let root = root.as_ref().to_path_buf();
let mut system = Vec::new();
let sys_dir = root.join("system");
if sys_dir.is_dir() {
let mut names = list_files(&sys_dir)?;
names.sort();
for name in names {
let ff = FoamFile::read(sys_dir.join(&name))?;
system.push((name, ff));
}
}
let poly_dir = root.join("constant").join("polyMesh");
let (poly_mesh, mesh) = if poly_dir.join("points").is_file() {
let pm = PolyMesh::read(&poly_dir)?;
let fv = Arc::new(pm.to_fv_mesh()?);
(Some(pm), Some(fv))
} else {
(None, None)
};
let mut fields = Vec::new();
let mut skipped_fields = Vec::new();
let t_dir = root.join(time_dir);
if let Some(mesh) = &mesh {
if t_dir.is_dir() {
let mut names = list_files(&t_dir)?;
names.sort();
for name in names {
let path = t_dir.join(&name);
match peek_class(&path)?.as_deref() {
Some("volScalarField") => {
let (f, d) = read_vol_scalar_field(&path, Arc::clone(mesh))?;
fields.push(CaseField::Scalar(f, d));
}
Some("volVectorField") => {
let (f, d) = read_vol_vector_field(&path, Arc::clone(mesh))?;
fields.push(CaseField::Vector(f, d));
}
Some(other) => skipped_fields.push((name, other.to_string())),
None => skipped_fields.push((name, "<no class>".to_string())),
}
}
}
}
Ok(Self {
root,
system,
poly_mesh,
mesh,
time_dir: time_dir.to_string(),
fields,
skipped_fields,
})
}
pub fn system_dict(&self, name: &str) -> Option<&FoamFile> {
self.system.iter().find(|(n, _)| n == name).map(|(_, f)| f)
}
pub fn write(&self, root: impl AsRef<Path>) -> Result<(), IoError> {
let root = root.as_ref();
let sys_dir = root.join("system");
create_dir(&sys_dir)?;
for (name, ff) in &self.system {
ff.write(sys_dir.join(name))?;
}
if let Some(pm) = &self.poly_mesh {
pm.write(root.join("constant").join("polyMesh"))?;
}
let t_dir = root.join(&self.time_dir);
if !self.fields.is_empty() {
create_dir(&t_dir)?;
}
for field in &self.fields {
match field {
CaseField::Scalar(f, d) => write_vol_scalar_field(t_dir.join(&f.name), f, *d)?,
CaseField::Vector(f, d) => write_vol_vector_field(t_dir.join(&f.name), f, *d)?,
}
}
Ok(())
}
}
fn create_dir(dir: &Path) -> Result<(), IoError> {
std::fs::create_dir_all(dir).map_err(|e| IoError::io(dir.display().to_string(), e))
}
fn list_files(dir: &Path) -> Result<Vec<String>, IoError> {
let mut out = Vec::new();
let rd = std::fs::read_dir(dir).map_err(|e| IoError::io(dir.display().to_string(), e))?;
for entry in rd {
let entry = entry.map_err(|e| IoError::io(dir.display().to_string(), e))?;
let path = entry.path();
if path.is_file() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
out.push(name.to_string());
}
}
}
Ok(out)
}
fn peek_class(path: &Path) -> Result<Option<String>, IoError> {
let text =
std::fs::read_to_string(path).map_err(|e| IoError::io(path.display().to_string(), e))?;
let tokens = super::dict::tokenize(&text);
let mut i = 0;
while i < tokens.len() {
if tokens[i].text == "class" {
if let Some(v) = tokens.get(i + 1) {
return Ok(Some(v.text.clone()));
}
}
i += 1;
}
Ok(None)
}