Skip to content

Commit 18c6740

Browse files
committed
FEAT: Create function to read armadillo cubes into Python. Contains instructions :)
is debugged too, btw. LMK if you find issues though.
1 parent 03e521e commit 18c6740

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

srcPython/read_armadillo.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Routines to read Armadillo objects
4+
==================================
5+
6+
7+
This is just a couple of functions which will read Armadillo exports into Python.
8+
Directly exporting Armadillo cubes can be very useful during development and/or
9+
debugging, but is not suitable for production runs.
10+
11+
To export an Armadillo cube from Aether:
12+
(substitute the cube name & file name. This does support multiple processors)
13+
14+
grid.geoLon_scgc.save("geoLon_" + tostr(iProc, 3) + ".txt", arma_ascii));
15+
16+
Notes:
17+
- tostr() is defined in src/tools.cpp of Aether & zero-pads an int to return a str.
18+
- This will rewrite the existing file each time it is called.
19+
- Output is to the same directory the executable is called from.
20+
- This uses the arma_ascii format, which is way less efficient than HDF5 or binary.
21+
I have found this format is the easiest to work with, but your mileage may vary.
22+
- The several python armadillo implementations look abandoned and/or did not work for me.
23+
- See the armadillo documentation for more information on saving cubes or other data types:
24+
https://arma.sourceforge.net/docs.html#save_load_mat
25+
26+
"""
27+
28+
import numpy as np
29+
from glob import glob
30+
import os, errno
31+
32+
def check_file_inputs(files):
33+
""" Make sorted list of files (that exist) from a str or list
34+
35+
Inputs
36+
------
37+
files (str or list) Can be list of files, single file, directory, or a pattern to glob
38+
39+
Returns
40+
-------
41+
list: sorted list of files that so indeed exist
42+
43+
"""
44+
print(type(files))
45+
if isinstance(files, str): # Probably need to glob
46+
print(type(files))
47+
if "*" in files: # Definitely need to glob
48+
files2read = np.sort(glob(files))
49+
elif os.path.isfile(files):
50+
files2read = [files] # Single file needs to be made into list
51+
elif os.path.isdir(files):
52+
# We were given a directory. Read all .txt files without log in name
53+
files_ = np.sort(glob(os.path.join(files, "*.txt")))
54+
files2read = [f for f in files_ if "log" not in f]
55+
else: # pretty error message from stack overflow
56+
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), *files)
57+
58+
if len(files2read) == 0:
59+
raise ValueError(
60+
f"Could not find any armadillo cubes from '{files}'."
61+
" Check path or provide files.\n")
62+
63+
return files2read
64+
65+
# Sort list & check if all files exist. error if not.
66+
try: # attempt to handle anything listlike (np arrays, dict keys, etc.)
67+
files2read = [f for f in np.sort(files) if os.path.isfile(f)]
68+
if len(files2read) != len(files):
69+
bad_files = [f for f in files if f not in files2read]
70+
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), *bad_files)
71+
72+
except: # Not expected types
73+
raise TypeError("Need list or str. Could not handle type: " + type(files))
74+
75+
return files2read
76+
77+
78+
def cube2np(files2read):
79+
""" Read armadillo cubes from .txt files, automatically globs and/or input.
80+
return np array of shape (nFiles, n_x, n_y, n_x)
81+
82+
Inputs
83+
------
84+
files (str or list-like): either path to files or list of files. If it's a str,
85+
the pattern is globbed & sorted, or the directory's .txt files are sorted.
86+
If it's list-like, the list is sorted.
87+
88+
Outputs
89+
-------
90+
np.array of shape (nFiles, n_x, n_y, n_z) & dtype float. If we are only reading
91+
one file, return shape is just (n_x, n_y, n_z)
92+
93+
Usage
94+
-----
95+
96+
lons = cube2np("../run/geolon_*.txt")
97+
lons = cube2np(np.sort(glob.glob("../run/geolon_*.txt")))
98+
99+
"""
100+
101+
# Sanitize input
102+
files2read = check_file_inputs(files2read)
103+
104+
out = [] # output holder
105+
for thisf in files2read:
106+
with open(thisf, 'r') as f:
107+
_ = f.readline() # first line is a header, not needed
108+
shape = f.readline().strip() # next line holds the shape of the cube
109+
shape = shape.split(' ')
110+
if len(shape) != 3:
111+
raise ValueError(
112+
f"File ({thisf}) does not appear to be an armadillo cube.\n"
113+
f"Found shape: {shape}")
114+
shape = np.array(shape, dtype=int) # convert shape to np array of int's
115+
ls = np.zeros(shape) # holder for this file's outputs, dtype is float
116+
for i in range(int(shape[0])): # n_x
117+
for j in range(int(shape[2])): # n_z
118+
# each line is n_y long. Convert it to a python list & retain it
119+
l = f.readline().strip().replace(' ',',').split(',')
120+
ls[i,:, j] = l # n_y
121+
out.append(ls) # speed not a huge issue, work with lists
122+
123+
# remove 0th dimension if we only are reading one file
124+
if len(files2read) == 1:
125+
out = out[0]
126+
127+
return np.array(out)

0 commit comments

Comments
 (0)