1use std::fs::File;
2use std::io::{self, BufRead, BufReader, Read};
3
4use crate::block::Block;
5use crate::utils::{self, read_fortran_record, Endian};
6
7#[derive(Copy, Clone, Debug)]
8pub enum BinaryFormat {
9 Fortran,
10 Raw,
11}
12
13#[derive(Copy, Clone, Debug)]
14pub enum FloatPrecision {
15 F32,
16 F64,
17}
18
19pub use crate::utils::Endian as EndianOrder;
21
22pub fn read_plot3d_ascii(path: &str) -> io::Result<Vec<Block>> {
23 let f = File::open(path)?;
24 let mut rdr = BufReader::new(f);
25
26 let mut first = String::new();
28 loop {
29 first.clear();
30 let n = rdr.read_line(&mut first)?;
31 if n == 0 {
32 return Err(ioerr("empty file"));
33 }
34 if !first.trim().is_empty() {
35 break;
36 }
37 }
38 let nblocks: usize = first
39 .split_whitespace()
40 .next()
41 .ok_or_else(|| ioerr("bad nblocks"))?
42 .parse()
43 .map_err(|_| ioerr("bad nblocks"))?;
44
45 let mut dims = Vec::with_capacity(nblocks);
47 for _ in 0..nblocks {
48 let mut line = String::new();
49 loop {
50 line.clear();
51 rdr.read_line(&mut line)?;
52 if !line.trim().is_empty() {
53 break;
54 }
55 }
56 let mut it = line.split_whitespace();
57 let imax: usize = it
58 .next()
59 .ok_or_else(|| ioerr("bad dims"))?
60 .parse()
61 .map_err(|_| ioerr("bad dims"))?;
62 let jmax: usize = it
63 .next()
64 .ok_or_else(|| ioerr("bad dims"))?
65 .parse()
66 .map_err(|_| ioerr("bad dims"))?;
67 let kmax: usize = it
68 .next()
69 .ok_or_else(|| ioerr("bad dims"))?
70 .parse()
71 .map_err(|_| ioerr("bad dims"))?;
72 dims.push((imax, jmax, kmax));
73 }
74
75 fn read_n(rdr: &mut BufReader<File>, n: usize) -> io::Result<Vec<f64>> {
77 let mut out = Vec::with_capacity(n);
78 while out.len() < n {
79 let mut line = String::new();
80 let cnt = rdr.read_line(&mut line)?;
81 if cnt == 0 {
82 break;
83 }
84 for t in line.split_whitespace() {
85 if out.len() == n {
86 break;
87 }
88 out.push(t.parse::<f64>().map_err(|_| ioerr("bad float"))?);
89 }
90 }
91 if out.len() != n {
92 return Err(ioerr("unexpected EOF in payload"));
93 }
94 Ok(out)
95 }
96
97 let mut blocks = Vec::with_capacity(nblocks);
99 for (imax, jmax, kmax) in dims {
100 let n = imax * jmax * kmax;
101 let x = read_n(&mut rdr, n)?;
102 let y = read_n(&mut rdr, n)?;
103 let z = read_n(&mut rdr, n)?;
104 blocks.push(Block::new(imax, jmax, kmax, x, y, z));
105 }
106 Ok(blocks)
107}
108
109pub fn read_plot3d_binary(
110 path: &str,
111 format: BinaryFormat,
112 precision: FloatPrecision,
113 endian: Endian,
114) -> io::Result<Vec<Block>> {
115 let mut f = File::open(path)?;
116 match format {
117 BinaryFormat::Raw => read_binary_raw(&mut f, precision, endian),
118 BinaryFormat::Fortran => read_binary_fortran(&mut f, precision, endian),
119 }
120}
121
122fn read_binary_raw(
123 r: &mut impl Read,
124 precision: FloatPrecision,
125 endian: Endian,
126) -> io::Result<Vec<Block>> {
127 use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
128
129 let nblocks = match endian {
131 Endian::Little => r.read_u32::<LittleEndian>()?,
132 Endian::Big => r.read_u32::<BigEndian>()?,
133 } as usize;
134
135 let mut dims = Vec::with_capacity(nblocks);
136 for _ in 0..nblocks {
137 let imax = match endian {
138 Endian::Little => r.read_u32::<LittleEndian>()?,
139 Endian::Big => r.read_u32::<BigEndian>()?,
140 } as usize;
141 let jmax = match endian {
142 Endian::Little => r.read_u32::<LittleEndian>()?,
143 Endian::Big => r.read_u32::<BigEndian>()?,
144 } as usize;
145 let kmax = match endian {
146 Endian::Little => r.read_u32::<LittleEndian>()?,
147 Endian::Big => r.read_u32::<BigEndian>()?,
148 } as usize;
149 dims.push((imax, jmax, kmax));
150 }
151
152 let mut blocks = Vec::with_capacity(nblocks);
154 for (imax, jmax, kmax) in dims {
155 let n = imax * jmax * kmax;
156 let x = read_vec_num(r, n, precision, endian)?;
157 let y = read_vec_num(r, n, precision, endian)?;
158 let z = read_vec_num(r, n, precision, endian)?;
159 blocks.push(Block::new(imax, jmax, kmax, x, y, z));
160 }
161 Ok(blocks)
162}
163
164fn read_binary_fortran(
165 r: &mut impl Read,
166 precision: FloatPrecision,
167 endian: Endian,
168) -> io::Result<Vec<Block>> {
169 let nb_rec = read_fortran_record(r, endian)?;
171 if nb_rec.len() < 4 {
172 return Err(ioerr("short nblocks record"));
173 }
174 let nblocks = utils::Endian::read_u32(&nb_rec[..4], endian) as usize;
175
176 let mut dims = Vec::with_capacity(nblocks);
178 for _ in 0..nblocks {
179 let rec = read_fortran_record(r, endian)?;
180 if rec.len() < 12 {
181 return Err(ioerr("short dims record"));
182 }
183 let imax = utils::Endian::read_u32(&rec[0..4], endian) as usize;
184 let jmax = utils::Endian::read_u32(&rec[4..8], endian) as usize;
185 let kmax = utils::Endian::read_u32(&rec[8..12], endian) as usize;
186 dims.push((imax, jmax, kmax));
187 }
188
189 let mut blocks = Vec::with_capacity(nblocks);
191 for (imax, jmax, kmax) in dims {
192 let n = imax * jmax * kmax;
193
194 let xr = read_fortran_record(r, endian)?;
195 let x = match precision {
196 FloatPrecision::F32 => utils::Endian::read_f32_slice(&xr, endian)
197 .into_iter()
198 .map(|v| v as f64)
199 .collect(),
200 FloatPrecision::F64 => utils::Endian::read_f64_slice(&xr, endian),
201 };
202 if x.len() != n {
203 return Err(ioerr("X size mismatch"));
204 }
205
206 let yr = read_fortran_record(r, endian)?;
207 let y = match precision {
208 FloatPrecision::F32 => utils::Endian::read_f32_slice(&yr, endian)
209 .into_iter()
210 .map(|v| v as f64)
211 .collect(),
212 FloatPrecision::F64 => utils::Endian::read_f64_slice(&yr, endian),
213 };
214 if y.len() != n {
215 return Err(ioerr("Y size mismatch"));
216 }
217
218 let zr = read_fortran_record(r, endian)?;
219 let z = match precision {
220 FloatPrecision::F32 => utils::Endian::read_f32_slice(&zr, endian)
221 .into_iter()
222 .map(|v| v as f64)
223 .collect(),
224 FloatPrecision::F64 => utils::Endian::read_f64_slice(&zr, endian),
225 };
226 if z.len() != n {
227 return Err(ioerr("Z size mismatch"));
228 }
229
230 blocks.push(Block::new(imax, jmax, kmax, x, y, z));
231 }
232
233 Ok(blocks)
234}
235
236fn read_vec_num(
237 r: &mut impl Read,
238 n: usize,
239 precision: FloatPrecision,
240 endian: Endian,
241) -> io::Result<Vec<f64>> {
242 use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
243
244 let mut out = Vec::with_capacity(n);
245 match (precision, endian) {
246 (FloatPrecision::F32, Endian::Little) => {
247 for _ in 0..n {
248 out.push(r.read_f32::<LittleEndian>()? as f64);
249 }
250 }
251 (FloatPrecision::F32, Endian::Big) => {
252 for _ in 0..n {
253 out.push(r.read_f32::<BigEndian>()? as f64);
254 }
255 }
256 (FloatPrecision::F64, Endian::Little) => {
257 for _ in 0..n {
258 out.push(r.read_f64::<LittleEndian>()?);
259 }
260 }
261 (FloatPrecision::F64, Endian::Big) => {
262 for _ in 0..n {
263 out.push(r.read_f64::<BigEndian>()?);
264 }
265 }
266 }
267 Ok(out)
268}
269
270fn ioerr(msg: &str) -> io::Error {
271 io::Error::new(io::ErrorKind::InvalidData, msg)
272}
273
274pub fn read_ap_nasa(path: &str, endian: Endian) -> io::Result<(Block, i32)> {
284 let mut f = File::open(path)?;
285
286 let int_rec = read_fortran_record(&mut f, endian)?;
288 if int_rec.len() < 28 {
289 return Err(ioerr("AP NASA header too short (expected 7 i32)"));
290 }
291 let il = utils::Endian::read_u32(&int_rec[0..4], endian) as usize;
292 let jl = utils::Endian::read_u32(&int_rec[4..8], endian) as usize;
293 let kl = utils::Endian::read_u32(&int_rec[8..12], endian) as usize;
294 let nbld = utils::Endian::read_u32(&int_rec[24..28], endian) as i32;
296
297 let stride = il * kl;
299 let total = il * jl * kl;
300 let mut meshx = Vec::with_capacity(total);
301 let mut meshr = Vec::with_capacity(total);
302 let mut mesht = Vec::with_capacity(total);
303
304 for _j in 0..jl {
305 let rec = read_fortran_record(&mut f, endian)?;
306 let floats = utils::Endian::read_f32_slice(&rec, endian);
307 if floats.len() < 3 * stride {
308 return Err(ioerr("AP NASA data record too short"));
309 }
310 for idx in 0..stride {
312 meshx.push(floats[idx] as f64);
313 meshr.push(floats[stride + idx] as f64);
314 mesht.push(floats[2 * stride + idx] as f64);
315 }
316 }
317
318 let mut x = Vec::with_capacity(total);
323 let mut y = Vec::with_capacity(total);
324 let mut z = Vec::with_capacity(total);
325
326 for k in 0..kl {
327 for j in 0..jl {
328 for i in 0..il {
329 let src = j * (kl * il) + k * il + i;
330 let r = meshr[src];
331 let theta = mesht[src];
332 x.push(meshx[src]);
333 y.push(r * theta.cos());
334 z.push(r * theta.sin());
335 }
336 }
337 }
338
339 Ok((Block::new(il, jl, kl, x, y, z), nbld))
340}