homodyne.core.homodyne_model

The homodyne_model module provides HomodyneModel — the unified model facade used by both NLSQ and CMC optimization backends. It wraps configuration parsing, physics factor pre-computation, and time grid construction into a single stateful object that feeds into compute_g2_scaled_with_factors().

Note

HomodyneModel is the recommended entry point for computing correlation matrices from a YAML configuration. It validates the config once during __init__, pre-computes physics factors (q²dt/2, qLdt/2π), and builds time grids — so repeated calls to compute_c2() skip all setup overhead.


HomodyneModel

class homodyne.core.homodyne_model.HomodyneModel[source]

Bases: object

Hybrid architecture wrapper for homodyne XPCS analysis.

This class combines the robustness of stateful object-oriented design with the performance of functional JAX programming. It:

  1. Stores configuration (dt, q, L) as instance state

  2. Pre-computes physics factors once at initialization

  3. Provides high-level methods that use stored state

  4. Calls JIT-compiled functional cores for performance

Benefits

  • Robustness: Configuration validated once at initialization

  • Performance: Physics factors pre-computed, JIT-compiled cores

  • Usability: Simple API, no dt parameter passing needed

  • Safety: No dt estimation errors possible

  • Efficiency: Factors computed once, reused for all calculations

physics_factors

Pre-computed physics factors (q²dt/2, qLdt/2π)

Type:

PhysicsFactors

time_array

Time array for correlation calculations [s]

Type:

jnp.ndarray

t1_grid, t2_grid

2D time grids for correlation matrices

Type:

jnp.ndarray

model

Underlying physics model (for backward compatibility)

Type:

homodyne.core.models.CombinedModel

dt

Time step [s]

Type:

float

wavevector_q

Scattering wave vector magnitude [Å⁻¹]

Type:

float

stator_rotor_gap

Sample-detector distance [Å]

Type:

float

analysis_mode

Analysis mode (“static”, “laminar_flow”)

Type:

str

Examples

Basic usage:

>>> model = HomodyneModel(config)
>>> c2 = model.compute_c2(params, phi_angles)

With plotting:

>>> model.plot_simulated_data(params, phi_angles, output_dir="./results")

Access configuration:

>>> print(model.config_summary)
>>> print(f"dt = {model.dt} s")
>>> print(f"Pre-computed factors: {model.physics_factors}")
__init__(config)[source]

Initialize HomodyneModel from configuration dictionary.

Parameters:

config (dict) –

Homodyne configuration dictionary with structure:

{
    'analyzer_parameters': {
        'temporal': {'dt': float, 'start_frame': int, 'end_frame': int},
        'scattering': {'wavevector_q': float},
        'geometry': {'stator_rotor_gap': float}
    },
    'analysis_settings': {...}  # Optional
}

Raises:
  • KeyError – If required configuration keys are missing

  • ValueError – If configuration values are invalid

compute_c2(params, phi_angles, contrast=0.5, offset=1.0)[source]

Compute C2 correlation function using stored configuration.

This high-level method: - Uses pre-computed time grids (self.t1_grid, self.t2_grid) - Uses pre-computed physics factors (self.physics_factors) - Calls JIT-compiled functional core for performance - Returns C2 for all phi angles

NO dt parameter needed - uses stored configuration!

Parameters:
  • params (ndarray) – Physical parameters: - For laminar_flow (7 params): [D0, alpha, D_offset, gamma_dot_t0, beta, gamma_dot_t_offset, phi0] - For static (3 params): [D0, alpha, D_offset]

  • phi_angles (ndarray) – Scattering angles [degrees], shape (n_phi,)

  • contrast (float) – Contrast parameter (β in literature)

  • offset (float) – Baseline offset

Returns:

C2 correlation matrices, shape (n_phi, n_time, n_time)

Return type:

ndarray

Examples

>>> model = HomodyneModel(config)
>>> params = np.array([100.0, 0.0, 10.0, 1e-4, 0.0, 0.0, 0.0])
>>> phi_angles = np.array([0, 30, 45, 60, 90])
>>> c2 = model.compute_c2(params, phi_angles)
>>> print(c2.shape)  # (5, 100, 100) for 5 angles, 100 time points
compute_c2_single_angle(params, phi, contrast=0.5, offset=1.0)[source]

