Quick-start guide

This quick-start guide will walk you into the main steps to document your numerical project. If you want to skip the tour and directly see an example, see Full example script.

Project and study

Creation and persistency

To create a Project and save it into a SimulationStudy HDF5 file, you may run this minimal Python script :

>>> from astrophysix.simdm import SimulationStudy, Project, ProjectCategory
>>> proj = Project(category=ProjectCategory.Cosmology, project_title="Extreme Horizons cosmology project")
>>> study = SimulationStudy(project=proj)
>>> study.save_HDF5("./EH_study.h5")

Loading a study

To read the SimulationStudy from your HDF5 file, update it and save the updated study back in its original file :

>>> from astrophysix.simdm import SimulationStudy
>>> study = SimualationStudy.load_HDF5("./EH_study.h5")
>>> proj = study.project
>>> proj.short_description = "This is a short description (one-liner) of my project."
>>> # Saves your updated study back in the same file './EH_study.h5'
>>> study.save_HDF5()

Study information

A SimulationStudy object contains details on when it has been created (creation_time property) and last modified (last_modification_time property) :

>>> study.creation_time
datetime.datetime(2020, 9, 4, 14, 05, 21, 84601, tzinfo=datetime.timezone.utc)
>>> study.last_modification_time
datetime.datetime(2020, 9, 18, 15, 12, 27, 14512, tzinfo=datetime.timezone.utc)

Full project initialization example

To initialize a Project, you only need to set category and project_title properties. Here is a more complete example of a Project initialization with all optional attributes :

>>> proj = Project(category=ProjectCategory.StarFormation, alias="FRIG",
...                project_title="Self-regulated magnetised ISM modelisation",
...                short_description="Short description of my 'FRIG' project",
...                general_description="""This is a pretty long description for my project spanning over multiple lines
...                if necessary""",
...                data_description="The data available in this project...",
...                directory_path="/path/to/project/data/")

Warning

Setting the alias property is necessary only if you wish to upload your study on the Galactica simulation database. See Why an alias ? and How can I check validity for Galactica ?

See also

Simulation codes and runs

To add a simulation run into your project, the definition of a SimulationCode is mandatory. Once defined, you can create a Simulation based on that simulation code:

>>> from astrophysix.simdm.protocol import SimulationCode
>>> from astrophysix.simdm.experiment import Simulation
>>> ramses = SimulationCode(name="Ramses 3.1 (MHD)", code_name="RAMSES")
>>> simu = Simulation(simu_code=ramses, name="Hydro run full resolution")

To add the simulation to the project, use the Project.simulations property (ObjectList):

>>> proj.simulations.add(simu)
>>> proj.simulations
Simulation list :
+---+---------------------------+----------------------------------------+
| # |           Index           |                   Item                 |
+---+---------------------------+----------------------------------------+
| 0 | Hydro run full resolution | 'Hydro run full resolution' simulation |
+---+---------------------------+----------------------------------------+
| 1 | Hydro run full resolution | 'MHD run full resolution' simulation   |
+---+---------------------------+----------------------------------------+

See also

Post-processing runs

Optionally, you can add PostProcessingRun into a Simulation using the Simulation.post_processing_runs property (ObjectList). To create a PostProcessingRun, you must first define a PostProcessingCode :

from astrophysix.simdm.protocol import PostProcessingCode
from astrophysix.simdm.experiment import PostProcessingCodeRun

radmc = PostProcessingCode(name="RADMC-3D", code_name="RADMC-3D", code_version="2.0")
pprun = PostProcessingCodeRun(ppcode=radmc, name="Synthetic observable creation of pre-stellar cores")

# Add post-pro run into the simulation
simu.post_processing_runs.add(pprun)

See also

Results and snapshots

Experiment-wide

You can add results into any simulation or post-processing run. If it is an experiment-wide result, create a GenericResult and use the Simulation.generic_results or PostProcessingRun.generic_results property (ObjectList) to add it into your run :

from astrophysix.simdm.results import GenericResult

res1 = GenericResult(name="Star formation history", directory_path="/my/path/to/result",
                     description="""This is the star formation history during the 2
                                    Myr of the galaxy major merger""")
simu.generic_results.add(res1)

Time-specific

