.. _psygrid: ################ `PSyGrid` object ################ The :samp:`PSyGrid` object contains the data coming from detailed MESA simulations. Import the :samp:`PSyGrid` module with: .. code-block:: python from posydon.grids.psygrid import PSyGrid Initializing a `PSyGrid` object ------------------------------- You can instantiate a new :samp:`PSyGrid` object with .. code-block:: python mygrid = PSyGrid() This calls the initializer, which can take two optional arguments - :samp:`filepath`: this is the path to the associated :samp:`h5` file; if the file exists, the initializer will load the contained `PSyGrid` object - :samp:`verbose`: this is a boolean indicating whether detailed output should be, given when calling functions of the :samp:`PSyGrid` object Creating a `PSyGrid` object --------------------------- The :samp:`create` function of a :samp:`PSyGrid` object will read MESA output data into the :samp:`PSyGrid` object. It has a required argument, :samp:`MESA_grid_path`, which is the path to the directory containing the MESA runs. There are several optional arguments as well: .. table:: Optional arguments of the :samp:`PSyGrid` creation :widths: 18,10,72 =============== ========= =========== Argument Default Description =============== ========= =========== psygrid_path None the path to the associated :samp:`h5` file (required if none was specified during initialization) overwrite False if :samp:`True`, overwrite any file that exists slim False if :samp:`True`, only store initial/final values and metadata warn "end" if :samp:`normal`, warnings are printed, when they arise if :samp:`end`, all warnings are printed at the end if :samp:`suppress`, no warnings are printed fmt "posydon" grid format; only :samp:`posydon` is currently supported \*\*grid_kwargs further grid configuration properties can be specified in a dictionary =============== ========= =========== The :samp:`PSyGrid` object has the following grid configuration properties: .. _tab_grid_properties: .. table:: Grid configuration properties ============================== ============ =========== Property Default Description ============================== ============ =========== 'description' "" description text 'max_number_of_runs' None the maximum number of runs 'format' "hdf5" file format; only :samp:`hdf5` is currently supported 'compression' "gzip9" the compression (of the hdf5 file) 'history_DS_error' None the maximum error allowed when downsampling the history 'history_DS_exclude' default list the history columns to exclude from downsampling (default list = ["model_number", "age", "star_age"]) 'profile_DS_error' None the maximum error allowed when downsampling the final profile 'profile_DS_interval' None the maximum change in an downsampled interval relative to the change from initial to final 'profile_DS_exclude' default list the profile columns to exclude from downsampling (default list = ["mass", "star_mass"]) 'star1_history_saved_columns' "minimum" specifies which history columns of star 1 should be read if :samp:`all`, read all the columns in the MESA output if :samp:`minimum`, use the default if a tuple of column names, read only those columns if a list of column names, read the default and those columns 'star2_history_saved_columns' "minimum" specifies which history columns of star 2 should be read (same options as star1_history_saved_columns) 'binary_history_saved_columns' "minimum" specifies which binary history columns should be read (same options as star1_history_saved_columns) 'star1_profile_saved_columns' "minimum" specifies which profile columns of star 1 should be read (same options as star1_history_saved_columns) 'star2_profile_saved_columns' "minimum" specifies which profile columns of star 2 should be read (same options as star1_history_saved_columns) 'initial_value_columns' None history columns from which to store initial values (currently not in use, instead all specified history columns are used as well as the abundances X, Y, and Z) 'final_value_columns' None history columns from which to store final values (currently not in use, instead all specified history columns are used as well as termination flags and for binaries the interpolation class) 'start_at_RLO' False specifies whether to crop the history to start at RLO 'stop_before_carbon_depletion' False specifies whether to crop the history of massive stars (>100 Msun) to stop at 10% central carbon and after helium is depleted 'binary' True specifies whether a grid evolved binaries; put :samp:`False` for single stars 'eep' None path to directory with EEP files (for single stars only) 'initial_RLO_fix' False specifies whether the boundary of initial RLO should be determined to flag all systems below as initial RLO independent of the MESA output 'He_core_fix' True specifies to ensure that the helium core is always larger or equal to the carbon-oxygen core 'accept_missing_profile' False specifies whether try to include all data from MESA runs without final profiles ============================== ============ =========== You can read the MESA data into an existing :samp:`PSyGrid` object, which may overwrite data: .. code-block:: python mygrid.create(MESA_grid_path=".") Alternatively, you can combine the initialization with creation of the grid based on MESA data: .. code-block:: python mygrid = PSyGrid().create(MESA_grid_path=".") Loading a `PSyGrid` object -------------------------- You can load an existing :samp:`h5` file (e.g. "myPSyGrid.h5") into a :samp:`PSyGrid` object: .. code-block:: python mygrid.load(filepath="myPSyGrid.h5") It may be more convenient to load the file directly when initializing the :samp:`PSyGrid` object .. code-block:: python mygrid = PSyGrid(filepath="myPSyGrid.h5") Contents of a `PSyGrid` object ------------------------------ Print a `PSyGrid` object ~~~~~~~~~~~~~~~~~~~~~~~~ You can check the contents of the :samp:`PSyGrid` object with a print command: .. code-block:: python print(mygrid) This will provide a summary, which tell you: - to which hdf5 file it is connected - how many runs are in the grid and how many have - a binary history - a history of star 1 - a history of star 2 - a final profile of star 1 - a final profile of star 2 - which histories/profile fields are included in the last run - which initial and final values are stored in the grid - information about the grid configuration - a shorthand list of the MESA directories (the locations of the data the runs where extracted from) To access single runs, it is important to know how many are there to avoid calling a nonexisting run. You can find the number of runs with: .. code-block:: python len(mygrid) .. note:: Alternatively, you can request the length internally with :samp:`mygrid.n_runs`. Accessing data in a `PSyGrid` object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The first data you may want to check are the :ref:`grid configuration properties <tab_grid_properties>`. You can get a list of the properties available for your :samp:`PSyGrid` object with .. code-block:: python mygrid.config.keys() You can access the value of any grid configuration property "PROPERTY" with :samp:`mygrid.config[{PROPERTY}]`. Next, you can look at the initial and final values of the runs. All the values are available at :samp:`mygrid.initial_values` and :samp:`mygrid.final_values`, respectively. To get a tuple of all the available values use .. code-block:: python mygrid.initial_values.dtype.names mygrid.final_values.dtype.names You can access the initial value of any physical grid property "PHYS" with :samp:`mygrid.initial_values[{PHYS}]`. It will return a numpy array with the values of this property for all the runs. Note that these physical properties of the binaries in the grid are different from the grid configuration properties listed above. Then, you can find the initial mass of star 1 in the third MESA run with .. code-block:: python mygrid.initial_values['star_1_mass'][2] .. note:: Remember that the first run has the index :samp:`0` and the last one :samp:`len(mygrid)-1`. Each grid property will have the same number and order of MESA run entries in the initial and final values. This holds for the list of MESA directories from which the runs are extracted, too. .. code-block:: python mygrid.MESA_dirs You can retrieve individual runs by index. :samp:`mygrid[{IDX}]` is a :samp:`PSyRunView` object, which contains the data of the run of index :samp:`IDX`. The :samp:`PSyRunView` object contains seven components: .. table:: :samp:`PSyRunView` object components ================ =========== Component Description ================ =========== 'initial_values' all initial values of the run 'final_values' all final values of the run including termination flags 'binary_history' the binary history 'history1' the history of star 1 'history2' the history of star 2 'final_profile1' the final profile of star 1 'final_profile2' the final profile of star 2 ================ =========== Again, you can check for the contents of the individual runs with :samp:`dtype.names`, e.g. .. code-block:: python myrun = mygrid[0] myrun['binary_history'].dtype.names The example above finds the initial mass of star 1 in the third MESA run by indexing the list :samp:`mygrid.initial_values`. You can get the same value from the list of initial values associated with a single MESA run: .. code-block :: python mygrid[2]['initial_values']['star_1_mass'] You can get something close to the initial value with: .. code-block :: python mygrid[2]['binary_history']['star_1_mass'][0] But this will not give the initial value, while it is close to it. This is because MESA has a slightly different value in the first line of the history files compared to the given initial value. The final values and the derived initial values instead are the same as the last or first values in the corresponding history. .. note:: For efficiency reasons not all the :samp:`PSyGrid` object is loaded into RAM. Instead parts are reads from the associated hdf5 file if needed. For this reason, it is discouraged to refer to the same values more than once in a code. If you need the same value more often, you should store it in a local variable. Plot a `PSyGrid` object ~~~~~~~~~~~~~~~~~~~~~~~ Beside getting the values itself there are plotting functionalities available to display the content of a :samp:`PSyGrid` object. There are three main plotting functionalities: - :samp:`plot`: This creates a one dimensional plot from the :samp:`PSyGrid`. An example can be found in the :ref:`tutorials <plot_1d>`. The code details are available in the :py:func:`PSyGrid.plot <posydon.grids.psygrid.PSyGrid.plot>` code and the :py:class:`visualization <posydon.visualization.plot1D>` library. - :samp:`plot2D`: This creates a two dimensional representation from the :samp:`PSyGrid`. Again, an example can be found in the :ref:`tutorials <plot_2d>`. The code details are available in the :py:func:`PSyGrid.plot <posydon.grids.psygrid.PSyGrid.plot2D>` code and the :py:class:`visualization <posydon.visualization.plot2D>` library. - :samp:`HR`: This is similar to :samp:`plot` but specialized for producing Hertzsprung–Russell diagrams. Work on/with a `PSyGrid` object ------------------------------- Loop over a `PSyGrid` object ~~~~~~~~~~~~~~~~~~~~~~~~~~ Similarly to accessing a single value in the :samp:`PSyGrid` object, we can loop over a :samp:`PSyGrid` object, which will loop over the individual runs in the :samp:`PSyGrid` object. Hence the following two code snippets will produce the same output. The first one loops through the numpy array of the initial companion masses: .. code-block:: python for mass in mygrid.initial_values['star_2_mass']: print(mass) while the second one loops through the runs and prints the initial companion mass of each one: .. code-block:: python for run in mygrid: print(run['initial_values']['star_2_mass']) Expand a `PSyGrid` object ~~~~~~~~~~~~~~~~~~~~~~~~~ Because of the complexity of the :samp:`PSyGrid` object, we encourage users to only use our dedicated functions to add content to the object. There is a function to add an extra column to the :samp:`final_values`. Here is an example of how to add a new column that contains the final orbital period, in units of years instead of days: .. code-block:: python new_column_data = mygrid.final_values['period_days']/365.25 mygrid.add_column('period_years', new_column_data, where='final_values', overwrite=False) The four arguments are a string with the name of the new field, the data to be stored in the column, the component of the :samp:`PSyGrid` object to which the column will be added, and a boolean indicating whether the column should overwrite any existing column with the same name. .. warning:: The new data has to have as many entries as the :samp:`PSyGrid` object has runs. .. note:: Currently, the parameter :samp:`where` only supports the value 'final_values'. Join two or more `PSyGrid` objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are different reasons why you might have several :samp:`PSyGrid` objects that you would like to combine into a single grid later, e.g. adding reruns. POSYDON has a function called :samp:`join_grids` to do this for you. To avoid too many conflicts with possible modifications of already loaded :samp:`PSyGrid` objects, this function is not part of the :samp:`PSyGrid` object class. Instead, it takes as an argument the list of paths to the hdf5 files containing :samp:`PSyGrid` objects to be combined to a new one, and then a path to the hdf5 file of the new grid to be generated. The :samp:`join_grids` function will check whether the grids are compatible and join them if possible. Additionally, you can optionally specify the arguments :samp:`compression`, :samp:`description`, and :samp:`verbose`. .. table:: Arguments of the :samp:`join_grids` function ================= ======== =========== Argument Default Description ================= ======== =========== input_paths None list of the paths to the grid files to be joined output_path None path for the new grid file containing the joined grid. compression 'gzip9' compression details description 'joined' description of the new joined grid verbose True whether the function reports by printing to standard output. ================= ======== =========== .. note:: If there are common systems in two or more grids, this routine will only put the last run with same initial conditions in the newly combined :samp:`PSyGrid` object. We recommend that you use the :ref:`post-processing pipeline <pipeline>` to create and join grids. .. Extract the initial and final values as a pandas data frame ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TODO: get_pandas_initial_final() Get reruns from a `PSyGrid` object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Usually, not all runs of a grid will be successfully run in MESA. You may want to rerun some of them with changed parameters. The function :samp:`rerun` exports runs from a :samp:`PSyGrid` object to be run again. There are two options: 1. Write your own logic and create a numpy array with the indices of the systems that you would like to run again. 2. Specify which termination flag(s) necessitate a rerun of the system. .. .. table:: Arguments of the :samp:`rerun` function ================= ======= =========== Argument Default Description ================= ======= =========== path_to_file './' where to create the file(s) for the rerun runs_to_rerun None a numpy array containing the indices of the runs in the :samp:`PSyGrid` object (if given, leave :samp:`termination_flags=None`) termination_flags None a single termination flag code or a list of them (if given, leave :samp:`runs_to_rerun=None`) new_mesa_flag None dictionary with the names and the values of MESA parameters to be changed for the inlists of the new runs ================= ======= =========== .. table:: Arguments of the :samp:`rerun` function ================= ======= =========== Argument Default Description ================= ======= =========== path_to_file './' where to create the file(s) for the rerun runs_to_rerun None a list containing the indices of the runs in the :samp:`PSyGrid` object termination_flags None a single termination flag code, or a list of them new_mesa_flag None dictionary with the names and the values of MESA parameters to be changed for the inlists of the new runs flags_to_check None a termination flag key or a list of them (if :samp:`None`, check only 'termination_flag_1') ================= ======= =========== .. note:: If both :samp:`runs_to_rerun` and :samp:`termination_flags` are given, all systems matching at least one of the two conditions will be selected for rerun. The :ref:`post-processing pipeline <pipeline_stepR>` provides some pre-defined rerun options. Close associated hdf5 file ~~~~~~~~~~~~~~~~~~~~~~~~~~ Finally, you can close the hdf5 file, which is recommended to ensure that all your changes on the :samp:`PSyGrid` object are safely written into the file. .. code-block:: python mygrid.close() This is also done if you call the destructor of the :samp:`PSyGrid` object. .. code-block:: python del mygrid The code summary of the :samp:`PSyGrid` object can be found at the :py:class:`~posydon.grids.psygrid` reference page.