torchref.refinement.targets.occupancy_floor_diagnostic module

Occupancy Floor Diagnostic for Time-Resolved Crystallography.

This module provides tools to estimate a lower bound on the activation fraction by analyzing electron density. The key insight is that negative electron density is unphysical - you cannot remove more electrons than were present.

For atoms that move in the excited state (e.g., waters, ligands): - The dark state has density ρ_dark at the original position - If the atom completely leaves, the light state has ρ_light ≈ 0 there - The observed difference is: Δρ = α × (ρ_light - ρ_dark) = -α × ρ_dark - The depth of the negative peak gives: α = |Δρ| / ρ_dark

If α is underestimated, the model must predict ρ_light < 0 to fit the data, which is unphysical. This provides a floor on α.

class torchref.refinement.targets.occupancy_floor_diagnostic.OccupancyFloorDiagnostic(model_dark, model_light, grid_spacing=0.5, negative_threshold=-0.5)[source]

Bases: object

Diagnostic tool to estimate activation fraction floor from electron density.

Analyzes the electron density of the light/refined model and checks for unphysical negative density, which indicates the activation fraction is too small.

Parameters:
  • model_dark (ModelFT) – The dark/ground state model.

  • model_light (ModelFT) – The light/excited state model (the refined one, not MixedModel).

  • grid_spacing (float, optional) – Grid spacing in Angstroms for density calculation. Default is 0.5.

  • negative_threshold (float, optional) – Threshold below which density is considered “significantly negative”. Default is -0.5 (in sigma units after normalization).

Examples

Basic usage:

diagnostic = OccupancyFloorDiagnostic(model_dark, model_light_refine)
result = diagnostic.analyze()
print(f"Estimated alpha floor: {result['alpha_floor']:.3f}")
__init__(model_dark, model_light, grid_spacing=0.5, negative_threshold=-0.5)[source]
compute_density_at_positions(model, positions, hkl)[source]

Compute electron density at specific positions using Fourier summation.

This is a simplified calculation that sums F_calc * exp(2πi * h·r).

Parameters:
  • model (ModelFT) – Model to compute density from.

  • positions (torch.Tensor) – Positions in fractional coordinates, shape (N, 3).

  • hkl (torch.Tensor) – Miller indices, shape (M, 3).

Returns:

Electron density values at each position, shape (N,).

Return type:

torch.Tensor

analyze_at_dark_positions(hkl, atom_mask=None)[source]

Analyze light model density at dark atom positions.

Parameters:
  • hkl (torch.Tensor) – Miller indices for Fourier calculation.

  • atom_mask (torch.Tensor, optional) – Boolean mask selecting which atoms to analyze (e.g., waters only).

Returns:

Dictionary with analysis results including: - ‘rho_dark’: Dark model density at atom positions - ‘rho_light’: Light model density at atom positions - ‘rho_ratio’: ρ_light / ρ_dark (should be ≥ 0) - ‘negative_mask’: Boolean mask of atoms with negative light density - ‘alpha_floor’: Estimated lower bound on activation fraction - ‘worst_atoms’: Indices of atoms with most negative density

Return type:

dict

estimate_alpha_floor_from_difference_map(hkl, delta_F_obs, sigma_diff, n_peaks=10, sigma_cutoff=3.0)[source]

Estimate alpha floor from significant negative peaks in difference map.

For each significant negative peak in the difference map, estimate the minimum α that could produce that peak without requiring negative density in the light state.

Parameters:
  • hkl (torch.Tensor) – Miller indices.

  • delta_F_obs (torch.Tensor) – Observed difference amplitudes (can be negative).

  • sigma_diff (torch.Tensor) – Uncertainties on difference amplitudes.

  • n_peaks (int, optional) – Number of peaks to analyze. Default is 10.

  • sigma_cutoff (float, optional) – Minimum significance (|ΔF|/σ) for peaks. Default is 3.0.

Returns:

Dictionary with alpha floor estimates.

Return type:

dict

class torchref.refinement.targets.occupancy_floor_diagnostic.NegativeDensityPenalty(mixed_model, model_dark, hkl, atom_mask=None, check_grid=False)[source]

Bases: DeviceMixin, Module

Loss term that penalizes negative electron density in the MIXED model.

This provides a soft constraint that prevents the activation fraction from being too small (which would require unphysical negative density).

The key insight: the MIXED state (not pure light) should have non-negative density everywhere. If α is too small and atoms have moved, the mixed model might predict negative density at some positions, which is unphysical.

Parameters:
  • mixed_model (MixedModel) – The mixed model (combines dark and light states with fractions).

  • model_dark (ModelFT) – The dark/ground state model (provides reference positions to check).

  • hkl (torch.Tensor) – Miller indices for density calculation.

  • atom_mask (torch.Tensor, optional) – Mask selecting which atoms to monitor.

  • check_grid (bool, optional) – If True, also check density on a grid (more thorough but slower). Default is False.

__init__(mixed_model, model_dark, hkl, atom_mask=None, check_grid=False)[source]
forward()[source]

Compute penalty for negative density in mixed model.

Returns:

Scalar penalty value (0 if no negative density).

Return type:

torch.Tensor

class torchref.refinement.targets.occupancy_floor_diagnostic.DisplacementRegularizer(model_light, model_dark, atom_mask=None, max_displacement=2.0)[source]

Bases: DeviceMixin, Module

Regularizer that penalizes large atomic displacements from reference structure.

This directly breaks the α-δF degeneracy by favoring solutions where atoms haven’t moved too far from the dark structure, which implies larger α.

The loss is: mean((xyz_light - xyz_dark)²)

Parameters:
  • model_light (ModelFT) – The light model being refined.

  • model_dark (ModelFT) – The dark reference model (frozen).

  • atom_mask (torch.Tensor, optional) – Boolean mask selecting which atoms to include.

  • max_displacement (float, optional) – Maximum allowed displacement in Angstroms. Displacements beyond this are penalized quadratically. Default is 2.0 Å.

__init__(model_light, model_dark, atom_mask=None, max_displacement=2.0)[source]
forward()[source]

Compute displacement penalty.

Returns:

Mean squared displacement penalty.

Return type:

torch.Tensor

class torchref.refinement.targets.occupancy_floor_diagnostic.DifferenceAmplitudeRegularizer(dataset_collection, mixed_model, model_dark)[source]

Bases: DeviceMixin, Module

Regularizer that encourages consistency between α and difference amplitudes.

The key insight: the ratio of calculated to observed difference amplitudes should be consistent. If α is too small, the model compensates by making larger structural changes, which changes this ratio in a detectable way.

This regularizer penalizes deviations from the expected relationship:

|ΔF_calc||ΔF_obs|

When α is correct and the structure is correct, these should match. When α is too small and structure has moved too far, the pattern of |ΔF_calc| vs |ΔF_obs| will be distorted.

Parameters:
  • dataset_collection (DatasetCollection) – Collection with ‘dark’ and ‘light’ datasets.

  • mixed_model (MixedModel) – The mixed model being refined.

  • model_dark (ModelFT) – The dark reference model.

__init__(dataset_collection, mixed_model, model_dark)[source]
property hkl
forward()[source]

Compute regularization loss.

Penalizes the variance in the ratio |ΔF_calc|/|ΔF_obs|. If α and structure are correct, this ratio should be ~1 everywhere. If α is wrong, this ratio will have high variance.