Otherwise, if it is time-specific result, create a Snapshot and use the Simulation.snapshots or PostProcessingRun.snapshots property (ObjectList) to add it into your run :

from astrophysix.simdm.results import Snapshot
from astrophysix import units as U

sn34 = Snapshot(name="Third pericenter", time=(254.7, U.Myr),
                directory_path="/my/path/to/simu/outputs/output_00034")
simu.snapshots.add(sn34)

See also

Datafiles

You can add Datafile objects into any Snapshot or GenericResult :

>>> from astrophysix.simdm.datafiles import Datafile
>>>
>>> # Add a datafile into a snapshot
>>> imf_df = Datafile(name="Initial mass function",
...                   description="This is my IMF plot detailed description...")
>>> sn34.datafiles.add(imf_df)

And then embed files from your local filesystem into the Datafile (the file raw byte array will be imported along with the original file name and last modification date):

>>> from astrophysix.simdm.datafiles import image
>>> from astrophysix.utils.file import FileType
>>>
>>> # Attach files to datafile
>>> imf_df[FileType.PNG_FILE] = os.path.join("/data", "io", "datafiles", "plot_image_IMF.png")
>>> jpg_fpath = os.path.join("/data", "io", "datafiles", "plot_with_legend.jpg")
>>> imf_df[FileType.JPEG_FILE] = image.JpegImageFile.load_file(jpg_fpath)
>>> imf_df[FileType.HDF5_FILE] = os.path.join("/data", "io", "HDF5", "stars.h5")

Anyone can reopen your SimulationStudy HDF5 file, read the attached files and re-export them on their local filesystem to retrieve a carbon copy of your original file:

>>> imf_df[FileType.JPEG_FILE].save_to_disk("/home/user/Desktop/export_plot.jpg")
File '/home/user/Desktop/export_plot.jpg' saved

See also

Full example script

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the 'astrophysix' Python package.
#
# Copyright © Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA)
#
#  FREE SOFTWARE LICENCING
#  -----------------------
# This software is governed by the CeCILL license under French law and abiding by the rules of distribution of free
# software. You can use, modify and/or redistribute the software under the terms of the CeCILL license as circulated by
# CEA, CNRS and INRIA at the following URL: "http://www.cecill.info". As a counterpart to the access to the source code
# and rights to copy, modify and redistribute granted by the license, users are provided only with a limited warranty
# and the software's author, the holder of the economic rights, and the successive licensors have only limited
# liability. In this respect, the user's attention is drawn to the risks associated with loading, using, modifying
# and/or developing or reproducing the software by the user in light of its specific status of free software, that may
# mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and
# experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the
# software's suitability as regards their requirements in conditions enabling the security of their systems and/or data
# to be ensured and, more generally, to use and operate it in the same conditions as regards security. The fact that
# you are presently reading this means that you have had knowledge of the CeCILL license and that you accept its terms.
#
#
# COMMERCIAL SOFTWARE LICENCING
# -----------------------------
# You can obtain this software from CEA under other licencing terms for commercial purposes. For this you will need to
# negotiate a specific contract with a legal representative of CEA.
#
from __future__ import print_function, unicode_literals
import os
import numpy as N

from astrophysix.simdm import SimulationStudy, Project, ProjectCategory
from astrophysix.simdm.experiment import Simulation, AppliedAlgorithm, ParameterSetting, ParameterVisibility,\
    ResolvedPhysicalProcess
from astrophysix.simdm.protocol import SimulationCode, AlgoType, Algorithm, InputParameter, PhysicalProcess, Physics
from astrophysix.simdm.results import GenericResult, Snapshot
from astrophysix.simdm.datafiles import Datafile, PlotType, PlotInfo
from astrophysix.utils.file import FileType
from astrophysix import units as U

# ----------------------------------------------- Project creation --------------------------------------------------- #
# Available project categories are :
# - ProjectCategory.SolarMHD
# - ProjectCategory.PlanetaryAtmospheres
# - ProjectCategory.StarPlanetInteractions
# - ProjectCategory.StarFormation
# - ProjectCategory.Supernovae
# - ProjectCategory.GalaxyFormation
# - ProjectCategory.GalaxyMergers
# - ProjectCategory.Cosmology
proj = Project(category=ProjectCategory.StarFormation, project_title="Frig",
               alias="FRIG", short_description="Short description of my 'FRIG' project",
               general_description="""This is a pretty long description for my project""",
               data_description="The data available in this project...", directory_path="/path/to/project/data/")
