Reflectometry Plugin Documentation

Overview

The Reflectometry Plugin extends VIPR for analyzing thin film structures using X-ray (XRR) and neutron reflectometry (NR) data. It determines layer properties such as thickness, roughness, and scattering length density (SLD) through three distinct model approaches: Reflectorch (deep learning), Flow Models (NSF/MAF/CINN normalizing flows), and PANPE (prior-amortized neural posterior estimation).

Application Domain: Inverse problem solving for reflectometry - predicting layer parameters from experimental reflectivity curves.

Plugin Architecture

Modular Structure

The plugin implements a three-submodule architecture with shared components:

vipr_reflectometry/
├── shared/              # Shared components (data loaders, preprocessing)
├── reflectorch/         # Deep learning models (EasyInferenceModel)
├── flow_models/         # Normalizing flow models (NSF, MAF, CINN)
└── panpe/               # Prior-Amortized Neural Posterior Estimation (optional)

Submodule Loading Pattern

Each submodule follows the VIPR plugin loading convention with a load(app) function:

# vipr_reflectometry/__init__.py
def load(app):
    """Load plugin with all submodules."""
    # Register environment defaults hook
    app.hook.register('INFERENCE_BEFORE_START_HOOK', setup_reflectometry_env_defaults)
    
    # Load shared components first (data loaders used by multiple submodules)
    from . import shared
    shared.load(app)
    
    # Load submodules
    from . import reflectorch
    reflectorch.load(app)
    
    from . import flow_models
    flow_models.load(app)
    
    # Optional submodule
    try:
        from . import panpe
        panpe.load(app)
    except ImportError:
        app.log.info("PANPE not installed (optional)")

Key Pattern:

  • Each submodule (including shared) has its own load(app) function

  • Clean separation of concerns - no cross-submodule handler registration

Handler Registration Strategy

Submodules register handlers specific to their model approach:

# reflectorch/load(app)
def load(app):
    # Register handlers
    app.handler.register(ReflectorchModelLoader)
    app.handler.register(ReflectorchPredictor)
    
    # Extend app with custom functionality
    app.extend('reflectorch', Reflectorch(app))
    app.extend('reflectorch_dc', ReflectorchDataCollector(app))

Pattern Used:

  • Handler registration via app.handler.register()

  • App extension via app.extend() for domain-specific utilities

  • Data collector pattern for UI visualization

Shared Components

Shared components have their own submodule with a load(app) function:

# shared/__init__.py
def load(app):
    """Load shared handlers used by multiple submodules."""
    # Data loaders
    from .data_loader import HDF5SpectraReaderDataLoader, CSVSpectraReaderDataLoader
    app.handler.register(HDF5SpectraReaderDataLoader)
    app.handler.register(CSVSpectraReaderDataLoader)
    
    # Shared controller
    from .controllers import ReflectometryController
    app.handler.register(ReflectometryController)

# flow_models/__init__.py
def load(app):
    """Load flow-specific handlers only."""
    # No shared handler registration here
    app.handler.register(FlowModelLoader)
    app.handler.register(FlowPredictor)

Pattern: Shared handlers are registered in one place (shared/__init__.py), avoiding duplication and clarifying ownership.

Framework Integration Points

1. Discovery Decorators

Handlers use @discover_* decorators for automatic parameter exposure (see Discovery Plugin for details):

@discover_data_loader('csv_spectrareader', {
    'data_path': {'type': 'str', 'required': True},
    'column_mapping': {'type': 'dict', 'required': False}
})
class CSVSpectraReaderDataLoader(DataLoaderHandler):
    pass

2. DataSet Transfer Object

All data loaders return the VIPR DataSet object:

return DataSet(
    x=q_values,           # (batch_size, n_points)
    y=intensities,        # (batch_size, n_points)
    dx=q_errors,          # Optional uncertainties
    dy=intensity_errors,  # Optional uncertainties
    metadata={'loader': 'csv_spectrareader', ...}
)

3. Filter System Integration

Preprocessing uses weighted filters in the inference pipeline:

@discover_filter(
    'INFERENCE_PREPROCESS_PRE_FILTER',
    weight=-10,  # Runs before Reflectorch interpolation (weight=0)
    parameters={...}
)
def clean_experimental_data(self, data: DataSet, **kwargs) -> DataSet:
    # Transform and return DataSet
    return cleaned_dataset

4. Hook System

Environment setup via hooks before inference starts:

def setup_reflectometry_env_defaults(app):
    """Set environment defaults before inference."""
    if 'REFLECTORCH_ROOT_DIR' not in os.environ:
        os.environ['REFLECTORCH_ROOT_DIR'] = str(Path.cwd() / 'storage' / 'reflectorch')

# Register in plugin loading
app.hook.register('INFERENCE_BEFORE_START_HOOK', setup_reflectometry_env_defaults)

Registered Components

Model Loaders

Handler Label

Submodule

Purpose

reflectorch

reflectorch

Load Reflectorch deep learning models

flow_model_loader

flow_models

Load normalizing flow models

panpe_model_loader

panpe

Load PANPE models (optional)

Data Loaders

Handler Label

Format

Batch Size

csv_spectrareader

CSV/DAT/TXT

1

hdf5_spectrareader

HDF5

N

Predictors

Handler Label

Submodule

Output Type

reflectorch_predictor

reflectorch

Layer parameters + SLD profiles

flow_predictor

flow_models

Posterior samples

panpe_predictor

panpe

Posterior samples (optional)

Filters

Filter

Weight

Purpose

NeutronDataCleaner.clean_experimental_data

-10

Remove negative/high-error points

Reflectorch._preprocess_interpolate

0

Interpolate to model grid

FlowPreprocessor._preprocess_flow

0

Prepare for flow models

Environment Configuration

Required Variables

export REFLECTORCH_ROOT_DIR=${PWD}/storage/reflectorch  # Required for model/config storage

The plugin automatically sets this if not defined, using ${PWD}/storage/reflectorch as default.

Directory Structure

storage/reflectorch/
├── configs/           # Model configuration files
├── saved_models/      # Trained model weights
└── cache/            # Downloaded models (Reflectorch)

Installation

Basic Installation

pip install git+https://codebase.helmholtz.cloud/vipr/vipr-core.git
pip install git+https://codebase.helmholtz.cloud/vipr/vipr-reflectometry-plugin.git

With PANPE Support

pip install "git+https://codebase.helmholtz.cloud/vipr/vipr-reflectometry-plugin.git[plugin-panpe]"

Local Development

cd vipr-reflectometry-plugin
pip install -e .

# With PANPE
pip install -e .[plugin-panpe]

Quick Start

CLI Usage

# Reflectorch XRR analysis
vipr --config '@vipr_reflectometry/reflectorch/examples/configs/PTCDI-C3.yaml' inference run

# Flow model inference
vipr --config '@vipr_reflectometry/flow_models/examples/configs/NSF_XRR.yaml' inference run

Configuration Pattern

vipr:
  inference:
    load_data:
      handler: csv_spectrareader
      parameters:
        data_path: '@vipr_reflectometry/reflectorch/examples/data/D17_SiO.dat'
    
    load_model:
      handler: reflectorch
      parameters:
        config_name: e_mc_point_neutron_conv_standard_L1_InputQDq_n256_size1024
    
    prediction:
      handler: reflectorch_predictor
      parameters:
        calc_pred_curve: true
        calc_pred_sld_profile: true

Documentation