Compute C2 correlation function for a single angle.

Convenience method for single-angle calculations.

Parameters:
  • params (ndarray) – Physical parameters

  • phi (float) – Scattering angle [degrees]

  • contrast (float) – Contrast parameter

  • offset (float) – Baseline offset

Returns:

C2 correlation matrix, shape (n_time, n_time)

Return type:

ndarray

plot_simulated_data(params, phi_angles, output_dir='./simulated_data', contrast=0.5, offset=1.0, generate_plots=True)[source]

Generate and optionally plot simulated C2 data.

This convenience method: 1. Computes C2 using stored configuration 2. Optionally generates heatmap plots for each angle 3. Saves data to NumPy file 4. Returns both data and output path

Parameters:
  • params (ndarray) – Physical parameters

  • phi_angles (ndarray) – Scattering angles [degrees]

  • output_dir (str) – Output directory for plots and data

  • contrast (float) – Contrast parameter

  • offset (float) – Baseline offset

  • generate_plots (bool) – Whether to generate heatmap plots

Returns:

(c2_data, output_path) - c2_data: Computed correlation matrices - output_path: Path to saved data file

Return type:

tuple[ndarray, Path]

Examples

>>> model = HomodyneModel(config)
>>> c2_data, output_path = model.plot_simulated_data(
...     params, phi_angles, output_dir="./results"
... )
>>> print(f"Data saved to: {output_path}")
property config_summary: dict

Get configuration summary for logging/debugging.

Returns:

Configuration summary with all key parameters

Return type:

dict

__repr__()[source]

String representation of HomodyneModel.

Return type:

str


Architecture

HomodyneModel sits between configuration and optimization:

YAML → ConfigManager → HomodyneModel → NLSQ / CMC
                          │
                          ├── physics_factors (pre-computed q²dt/2, qLdt/2π)
                          ├── t1_grid, t2_grid (2-D time meshgrids)
                          └── compute_c2() → compute_g2_scaled_with_factors()

The model delegates actual physics to the JIT-compiled functions in homodyne.core.theory and jax_backend, ensuring that the hot path (Jacobian evaluation in NLSQ, leapfrog steps in NUTS) never re-parses configuration or recomputes static factors.


Key Attributes

Attribute

Type

Description

physics_factors

PhysicsFactors

Pre-computed factors (q²dt/2, qLdt/2π)

time_array

jnp.ndarray

1-D time grid [0 dt*(n-1)] in seconds

t1_grid, t2_grid

jnp.ndarray

2-D correlation time grids (shape n_time × n_time)

dt

float

Time step [s] from config

wavevector_q

float

Scattering wave-vector magnitude [Å⁻¹]

stator_rotor_gap

float

Sample-detector gap [Å]

analysis_mode

str

"static" or "laminar_flow"


Usage Examples

Computing correlation matrices

from homodyne.core.homodyne_model import HomodyneModel
import numpy as np

config = {
    "analysis_mode": "laminar_flow",
    "analyzer_parameters": {
        "temporal": {"dt": 0.001, "start_frame": 0, "end_frame": 99},
        "scattering": {"wavevector_q": 0.01},
        "geometry": {"stator_rotor_gap": 2_000_000.0},
    },
}

model = HomodyneModel(config)

# 7-parameter laminar_flow vector
params = np.array([19231.0, 1.5, 0.1, 0.003, 0.8, 0.0, 0.0])
phi_angles = np.array([0.0, np.pi / 4, np.pi / 2])

c2 = model.compute_c2(params, phi_angles, contrast=0.5, offset=1.0)
print(f"C2 shape: {c2.shape}")  # (3, 100, 100)

Generating simulated data

c2_data, output_path = model.plot_simulated_data(
    params, phi_angles, output_dir="./simulated"
)
# Saves compressed .npz and per-angle heatmap PNGs