Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Contributing to xmris

Welcome! xmris is built on a strict “xarray in, xarray out” philosophy. Our goal is to make MR imaging and spectroscopy processing functional, robust, and N-dimensional.

To keep the codebase clean and the documentation pristine, we follow a modern “docs-as-tests” pipeline using MyST, quartodoc, and uv.


Step 1: Write the Standalone Function

Functions live in specific, nested domain modules (e.g., xmris.processing.fid, xmris.vendor.bruker). Respect the physical domain: if an operation is a frequency-domain manipulation, it belongs in xmris.processing.phase or fourier, not mixed into time-domain FID scripts.

  1. Xarray First: The first argument must be da: xr.DataArray. Always return a new xr.DataArray.

  2. Use the DEFAULTS Config: Never hardcode dimension or attribute names. Default your signature arguments to None and fall back to the global DEFAULTS object from xmris.config.

  3. Data Lineage (Crucial): Always pass along existing coordinates and attributes. Append new processing parameters to the .attrs dictionary to preserve the processing history.

  4. Type Hinting & Docstrings: Fully type-hint signatures and use standard NumPy format docstrings. These are the source of truth for our online API docs generated by quartodoc.

from xmris.config import DEFAULTS

def my_func(da: xr.DataArray, dim: str | None = None, my_param: float = 1.0) -> xr.DataArray:
    """Description for the API docs."""
    
    # 0. Fallback to global config
    dim = dim or DEFAULTS.time.dim
    
    # ... mathematical operations ...
    
    # 1. Update data
    da_new = da.copy(data=new_vals)
    
    # 2. Preserve lineage by logging new parameters
    new_attrs = da.attrs.copy()
    new_attrs.update({"my_param_used": my_param})
    
    return da_new.assign_attrs(new_attrs)

Step 2: Register the Accessor

Expose your function through the .xmr namespace in src/xmris/accessor.py. This allows users to chain methods cleanly: da.xmr.fft().xmr.my_func(). Ensure the method signature matches your standalone function exactly.


Step 3: Create the Tutorial-Test (Jupytext)

We don’t use standard test_*.py files. Your tutorials are the test suite. Create a Python script in notebooks/ using the Jupytext percent format (# %%).

  1. Explain & Demonstrate: Explain the math/physics and show the function in action with matplotlib.

  2. Verify Math & Metadata: Use assert statements to prove the math is correct, but also assert that dimensions, coordinates, and attributes were preserved.

  3. Hide Tests: Add # %% tags=["remove-cell"] to assertion cells. This ensures pytest --nbmake checks them during CI/CD, but the generated website stays clean for readers.


Step 4: Update the Build Pipeline

  1. API Docs: Run uv run docs-api. This triggers quartodoc to scrape your new function’s docstring into the API Reference.

  2. Navigation: Add your new tutorial to the nav section in myst.yml.

  3. Verify Build: Run uv run docs to ensure the site renders correctly.

Step 5: Tests

  1. Run tests: Run uv run pytest to run all notebooks in testmode using nbmake.