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 :

Available project categories are :

>>> 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

Object catalogs

New in version 0.5.0

You can add object catalogs into any result (GenericResult or Snapshot), using the catalogs property :

>>> from astrophysix.simdm.catalogs import TargetObject, ObjectProperty, Catalog, CatalogField,\
...                                        PropertyFilterFlag
>>> from astrophysix.simdm.utils import DataType
>>> from astrophysix import units as U
>>>
>>> cluster = TargetObject(name="Galaxy cluster")
>>> x = tobj.object_properties.add(ObjectProperty(property_name="x", unit=U.Mpc,
...                                               filter_flag=PropertyFilterFlag.BASIC_FILTER))
>>> y = tobj.object_properties.add(ObjectProperty(property_name="y",  unit=U.Mpc,
...                                               filter_flag=PropertyFilterFlag.BASIC_FILTER))
>>> z = tobj.object_properties.add(ObjectProperty(property_name="z",  unit=U.Mpc,
...                                               filter_flag=PropertyFilterFlag.BASIC_FILTER))
>>> # You may group properties in a property group
>>> pos = ObjectPropertyGroup(group_name="position")
>>> pos.group_properties.add(x)
>>> pos.group_properties.add(y)
>>> pos.group_properties.add(z)
>>>
>>> m = tobj.object_properties.add(ObjectProperty(property_name="M500", unit=U.Msun,
...                                               filter_flag=PropertyFilterFlag.BASIC_FILTER))
>>>
>>> # Define a catalog
>>> cat = Catalog(target_object=tobj, name="Galaxy cluster catalog")
>>> # Add the catalog fields into the catalog (100 clusters)
>>> cat.catalog_fields.add(CatalogField(x, values=N.random.uniform(size=100)))
>>> cat.catalog_fields.add(CatalogField(y, values=N.random.uniform(size=100)))
>>> cat.catalog_fields.add(CatalogField(z, values=N.random.uniform(size=100)))
>>> cat.catalog_fields.add(CatalogField(m, values=N.random.uniform(size=100)))
>>>
>>> # Add the catalog in the snapshot
>>> sn34.catalogs.add(cat)

See also

Datafiles

You can add Datafile objects into any Snapshot or GenericResult, or even (new in version 0.5.0) in an object Catalog :

>>> 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

Data-processing services

You can define bindings between Snapshot objects (or even Catalog objects) and Galactica online data-processing services to allow visitors to request the execution of post-processing jobs on your raw astrophysical simulation data :

>>> from astrophysix.simdm.results import Snapshot
>>> from astrophysix import units as U
>>> from astrophysix.simdm.services import DataProcessingService

>>> sn_Z2 = Snapshot(name="Z~2", data_reference="output_00481")
>>> simu.snapshots.add(sn_Z2)
>>>
>>> # Add data processing services to a snapshot
>>> dps = DataProcessingService(service_name="column_density_map",
...                             data_host="My_Dept_Cluster")
>>> sn.processing_services.add(dsp)

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.catalogs import TargetObject, ObjectProperty, PropertySortFlag, PropertyFilterFlag, \
    ObjectPropertyGroup, Catalog, CatalogField
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, AsciiFile
from astrophysix.simdm.services import DataProcessingService, CatalogDataProcessingService, CatalogFieldBinding
from astrophysix.simdm.utils import DataType
from astrophysix.utils.file import FileType
from astrophysix import units as U

# ----------------------------------------------- Project creation --------------------------------------------------- #
# Available project categories are :
# - ProjectCategory.SolarMHD
# - ProjectCategory.PlanetaryAtmospheres
# - ProjectCategory.StellarEnvironments
# - 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.StructuredGrid
# - AlgoType.AdaptiveMeshRefinement
# - AlgoType.VoronoiMovingMesh
# - AlgoType.SpectralMethod
# - AlgoType.SmoothParticleHydrodynamics
# - AlgoType.Godunov
# - AlgoType.PoissonMultigrid
# - AlgoType.PoissonConjugateGradient
# - AlgoType.ParticleMesh
# - AlgoType.NBody
# - AlgoType.FriendOfFriend
# - AlgoType.HLLCRiemann
# - AlgoType.RayTracer
# - AlgoType.RadiativeTransfer
# - AlgoType.RungeKutta
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.ExternalGravity
# - Physics.Hydrodynamics
# - Physics.MHD
# - Physics.StarFormation
# - Physics.SupernovaeFeedback
# - Physics.AGNFeedback
# - Physics.SupermassiveBlackHoleFeedback
# - Physics.StellarIonisingRadiation
# - Physics.StellarUltravioletRadiation
# - Physics.StellarInfraredRadiation
# - Physics.ProtostellarJetFeedback
# - Physics.Chemistry
# - Physics.AtomicCooling
# - Physics.DustCooling
# - Physics.MolecularCooling
# - Physics.TurbulentForcing
# - Physics.RadiativeTransfer

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/",
                  config_file=AsciiFile.load_file(os.path.join("/run", "cfg", "SIMU_1", "blast_3D.ini")))
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)
# -------------------------------------------------------------------------------------------------------------------- #


