Getting started

The mojito package provides tools to download, read, and write Mojito L1 files (also known as “bricks”). This guide will walk you through the basic steps to get started with Mojito L1 files.

Installation

To install the mojito package, follow the instructions provided in the Installation section of the documentation.

Pour yourself a drink

Use the mojito.download module to download Mojito L1 files and source parameter catalogs from the Mojito brick market. Convenience functions are provided to handle authentication, caching (to reduce disk and network usage), integrity checks and versioning.

Note

Downloading Mojito L1 files and source catalogs from the Mojito brick market requires authentication using a Nextcloud username and token. For more information on how to obtain your credentials and use them with Mojito, see the Authentication section.

Download brick files

You can use the mojito.download.download_brick() function to download a Mojito L1 brick file. Simply provide the source type (e.g., “mbhb”, “gb”, “noise”, “combined”, etc.) and an optional source identifier.

from mojito.download import download_brick

# Download Mojito L1 brick file for a massive black hole binary source
brick_file = download_brick("mbhb", 12)

# Download Mojito L1 brick file for noise (no source id needed)
noise_file = download_brick("noise", version=1)

# Subsequent calls will use the cached file if available (no re-download)
brick_file_cached = download_brick("mbhb", 12)

# Provide authentication explicitly if needed
brick_file = download_brick("mbhb", 12, username="myuser", token="mytoken")

# Download reduced (downsampled and stripped off) brick
# Warning: reduced bricks are not official L1 files and may be missing some
# quantities; use only for testing and development!
reduced_brick_file = download_brick("mbhb", 12, reduced=True)

If the file is not already cached, it will be downloaded from the Mojito brick market and the path to the downloaded file will be returned. If the file is already cached, the path to the cached file will be returned without re-downloading.

Note

Authentication is required to download files. You can provide credentials via function arguments (username and token), environment variables (MOJITO_USERNAME and MOJITO_TOKEN), or you will be prompted interactively if credentials are not available.

Fetch source parameters

You can also use mojito.download.get_source_params() to download the relevant source catalog and fetch the source parameters for a specified source. You can also download an entire catalog of source parameters using mojito.download.download_catalog().

from mojito.download import download_catalog, get_source_params

# Fetch source parameters for a specified source
source_params = get_source_params("mbhb", 12)

# Download the entire catalog of galactic binaries
galactic_catalog_file = download_catalog("gb")

More details on the downloading functions and their options can be found in Download data.

Caching

By default, downloaded files are cached in a directory following the XDG Base Directory Specification (typically ~/.cache/mojito). You can change this location by setting the environment variable MOJITO_CACHE_DIR, or by passing the cache_dir keyword argument to the download functions.

Note

Make sure that the cache directory has enough disk space to store the downloaded files.

You can clear the cache by using the mojito.download.clear_cache() function.

from mojito.download import get_cache_dir, clear_cache

# Get the current cache directory
cache_dir = get_cache_dir()
print(f"Current cache directory: {cache_dir}")

# Clear the cache directory
clear_cache()

Warning

Use with caution as this will delete all cached files. All files will need to be re-downloaded if needed again.

Versioning

By default, the latest version of each brick or parameter catalog is downloaded. You can specify a different version by providing the version keyword argument.

Command-line interface

The mojito package also provides a command-line interface (CLI) to download Mojito L1 files and source catalogs. You can use

$ mojito download-brick mbhb 12
$ mojito source-params mbhb 12
$ mojito download-catalog gb
$ mojito clear-cache

More details on the CLI and its commands can be found in Command-line interface.

Have a drink

The mojito.reader module provides the mojito.reader.MojitoL1File class to read Mojito L1 brick files. This class provides access to the various datasets stored in the file, including TDI observables, light travel times, spacecraft orbits, and noise estimates.

Reading datasets

Dataset access is done via attributes of the mojito.reader.MojitoL1File object. Convenience properties are provided to stack and rescale datasets (unit conversion) as needed.

Note

Always use the context manager (with statement) to open Mojito L1 files. This ensures that the file is properly closed after use, preventing resource leaks.

from mojito import MojitoL1File

with MojitoL1File("path/to/file.h5") as f:

    # TDI observables
    x2 = f.tdis.x2[:]  # TDI X2 observable in Hz
    y2 = f.tdis.y2[:]  # TDI Y2 observable in Hz
    z2 = f.tdis.z2[:]  # TDI Z2 observable in Hz

    # Complete set of TDI observables in Doppler units
    xyz = f.tdis.xyz_doppler[:]  # TDI XYZ in Doppler units
    aet = f.tdis.aet_doppler[:]  # TDI AET in Doppler units

    # Associated time sampling
    tdi_time_sampling = f.tdis.time_sampling
    tdi_dt = tdi_time_sampling.dt      # time step in seconds
    tdi_times = tdi_time_sampling.t()  # array of times in seconds

    # Light travel times and spacecraft orbits for response function
    ltts = f.ltts.ltts[:]           # Stacked light travel times
    ltt_times = f.ltts.time_sampling.t()
    orbits = f.orbits.positions[:]  # Stacked spacecraft positions
    orbits_times = f.orbits.time_sampling.t()

    # Noise estimates for TDI AET
    noise_aet = f.noise_estimates.aet[:]  # in Hz^2/Hz
    noise_times = f.noise_estimates.time_sampling.t()  # array of times
    noise_freqs = f.noise_estimates.freq_sampling.f()  # array of freqs

Multiple bricks can be read together by providing a list of file paths to the constructor. The datasets will be automatically combined across bricks.

with MojitoL1File(["file1.h5", "file2.h5"]) as f:

    # Access the first 1000 samples of XYZ TDI observables (Doppler units)
    # Datasets are automatically combined across bricks
    xyz = f.tdis.xyz_doppler[:1000]              # shape (1000, 3)

