Home Installation Walkthrough Pipeline modules Pipeline configuration Plotting tools Community guidelines

FAOM API documentation

foam.functions_for_mesa

A few helpful functions to process MESA output.

  1"""A few helpful functions to process MESA output."""
  2
  3import glob
  4import multiprocessing
  5from functools import partial
  6from pathlib import Path
  7
  8import h5py
  9import numpy as np
 10import pandas as pd
 11
 12from foam import support_functions as sf
 13
 14
 15################################################################################
 16def read_mesa_file(file_path, index_col=None):
 17    """
 18    Read in a mesa profile or history file and return 2 dictionaries.
 19    The first with the header info, and the second with the data.
 20    If the format is hdf5, assumes the attributes are the MESA header.
 21    If the format is not an hdf5 file, assumes the default MESA output format.
 22    This format is an ascii file delimited by whitespace with the following structure:
 23    line 1: header names
 24    line 2: header data
 25    line 3: blank
 26    line 4: main data names
 27    line >4:main data values
 28
 29    Parameters
 30    ----------
 31    file_path: String
 32        The path to the MESA profile or history file to read in.
 33    index_col: Hashable
 34        Column to use as row labels.
 35
 36    Returns
 37    ----------
 38    header: dict
 39        A dictionary holding the header info of the MESA file.
 40    data: dict
 41        A dictionary holding the data columns of the MESA file as numpy arrays.
 42    """
 43    if h5py.is_hdf5(file_path):
 44        return sf.read_hdf5(file_path)
 45
 46    else:  # assumes the default MESA output format
 47        header_df = pd.read_table(file_path, sep="\s+", nrows=1, header=1)
 48        data_df = pd.read_table(file_path, sep="\s+", skiprows=3, header=1, index_col=index_col)
 49
 50        header = {}
 51        for k in header_df.keys():
 52            header.update({k: header_df[k].to_numpy()[0]})
 53        data = {}
 54        for k in data_df.keys():
 55            data.update({k: data_df[k].to_numpy()})
 56
 57        return header, data
 58
 59
 60################################################################################
 61def calculate_number_densities(hist_file):
 62    """
 63    Calculate surface number densities for all isotopes in the MESA grid.
 64    All isotopes in the used nuclear network need to be be written in the history file,
 65    otherwise this will function give wrong numbers.
 66
 67    Parameters
 68    ----------
 69    hist_file: string
 70        The path to the MESA history file.
 71
 72    Returns
 73    ----------
 74    number_densities: dict
 75        Column keys specify the element (surf_X_per_N_tot), values are number densities of that element.
 76    """
 77    _, data = read_mesa_file(hist_file)
 78    element_list = {}
 79    number_densities = {}
 80    inverse_average_atomic_mass = np.zeros(len(data[list(data.keys())[0]]))
 81    for column_name in data.keys():
 82        if "_per_Mass_tot" in column_name:
 83            element_list.update({column_name: data[column_name]})
 84            inverse_average_atomic_mass += data[column_name]
 85
 86    average_atomic_mass = inverse_average_atomic_mass ** (-1)
 87    for key in element_list.keys():
 88        number_densities.update({key.replace("_per_Mass_tot", "_per_N_tot"): element_list[key] * average_atomic_mass})
 89
 90    return number_densities
 91
 92
 93################################################################################
 94def extract_surface_grid(
 95    mesa_profiles,
 96    output_file="surfaceGrid.hdf",
 97    parameters=["Z", "M", "logD", "aov", "fov", "Xc"],
 98    nr_cpu=None,
 99    additional_observables=None,
100):
101    """
102    Extract 'logTeff', 'logL', 'logg', 'age', and extra requested info for each globbed MESA profile and write them to 1 large file.
103
104    Parameters
105    ----------
106    mesa_profiles: string
107        String to glob to find all the relevant MESA profiles.
108    output_file: string
109        Name (or path) for the file containing all the pulsation frequencies of the grid.
110    parameters: list of strings
111        List of parameters varied in the computed grid, so these are taken from the
112        name of the profile files, and included in the output file containing the info of the whole grid.
113    nr_cpu: int
114        Number of worker processes to use in multiprocessing. The default 'None' will use the number returned by os.cpu_count().
115    additional_observables: list of strings
116        List of observables to add to the surface grid. Must correspond to mesa profile header-item names.
117    """
118    # Make list of extra observables requested by the user
119    if additional_observables is None:
120        additional_observables = []
121    extras_to_be_extracted = [x for x in additional_observables if x not in ["logTeff", "logL", "logg", "age"]]
122
123    extract_func = partial(info_from_profiles, parameters=parameters, extra_header_items=extras_to_be_extracted)
124    # Glob all the files, then iteratively send them to a pool of processors
125    profiles = glob.iglob(mesa_profiles)
126    with multiprocessing.Pool(nr_cpu) as p:
127        surface = p.imap(extract_func, profiles)
128
129        # Generate the directory for the output file and write the file afterwards
130        Path(Path(output_file).parent).mkdir(parents=True, exist_ok=True)
131        # make a new list, so 'parameters' is not extended before passing it on to 'info_from_profiles'
132        header_parameters = list(parameters)
133        header_parameters.extend(["logTeff", "logL", "logg", "age"])
134        # Add the extra observables requested by the user
135        header_parameters.extend(extras_to_be_extracted)
136
137        # Make list of lists, put it in a dataframe, and write to a file
138        data = []
139        for line in surface:
140            data.append(line)
141
142    df = pd.DataFrame(data=data, columns=header_parameters)
143    df.to_hdf(path_or_buf=output_file, key="surface_grid", format="table", mode="w")
144
145
146################################################################################
147def info_from_profiles(mesa_profile, parameters, extra_header_items):
148    """
149    Extract 'logTeff', 'logL', 'logg', 'age', and extra requested info from a MESA profile and the model parameters from its filename.
150
151    Parameters
152    ----------
153    mesa_profile: string
154        path to the MESA profile
155    parameters: list of strings
156        List of input parameters varied in the computed grid, so these are read from the filename and included in returned line.
157    extra_header_items: list of strings
158        List of extra observables to add to the surface grid. Must correspond to mesa profile header-item names.
159
160    Returns
161    ----------
162    line: string
163        Line containing all the model- and surface parameters of the MESA profile.
164    """
165    param_dict = sf.get_param_from_filename(mesa_profile, parameters, values_as_float=True)
166    prof_header, prof_data = read_mesa_file(mesa_profile)
167
168    logL = np.log10(float(prof_header["photosphere_L"]))
169    logTeff = np.log10(float(prof_header["Teff"]))
170    logg = prof_data["log_g"][0]
171    age = int(float(prof_header["star_age"]))
172
173    line = []
174    for p in parameters:
175        line.append(param_dict[p])
176    line.extend([logTeff, logL, logg, age])
177
178    for obs in extra_header_items:  # Add the extra observables requested by the user
179        item = float(prof_header[obs])
180        line.append(item)
181
182    return line
def read_mesa_file(file_path, index_col=None):
17def read_mesa_file(file_path, index_col=None):
18    """
19    Read in a mesa profile or history file and return 2 dictionaries.
20    The first with the header info, and the second with the data.
21    If the format is hdf5, assumes the attributes are the MESA header.
22    If the format is not an hdf5 file, assumes the default MESA output format.
23    This format is an ascii file delimited by whitespace with the following structure:
24    line 1: header names
25    line 2: header data
26    line 3: blank
27    line 4: main data names
28    line >4:main data values
29
30    Parameters
31    ----------
32    file_path: String
33        The path to the MESA profile or history file to read in.
34    index_col: Hashable
35        Column to use as row labels.
36
37    Returns
38    ----------
39    header: dict
40        A dictionary holding the header info of the MESA file.
41    data: dict
42        A dictionary holding the data columns of the MESA file as numpy arrays.
43    """
44    if h5py.is_hdf5(file_path):
45        return sf.read_hdf5(file_path)
46
47    else:  # assumes the default MESA output format
48        header_df = pd.read_table(file_path, sep="\s+", nrows=1, header=1)
49        data_df = pd.read_table(file_path, sep="\s+", skiprows=3, header=1, index_col=index_col)
50
51        header = {}
52        for k in header_df.keys():
53            header.update({k: header_df[k].to_numpy()[0]})
54        data = {}
55        for k in data_df.keys():
56            data.update({k: data_df[k].to_numpy()})
57
58        return header, data

