Skip to content

Commit ec1e907

Browse files
committed
updated mesa_data with a workaround for new columns in MESA headers
1 parent 851ef23 commit ec1e907

9 files changed

+142
-29
lines changed

Kippenhahn.png

-12.3 KB
Loading

Kippenhahn2.png

-8.36 KB
Loading

Kippenhahn3.png

-24.6 KB
Loading

Kippenhahn4.png

-10.2 KB
Loading

Kippenhahn5.png

-13 KB
Loading

Kippenhahn6.png

-9.97 KB
Loading

example.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
fig = plt.figure()
4141
axis = plt.gca()
4242
#only need to read star_age column first
43-
history = mesa_data.Mesa_Data("LOGS/history.data", read_data_cols = ["star_age"])
43+
history = mesa_data.mesa_data("LOGS/history.data", read_data_cols = ["star_age"])
4444
max_age = max(history.get("star_age"))
4545
kipp_plot = mkipp.kipp_plot(mkipp.Kipp_Args(
4646
xaxis = "star_age",

kipp_data.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def get_mixing_zones(history_paths, kipp_args, xlims = None):
119119
mix_data = []
120120
histories = []
121121
for history_name in history_paths:
122-
history = Mesa_Data(history_name, read_data = False)
122+
history = mesa_data(history_name, read_data = False)
123123
columns = []
124124
for key in history.columns.keys():
125125
search_regex = "log_R|star_mass|model_number|star_age|.*core_mass|mix_type.*|"
@@ -267,7 +267,7 @@ def get_xyz_data(profile_paths, kipp_args, xlims = None):
267267
prof_include = [False]*len(profile_paths)
268268
for i,profile_name in enumerate(profile_paths):
269269
try:
270-
prof = Mesa_Data(profile_name, only_read_header = True)
270+
prof = mesa_data(profile_name, only_read_header = True)
271271
if not kipp_args.yaxis_normalize:
272272
if kipp_args.yaxis == "mass":
273273
max_y = max(prof.header['star_mass'],max_y)
@@ -313,7 +313,7 @@ def get_xyz_data(profile_paths, kipp_args, xlims = None):
313313
Y_data_array[j,:] = max_y * j / (kipp_args.yresolution-1)
314314
for i,profile_name in enumerate(profile_paths):
315315
try:
316-
prof = Mesa_Data(profile_name, read_data = False)
316+
prof = mesa_data(profile_name, read_data = False)
317317
prof.read_data(columns)
318318
except Exception as e:
319319
print("Couldn't read profile " + profile_name, e)

mesa_data.py

+138-25
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,89 @@
11
import numpy as np
22
import numpy.ma as ma
3+
import h5py
4+
from collections import OrderedDict
35

46
#class to extract a mesa data file.
5-
class Mesa_Data:
6-
def __init__(self, history_file, only_read_header = False, read_data = True, read_data_cols = [], clean_data = True):
7+
class mesa_data:
8+
def __init__(self, history_file, only_read_header = False, read_data = True, read_data_cols = [],
9+
clean_data = True, sample_every_n = 1, is_hdf5 = False):
710
self.filename = history_file
11+
self.is_hdf5 = is_hdf5
812
#header is a dictionary with the general info from the second and third line of file
913
self.header = {}
14+
self.header_num = {}
1015
#columns is a dictionary which gives the column number (minus 1) corresponding to the key
1116
self.columns = {}
12-
file = open(self.filename, "r")
13-
#first line is not used
14-
file.readline()
15-
#following two lines have header data
16-
header_names = file.readline().split()
17-
header_vals = file.readline().split()
18-
for i, header_name in enumerate(header_names):
19-
self.header[header_name] = float(header_vals[i])
20-
if only_read_header:
17+
columns = []
18+
if not is_hdf5:
19+
file = open(self.filename, "r")
20+
#first line is not used
21+
file.readline()
22+
#following two lines have header data
23+
header_names = file.readline().split()
24+
header_vals = file.readline().split()
25+
i = 0
26+
for i, header_name in enumerate(header_names):
27+
#need to properly account for these new columns
28+
if header_name in ["compiler", "build", "MESA_SDK_version", "date"]:
29+
continue
30+
self.header[header_name] = float(header_vals[i])
31+
self.header_num[header_name] = i
32+
i+=1
33+
if only_read_header:
34+
file.close()
35+
return
36+
#next line is empty
37+
file.readline()
38+
#following two lines have column data
39+
nums = file.readline().split()
40+
names = file.readline().split()
41+
for i, name in enumerate(names):
42+
self.columns[name] = int(nums[i])-1
43+
columns.append(name)
44+
file.close()
45+
else:
46+
file = h5py.File(self.filename, "r")
47+
header_names = file['header_names'][:]
48+
header_vals = file['header_vals'][:]
49+
for i in range(len(header_names)):
50+
key = header_names[i].decode('utf-8')
51+
self.header[key] = header_vals[i]
52+
self.header_num[key] = i
53+
columns = file['data_names'][:].tolist()
54+
for i, col in enumerate(columns):
55+
self.columns[col.decode('utf-8')] = i
56+
columns[i] = col.decode('utf-8')
2157
file.close()
22-
return
23-
#next line is empty
24-
file.readline()
25-
#following two lines have column data
26-
nums = file.readline().split()
27-
names = file.readline().split()
28-
for i, name in enumerate(names):
29-
self.columns[name] = int(nums[i])-1
30-
file.close()
3158

3259
if not read_data:
3360
return
3461

3562
if len(read_data_cols) == 0:
36-
read_data_cols = self.columns.keys()
63+
read_data_cols = columns
3764
self.read_data(read_data_cols, clean_data = clean_data)
3865

3966

40-
def read_data(self, column_names, clean_data = True):
67+
def read_data(self, column_names, clean_data = True, sample_every_n = 1):
4168
#always include model_number if its part of the data
4269
if "model_number" not in column_names and "model_number" in self.columns:
4370
column_names.append("model_number")
4471

45-
#read data
46-
data = np.loadtxt(self.filename, skiprows = 6, \
47-
usecols = tuple([self.columns[k] for k in column_names]), unpack = True)
72+
#be sure there are no repeated column names
73+
#(could use set but that breaks the order of the columns, which is needed if I want to save the file)
74+
column_names = list(OrderedDict.fromkeys(column_names))
75+
76+
self.read_columns = column_names
77+
78+
if not self.is_hdf5:
79+
#read data
80+
data = np.loadtxt(self.filename, skiprows = 6, \
81+
usecols = tuple([self.columns[k] for k in column_names]), unpack = True)
82+
else:
83+
file = h5py.File(self.filename, "r")
84+
data = file['data_vals'][:,sorted([self.columns[k] for k in column_names])]
85+
data = data.transpose()
86+
file.close()
4887

4988
self.data = {}
5089
#Be careful in case only one column is required
@@ -63,6 +102,7 @@ def read_data(self, column_names, clean_data = True):
63102
#last entry is valid, start from there and remove repeats
64103
for i in range(len(model_number)-2,-1,-1):
65104
if model_number[i] >= max_model_number:
105+
#exclude this point
66106
mask[i] = 1
67107
else:
68108
max_model_number = model_number[i]
@@ -71,14 +111,87 @@ def read_data(self, column_names, clean_data = True):
71111
for column in column_names:
72112
self.data[column] = ma.masked_array(self.data[column], mask=mask).compressed()
73113

114+
#subsample points
115+
if sample_every_n > 1 and "model_number" in self.columns and len(self.data["model_number"]) > 2:
116+
#keep first and last entry
117+
#create a mask
118+
model_number = self.data["model_number"]
119+
mask = np.zeros(len(model_number))
120+
for i in range(1,len(model_number)-1):
121+
if (i+1)%sample_every_n != 0:
122+
#exclude this point
123+
mask[i] = 1
124+
125+
if sum(mask) > 0:
126+
for column in column_names:
127+
self.data[column] = ma.masked_array(self.data[column], mask=mask).compressed()
128+
129+
#count number of points using first entry in dict
130+
self.num_points = len(self.data[list(self.read_columns)[0]])
131+
74132
def get(self,key):
75133
return self.data[key]
76134

135+
def save_as_hdf5(self, filename, header_str_dtype="S28", data_str_dtype="S40", compression_opts=4):
136+
f = h5py.File(filename, "w")
137+
dset_header_names = f.create_dataset("header_names", (len(self.header),), dtype=header_str_dtype)
138+
dset_header_vals = f.create_dataset("header_vals", (len(self.header),), dtype="d")
139+
for key in self.header:
140+
dset_header_names[self.header_num[key]] = np.string_(key)
141+
dset_header_vals[self.header_num[key]] = self.header[key]
142+
dset_column_names = f.create_dataset("data_names", (len(self.read_columns),), dtype=data_str_dtype)
143+
dset_column_vals = f.create_dataset("data_vals", (self.num_points,len(self.read_columns)), dtype="d",
144+
compression='gzip',compression_opts=compression_opts)
145+
for k, key in enumerate(self.read_columns):
146+
dset_column_names[k] = np.string_(key)
147+
dset_column_vals[:,k] = self.data[key]
148+
f.close()
149+
150+
#creates a mesa look-alike output file
151+
#prints all integers as doubles
152+
#not the most efficient code but I don't care
153+
def save_as_ascii(self, filename, header_str_format="{0:>28}", header_double_format="{0:>28.16e}",
154+
data_str_format="{0:>40}", data_double_format="{0:>40.16e}"):
155+
f = open(filename, "w")
156+
for i in range(len(list(self.header))):
157+
f.write(header_str_format.format(i+1))
158+
f.write("\n")
159+
#create an ordered list of keys
160+
header_keys = []
161+
for i in range(len(list(self.header))):
162+
for key in self.header:
163+
if self.header_num[key] == i:
164+
header_keys.append(key)
165+
break
166+
for i, key in enumerate(header_keys):
167+
f.write(header_str_format.format(key))
168+
f.write("\n")
169+
for i, key in enumerate(header_keys):
170+
f.write(header_double_format.format(self.header[key]))
171+
f.write("\n")
172+
f.write("\n")
173+
174+
for i in range(len(list(self.read_columns))):
175+
f.write(data_str_format.format(i+1))
176+
f.write("\n")
177+
178+
for i, key in enumerate(self.read_columns):
179+
f.write(data_str_format.format(key))
180+
for k in range(self.num_points):
181+
f.write("\n")
182+
for i, key in enumerate(self.read_columns):
183+
f.write(data_double_format.format(self.data[key][k]))
184+
185+
f.close()
186+
187+
188+
77189
#reads the profiles.index files in the folders specified by the logs_dirs array and returns
78190
#an array containing paths to the individual profile files, after cleaning up redos and backups
79191
def get_profile_paths(logs_dirs = ["LOGS"]):
80192
profile_paths = []
81193
for log_dir in logs_dirs:
194+
print(log_dir, logs_dirs)
82195
model_number, paths = np.loadtxt(log_dir+"/profiles.index", skiprows = 1, usecols = (0,2), unpack = True)
83196
mask = np.zeros(len(paths))
84197
max_model_number = model_number[-1]

0 commit comments

Comments
 (0)