# ----------------------------------------------------- Result products ---------------------------------------------- #
# Target object properties have filter/sort flags to display them in various configurations on Galactica. Available
# flag values are :
# - PropertyFilterFlag.NO_FILTER
# - PropertyFilterFlag.BASIC_FILTER
# - PropertyFilterFlag.ADVANCED_FILTER

# - PropertySortFlag.NO_SORT
# - PropertySortFlag.BASIC_SORT
# - PropertySortFlag.ADVANCED_SORT
tobj = TargetObject(name="Spiral galaxy", description="Disk-like galaxy with spiral arms and sometimes bars")
id_gal = tobj.object_properties.add(ObjectProperty(property_name="gal_id", sort_flag=PropertySortFlag.ADVANCED_SORT,
                                                   filter_flag=PropertyFilterFlag.NO_FILTER, dtype=DataType.INTEGER,
                                                   description="Galaxy global index"))
x = tobj.object_properties.add(ObjectProperty(property_name="pos_x", sort_flag=PropertySortFlag.ADVANCED_SORT,
                                              unit=U.kpc, filter_flag=PropertyFilterFlag.BASIC_FILTER,
                                              #  dtype=DataType.REAL,
                                              description="Galaxy position coordinate along x-axis"))
y = tobj.object_properties.add(ObjectProperty(property_name="pos_y", sort_flag=PropertySortFlag.ADVANCED_SORT,
                                              unit=U.kpc, filter_flag=PropertyFilterFlag.BASIC_FILTER,
                                              #  dtype=DataType.REAL,
                                              description="Galaxy position coordinate along y-axis"))
z = tobj.object_properties.add(ObjectProperty(property_name="pos_z", sort_flag=PropertySortFlag.ADVANCED_SORT,
                                              unit=U.kpc, filter_flag=PropertyFilterFlag.BASIC_FILTER,
                                              #  dtype=DataType.REAL,
                                              description="Galaxy position coordinate along z-axis"))
# Property group (optional)
pos = tobj.property_groups.add(ObjectPropertyGroup(group_name="pos", description="galaxy position"))
pos.group_properties.add(x)
pos.group_properties.add(y)
pos.group_properties.add(z)

# Target object catalog + field value arrays
cat = sn.catalogs.add(Catalog(target_object=tobj, name="Spiral galaxy filtered catalog",
                              description="Full description of my catalog containing 200 spiral galaxies."))
fidgal = cat.catalog_fields.add(CatalogField(obj_prop=id_gal, values=N.arange(1, 200)))
fx = cat.catalog_fields.add(CatalogField(obj_prop=x, values=N.random.uniform(size=200)))
fy = cat.catalog_fields.add(CatalogField(obj_prop=y, values=N.random.uniform(size=200)))
fz = cat.catalog_fields.add(CatalogField(obj_prop=z, values=N.random.uniform(size=200)))

# Add datafiles to the catalog if required.
cdf1 = cat.datafiles.add(Datafile(name="My datafile", description="---"))
cdf1[FileType.PNG_FILE] = os.path.join("/data", "io", "datafiles", "plot_image_IMF.png")
cdf2 = cat.datafiles.add(Datafile(name="My datafile (2)", description="---"))
cdf2[FileType.JPEG_FILE] = os.path.join("/data", "io", "datafiles", "plot_with_legend.jpg")
# -------------------------------------------------------------------------------------------------------------------- #


# ---------------------------------------------- Data processing services bindings ----------------------------------- #
# Add data processing services to a snapshot
dps1 = sn.processing_services.add((DataProcessingService(service_name="column_density_map",
                                                         data_host="MyDeptCluster")))
dps2 = sn2.processing_services.add((DataProcessingService(service_name="ppv_cube", data_host="Lab_GPU_cluster")))

# Add data processing services to a target object catalog
dps3 = CatalogDataProcessingService(service_name="slice_map", data_host="Lab_Cluster")
cat.processing_services.add(dps3)
# Define catalog field bindings to automatically fill the data processing service parameter value 'pv' with a catalog
# field value 'fv' of one of your catalog's object according to the formula : pv = scale * fv + offset. Default scale
# value is 1.0 and default offset is 0.0.
fbx = dps1.catalog_field_bindings.add(CatalogFieldBinding(catalog_field=fx, param_key="x",
                                                          scale=1.0e2, offset=-50.0))
fby = dps1.catalog_field_bindings.add(CatalogFieldBinding(catalog_field=fy, param_key="y",
                                                          scale=1.0e2, offset=-50.0))
fbz = dps1.catalog_field_bindings.add(CatalogFieldBinding(catalog_field=fz, param_key="z",
                                                          scale=1.0e2, offset=-50.0))

# -------------------------------------------------------------------------------------------------------------------- #


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

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