Read in a mesa profile or history file and return 2 dictionaries. The first with the header info, and the second with the data. If the format is hdf5, assumes the attributes are the MESA header. If the format is not an hdf5 file, assumes the default MESA output format. This format is an ascii file delimited by whitespace with the following structure: line 1: header names line 2: header data line 3: blank line 4: main data names line >4:main data values

Parameters
  • file_path (String): The path to the MESA profile or history file to read in.
  • index_col (Hashable): Column to use as row labels.
Returns
  • header (dict): A dictionary holding the header info of the MESA file.
  • data (dict): A dictionary holding the data columns of the MESA file as numpy arrays.
def calculate_number_densities(hist_file):
62def calculate_number_densities(hist_file):
63    """
64    Calculate surface number densities for all isotopes in the MESA grid.
65    All isotopes in the used nuclear network need to be be written in the history file,
66    otherwise this will function give wrong numbers.
67
68    Parameters
69    ----------
70    hist_file: string
71        The path to the MESA history file.
72
73    Returns
74    ----------
75    number_densities: dict
76        Column keys specify the element (surf_X_per_N_tot), values are number densities of that element.
77    """
78    _, data = read_mesa_file(hist_file)
79    element_list = {}
80    number_densities = {}
81    inverse_average_atomic_mass = np.zeros(len(data[list(data.keys())[0]]))
82    for column_name in data.keys():
83        if "_per_Mass_tot" in column_name:
84            element_list.update({column_name: data[column_name]})
85            inverse_average_atomic_mass += data[column_name]
86
87    average_atomic_mass = inverse_average_atomic_mass ** (-1)
88    for key in element_list.keys():
89        number_densities.update({key.replace("_per_Mass_tot", "_per_N_tot"): element_list[key] * average_atomic_mass})
90
91    return number_densities