print(proj)  # "[Star formation] 'Frig' project"
# -------------------------------------------------------------------------------------------------------------------- #


# --------------------------------------- Simulation code definition ------------------------------------------------- #
ramses = SimulationCode(name="Ramses 3 (MHD)", code_name="Ramses", code_version="3.10.1", alias="RAMSES_3",
                        url="https://www.ics.uzh.ch/~teyssier/ramses/RAMSES.html",
                        description="This is a fair description of the Ramses code")
# => Add algorithms : available algorithm types are :
# - AlgoType.AdaptiveMeshRefinement
# - AlgoType.VoronoiMovingMesh
# - AlgoType.SmoothParticleHydrodynamics
# - AlgoType.Godunov
# - AlgoType.PoissonMultigrid
# - AlgoType.PoissonConjugateGradient
# - AlgoType.ParticleMesh
# - AlgoType.FriendOfFriend
# - AlgoType.HLLCRiemann
# - AlgoType.RayTracer
amr = ramses.algorithms.add(Algorithm(algo_type=AlgoType.AdaptiveMeshRefinement, description="AMR descr"))
ramses.algorithms.add(Algorithm(algo_type=AlgoType.Godunov, description="Godunov scheme"))
ramses.algorithms.add(Algorithm(algo_type=AlgoType.HLLCRiemann, description="HLLC Riemann solver"))
ramses.algorithms.add(Algorithm(algo_type=AlgoType.PoissonMultigrid, description="Multigrid Poisson solver"))
ramses.algorithms.add(Algorithm(algo_type=AlgoType.ParticleMesh, description="PM solver"))

# => Add input parameters
ramses.input_parameters.add(InputParameter(key="levelmin", name="Lmin",
                                           description="min. level of AMR refinement"))
lmax = ramses.input_parameters.add(InputParameter(key="levelmax", name="Lmax",
                                                  description="max. level of AMR refinement"))

# => Add physical processes : available physics are :
# - Physics.SelfGravity
# - Physics.Hydrodynamics
# - Physics.MHD
# - Physics.StarFormation
# - Physics.SupernovaeFeedback
# - Physics.AGNFeedback
# - Physics.MolecularCooling
ramses.physical_processes.add(PhysicalProcess(physics=Physics.StarFormation, description="descr sf"))
ramses.physical_processes.add(PhysicalProcess(physics=Physics.Hydrodynamics, description="descr hydro"))
grav = ramses.physical_processes.add(PhysicalProcess(physics=Physics.SelfGravity, description="descr self G"))
ramses.physical_processes.add(PhysicalProcess(physics=Physics.SupernovaeFeedback, description="SN feedback"))
# -------------------------------------------------------------------------------------------------------------------- #


# -------------------------------------------- Simulation setup ------------------------------------------------------ #
simu = Simulation(simu_code=ramses, name="My most important simulation", alias="SIMU_1", description="Simu description",
                  execution_time="2020-03-01 18:45:30", directory_path="/path/to/my/project/simulation/data/")
proj.simulations.add(simu)

# Add applied algorithms implementation details. Warning : corresponding algorithms must have been added in the 'ramses'
# simulation code.
simu.applied_algorithms.add(AppliedAlgorithm(algorithm=amr, details="My AMR implementation [Teyssier 2002]"))
simu.applied_algorithms.add(AppliedAlgorithm(algorithm=ramses.algorithms[AlgoType.HLLCRiemann.name],
                                             details="My Riemann solver implementation [Teyssier 2002]"))

# Add parameter setting. Warning : corresponding input parameter must have been added in the 'ramses' simulation code.
# Available parameter visibility options are :
# - ParameterVisibility.NOT_DISPLAYED
# - ParameterVisibility.ADVANCED_DISPLAY
# - ParameterVisibility.BASIC_DISPLAY
simu.parameter_settings.add(ParameterSetting(input_param=ramses.input_parameters["Lmin"], value=8,
                                             visibility=ParameterVisibility.BASIC_DISPLAY))