Note that some datasets will be summed (typically, TDI observables), while others will be OR-ed (typically, quality flags) and some will be returned from one of the bricks (typically, orbits and light travel times). This is transparent to the user.

More information on each dataset and its methods can be found in mojito.reader.MojitoL1File and its attributes.

Time and frequency grids

The time and frequency grids (sampling) associated with each dataset can be accessed via the time_sampling and freq_sampling attributes.

These are instances of mojito.sampling.UniformTimeSampling and mojito.sampling.UniformFreqSampling, and provide methods to reconstruct the time and frequency arrays, as well as properties like the time step dt.

with MojitoL1File("path/to/file.h5") as f:

    tdi_time_sampling = f.tdis.time_sampling

    # Print time sampling information
    print(tdi_time_sampling)

    tdi_t0 = tdi_time_sampling.t0  # Start time in seconds
    tdi_dt = tdi_time_sampling.dt  # Time step in seconds
    tdi_size = tdi_time_sampling.size  # Number of samples
    tdi_duration = tdi_time_sampling.duration  # Duration in seconds
    tdi_fs = tdi_time_sampling.fs  # Sampling frequency in Hz

    # Generate time array in seconds
    tdi_times = tdi_time_sampling.t()

    # You can also start at zero (ignore t0)
    tdi_times_zero = tdi_time_sampling.t(elapsed=True)

Initial times t0 are expressed in seconds since the LISA epoch, as defined in lisaconstants.LISA_EPOCH_TCB.

You can also generate sliced time arrays by providing a slice object to the t() method. This allows for better memory management when working with large datasets.

# Generate time array for samples 1000 to 2000
tdi_times = tdi_time_sampling.t(slice(1000, 2000))

# Generate time array for every other sample
tdi_times = tdi_time_sampling.t(slice(None, None, 2))

More details on the time and frequency sampling classes can be found in Time and frequency grids.

Memory usage and slicing

All datasets in mojito.reader.MojitoL1File support lazy loading to minimize memory usage; therefore, only the requested data (sliced arrays) are loaded into memory.

Warning

You must slice the datasets to read the data into memory. Accessing a non-sliced dataset returns a lazy-loading object that cannot be used outside of the context manager (that is, outside of the with block).

For example, to read only the first 1000 samples of the TDI X2 observable, you can do:

with MojitoL1File("path/to/file.h5") as f:
    x2_first_1000 = f.tdis.x2[:1000]
    times_first_1000 = f.tdis.time_sampling.t(slice(0, 1000))

Note that some attributes, such as mojito.reader.TDI.xyz_doppler and mojito.reader.TDI.aet_doppler, perform stacking and rescaling of the underlying datasets. They have been designed to provide lazy loading and transparent slicing as well (including along the stacking dimension).

For example, to read only the first 1000 samples of the TDI XY observables in Doppler units, you can do:

with MojitoL1File("path/to/file.h5") as f:
    xy_doppler_first_1000 = f.tdis.xyz_doppler[:1000, :2]

Behind the scenes, Mojito uses h5py to read HDF5 files, and properties return h5py.Dataset objects that support slicing and lazy loading. Stacked datasets are handled by the mojito.lazy.ScaledStackedDataset class, which also supports slicing and lazy loading.

Stir the drink

Writing Mojito files

Mojito provides a writer to create Mojito L1 files from simulation outputs or by combining existing Mojito L1 files. The writer ensures that the created files are compliant with the Mojito L1 file specification.

To combine existing Mojito L1 files, you can use the mojito.writer.combine_bricks() function. This function can also check for consistency across the input files and raise an error if inconsistencies are found.

from mojito.writer import combine_bricks

combine_bricks(
    ["file1.h5", "file2.h5"],
    "combined_file.h5",
    check_consistent=True,
)

The module mojito.writer also provides functions to create empty Mojito L1 files and various groups, and write attributes. The class mojito.reader.MojitoL1File can also be used to write data to an existing Mojito L1 file.

from mojito import MojitoL1File
from mojito.sampling import UniformTimeSampling
from mojito.writer import write_attrs, create_tdis

# Create an empty Mojito L1 file
with MojitoL1File("path/to/new_file.h5", mode="a") as f:

    # Write attributes
    write_attrs(
        f,
        pipeline_name="my_pipeline",
        lolipops_version="1.0.0",
        laser_frequency=2.82e14,
    )

    # Create TDI group with time sampling
    tdi_time_sampling = UniformTimeSampling(t0=0.0, dt=2.0, size=10000)
    create_tdis(f, time_sampling=tdi_time_sampling)

    # Write TDI observables
    f.tdis.x2[:] = my_x2_data  # in Hz
    f.tdis.y2[:] = my_y2_data  # in Hz
    f.tdis.z2[:] = my_z2_data  # in Hz
    # etc.

You can copy entire groups from an existing Mojito L1 file to a new one by providign the data keyword argument to the group creation functions. For example, to copy the orbits and LTT groups from an existing file:

from mojito import MojitoL1File
from mojito.writer import create_orbits, create_ltts

with MojitoL1File("path/to/existing_file.h5", mode="r") as f_existing:
    with MojitoL1File("path/to/new_file.h5", mode="a") as f_new:

        # Copy orbits group
        create_orbits(
            f_new,
            time_sampling=f_existing.orbits.time_sampling,
            data=f_existing.orbits,
        )

        # Copy LTT group
        create_ltts(
            f_new,
            time_sampling=f_existing.ltts.time_sampling,
            data=f_existing.ltts,
        )

If datasets are missing from the existing file, they will be created as empty datasets in the new file.

More details on the writer and its functions can be found in stirring/writer.