Calculate surface number densities for all isotopes in the MESA grid. All isotopes in the used nuclear network need to be be written in the history file, otherwise this will function give wrong numbers.

Parameters
  • hist_file (string): The path to the MESA history file.
Returns
  • number_densities (dict): Column keys specify the element (surf_X_per_N_tot), values are number densities of that element.
def extract_surface_grid( mesa_profiles, output_file='surfaceGrid.hdf', parameters=['Z', 'M', 'logD', 'aov', 'fov', 'Xc'], nr_cpu=None, additional_observables=None):
 95def extract_surface_grid(
 96    mesa_profiles,
 97    output_file="surfaceGrid.hdf",
 98    parameters=["Z", "M", "logD", "aov", "fov", "Xc"],
 99    nr_cpu=None,
100    additional_observables=None,
101):
102    """
103    Extract 'logTeff', 'logL', 'logg', 'age', and extra requested info for each globbed MESA profile and write them to 1 large file.
104
105    Parameters
106    ----------
107    mesa_profiles: string
108        String to glob to find all the relevant MESA profiles.
109    output_file: string
110        Name (or path) for the file containing all the pulsation frequencies of the grid.
111    parameters: list of strings
112        List of parameters varied in the computed grid, so these are taken from the
113        name of the profile files, and included in the output file containing the info of the whole grid.
114    nr_cpu: int
115        Number of worker processes to use in multiprocessing. The default 'None' will use the number returned by os.cpu_count().
116    additional_observables: list of strings
117        List of observables to add to the surface grid. Must correspond to mesa profile header-item names.
118    """
119    # Make list of extra observables requested by the user
120    if additional_observables is None:
121        additional_observables = []
122    extras_to_be_extracted = [x for x in additional_observables if x not in ["logTeff", "logL", "logg", "age"]]
123
124    extract_func = partial(info_from_profiles, parameters=parameters, extra_header_items=extras_to_be_extracted)
125    # Glob all the files, then iteratively send them to a pool of processors
126    profiles = glob.iglob(mesa_profiles)
127    with multiprocessing.Pool(nr_cpu) as p:
128        surface = p.imap(extract_func, profiles)
129
130        # Generate the directory for the output file and write the file afterwards
131        Path(Path(output_file).parent).mkdir(parents=True, exist_ok=True)
132        # make a new list, so 'parameters' is not extended before passing it on to 'info_from_profiles'
133        header_parameters = list(parameters)
134        header_parameters.extend(["logTeff", "logL", "logg", "age"])
135        # Add the extra observables requested by the user
136        header_parameters.extend(extras_to_be_extracted)
137
138        # Make list of lists, put it in a dataframe, and write to a file
139        data = []
140        for line in surface:
141            data.append(line)
142
143    df = pd.DataFrame(data=data, columns=header_parameters)
144    df.to_hdf(path_or_buf=output_file, key="surface_grid", format="table", mode="w")