simu.parameter_settings.add(ParameterSetting(input_param=lmax, value=12,
                                             visibility=ParameterVisibility.BASIC_DISPLAY))

# Add resolved physical process implementation details. Warning : corresponding physical process must have been added to
# the 'ramses' simulation code
simu.resolved_physics.add(ResolvedPhysicalProcess(physics=ramses.physical_processes[Physics.StarFormation.name],
                                                  details="Star formation specific implementation"))
simu.resolved_physics.add(ResolvedPhysicalProcess(physics=grav, details="self-gravity specific implementation"))
# -------------------------------------------------------------------------------------------------------------------- #


# -------------------------------------- Simulation generic result and snapshots ------------------------------------- #
# Generic result
gres = GenericResult(name="Key result 1 !", description="My description", directory_path="/my/path/to/result")
simu.generic_results.add(gres)

# Simulation snapshot
# In one-line
sn = simu.snapshots.add(Snapshot(name="My best snapshot !", description="My first snapshot description",
                                 time=(125, U.kyr), physical_size=(250.0, U.kpc), directory_path="/path/to/snapshot1",
                                 data_reference="OUTPUT_00056"))
# Or create snapshot, then add it to the simulation
sn2 = Snapshot(name="My second best snapshot !", description="My second snapshot description", time=(0.26, U.Myr),
               physical_size=(0.25, U.Mpc), directory_path="/path/to/snapshot2", data_reference="OUTPUT_00158")
simu.snapshots.add(sn2)
# -------------------------------------------------------------------------------------------------------------------- #


# ---------------------------------------------------- Result datafiles ---------------------------------------------- #
# Datafile creation
imf_df = sn.datafiles.add(Datafile(name="Initial mass function plot",
                                   description="This is my plot detailed description"))

# Add attached files to a datafile (1 per file type). Available file types are :
# - FileType.HDF5_FILE
# - FileType.PNG_FILE
# - FileType.JPEG_FILE
# - FileType.FITS_FILE
# - FileType.TARGZ_FILE
# - FileType.PICKLE_FILE
# - FileType.JSON_FILE
# - FileType.CSV_FILE
# - FileType.ASCII_FILE
imf_df[FileType.PNG_FILE] = os.path.join("/data", "io", "datafiles", "plot_image_IMF.png")
imf_df[FileType.JPEG_FILE] = os.path.join("/data", "io", "datafiles", "plot_with_legend.jpg")
imf_df[FileType.FITS_FILE] = os.path.join("/data", "io", "datafiles", "cassiopea_A_0.5-1.5keV.fits")
imf_df[FileType.TARGZ_FILE] = os.path.join("/data", "io", "datafiles", "archive.tar.gz")
imf_df[FileType.JSON_FILE] = os.path.join("/data", "io", "datafiles", "test_header_249.json")
imf_df[FileType.ASCII_FILE] = os.path.join("/data", "io", "datafiles", "abstract.txt")
imf_df[FileType.HDF5_FILE] = os.path.join("/data", "io", "HDF5", "study.h5")
imf_df[FileType.PICKLE_FILE] = os.path.join("/data", "io", "datafiles", "dict_saved.pkl")

# Datafile plot information (for plot future updates and online interactive visualisation on Galactica web pages).
# Available plot types are :
# - LINE_PLOT
# - SCATTER_PLOT
# - HISTOGRAM
# - HISTOGRAM_2D
# - IMAGE
# - MAP_2D
imf_df.plot_info = PlotInfo(plot_type=PlotType.LINE_PLOT, xaxis_values=N.array([10.0, 20.0, 30.0, 40.0, 50.0]),
                            yaxis_values=N.array([1.256, 2.456, 3.921, 4.327, 5.159]), xaxis_log_scale=False,
                            yaxis_log_scale=False, xaxis_label="Mass", yaxis_label="Probability", xaxis_unit=U.Msun,
                            plot_title="Initial mass function", yaxis_unit=U.Mpc)
# -------------------------------------------------------------------------------------------------------------------- #


# Save study in HDF5 file
study = SimulationStudy(project=proj)
study.save_HDF5("./frig_study.h5")

# Eventually reload it from HDF5 file to edit its content
# study = SimulationStudy.load_HDF5("./frig_study.h5")