Extract 'logTeff', 'logL', 'logg', 'age', and extra requested info for each globbed MESA profile and write them to 1 large file.

Parameters
  • mesa_profiles (string): String to glob to find all the relevant MESA profiles.
  • output_file (string): Name (or path) for the file containing all the pulsation frequencies of the grid.
  • parameters (list of strings): List of parameters varied in the computed grid, so these are taken from the name of the profile files, and included in the output file containing the info of the whole grid.
  • nr_cpu (int): Number of worker processes to use in multiprocessing. The default 'None' will use the number returned by os.cpu_count().
  • additional_observables (list of strings): List of observables to add to the surface grid. Must correspond to mesa profile header-item names.
def info_from_profiles(mesa_profile, parameters, extra_header_items):
148def info_from_profiles(mesa_profile, parameters, extra_header_items):
149    """
150    Extract 'logTeff', 'logL', 'logg', 'age', and extra requested info from a MESA profile and the model parameters from its filename.
151
152    Parameters
153    ----------
154    mesa_profile: string
155        path to the MESA profile
156    parameters: list of strings
157        List of input parameters varied in the computed grid, so these are read from the filename and included in returned line.
158    extra_header_items: list of strings
159        List of extra observables to add to the surface grid. Must correspond to mesa profile header-item names.
160
161    Returns
162    ----------
163    line: string
164        Line containing all the model- and surface parameters of the MESA profile.
165    """
166    param_dict = sf.get_param_from_filename(mesa_profile, parameters, values_as_float=True)
167    prof_header, prof_data = read_mesa_file(mesa_profile)
168
169    logL = np.log10(float(prof_header["photosphere_L"]))
170    logTeff = np.log10(float(prof_header["Teff"]))
171    logg = prof_data["log_g"][0]
172    age = int(float(prof_header["star_age"]))
173
174    line = []
175    for p in parameters:
176        line.append(param_dict[p])
177    line.extend([logTeff, logL, logg, age])
178
179    for obs in extra_header_items:  # Add the extra observables requested by the user
180        item = float(prof_header[obs])
181        line.append(item)
182
183    return line

Extract 'logTeff', 'logL', 'logg', 'age', and extra requested info from a MESA profile and the model parameters from its filename.

Parameters
  • mesa_profile (string): path to the MESA profile
  • parameters (list of strings): List of input parameters varied in the computed grid, so these are read from the filename and included in returned line.
  • extra_header_items (list of strings): List of extra observables to add to the surface grid. Must correspond to mesa profile header-item names.
Returns
  • line (string): Line containing all the model- and surface parameters of the MESA profile.