Scattering API

This page lists the pySED-native scattering extension APIs. See Scattering Extensions for the theory, q-point rules, and implementation rationale.

Shared Kernel

Shared low-level kernels for pySED scattering calculations.

The functions here are deliberately small CPU/NumPy kernels. They centralize the operations that will later be natural targets for optional CuPy or Numba-CUDA implementations: phase construction, density amplitudes, time FFT power spectra, and block statistics.

class pySED.scattering_kernel.FrequencyGrid(frequencies_thz: numpy.ndarray, freq_slice: slice)

Bases: object

freq_slice: slice
frequencies_thz: ndarray
class pySED.scattering_kernel.KernelBackend(name: str, xp: object, fft: object, fftfreq: object, asnumpy: object)

Bases: object

asnumpy: object
fft: object
fftfreq: object
name: str
xp: object
pySED.scattering_kernel.as_blocks(n_frames, num_blocks)

Split a trajectory length into equally sized contiguous blocks.

pySED.scattering_kernel.circular_autocorrelation(signal, backend='cpu')

Return circular autocorrelation used for finite-block reference DSF.

pySED.scattering_kernel.density_amplitude(positions_block, q_vec, weights=None, backend='cpu')

Return weighted density amplitude rho(Q,t) for one block.

pySED.scattering_kernel.density_amplitude_from_phase(phase, weights=None, backend='cpu')

Return weighted density amplitude from precomputed phase factors.

pySED.scattering_kernel.fft_power(signal, axis=0, norm='forward', backend='cpu')

Return abs(FFT(signal))**2 with the configured FFT normalization.

pySED.scattering_kernel.frequency_grid(block_size, dt, positive_only=True, backend='cpu')

Return THz frequencies and the slice used for spectral outputs.

pySED.scattering_kernel.phase_factors(positions_block, q_vec, backend='cpu')

Return exp(i Q dot r_i(t)) for one trajectory block and Q vector.

pySED.scattering_kernel.positive_frequency_slice(num_frames)

Return the positive-frequency half used by pySED spectra.

pySED.scattering_kernel.resolve_backend(backend='cpu')

Resolve a scattering kernel backend.

"cpu" and "numpy" use NumPy/SciPy and are always available. "cupy" and "gpu" request CuPy/CuPyX. CuPy is optional and must be installed by the user with the wheel that matches their CUDA runtime.

pySED.scattering_kernel.standard_error(samples, backend='cpu')

Return the standard error over the first axis.

pySED.scattering_kernel.to_numpy(array, backend='cpu')

Move a backend array to a NumPy array.

Probe Weights

Probe-specific scattering weights.

This module keeps scattering constants separate from the DSF estimators. The X-ray path uses the Cromer-Mann analytic approximation for the neutral-atom form factor,

f0(s) = sum_i a_i exp(-b_i s**2) + c, s = abs(Q) / (4*pi).

Q is expected in inverse Angstrom.

class pySED.probe_weights.CromerMannCoefficients(a: tuple, b: tuple, c: float, source: str = 'Cromer and Mann neutral-atom analytic form')

Bases: object

Four-Gaussian Cromer-Mann coefficients for an atomic form factor.

a: tuple
b: tuple
c: float
source: str = 'Cromer and Mann neutral-atom analytic form'
pySED.probe_weights.atomic_number_weights(atom_types, qpoints_cartesian=None, missing='raise')

Return atomic-number X-ray weights.

This is a fallback for elements without form-factor coefficients. It is useful for exploratory calculations but should not be treated as an experiment-quality X-ray model.

pySED.probe_weights.coerce_cromer_mann_coefficients(value)

Return value as CromerMannCoefficients.

value can be a CromerMannCoefficients instance, a mapping with a, b, and c keys, or a flat nine-value iterable ordered as a1, b1, a2, b2, a3, b3, a4, b4, c.

pySED.probe_weights.cromer_mann_form_factor(q_magnitude, coefficients)

Evaluate the Cromer-Mann form factor for |Q| in inverse Angstrom.

pySED.probe_weights.electron_form_factor_weights(atom_types, qpoints_cartesian, coefficients_table=None, missing='raise')

Return relative electron form-factor weights using Mott-Bethe.

This is a neutral-atom, first-Born approximation suitable for kinematic EELS visibility estimates. For quantitative electron diffraction or multislice calculations, users should pass a dedicated electron-scattering table through the higher-level EELS API.

pySED.probe_weights.mott_bethe_electron_form_factor(q_magnitude, atomic_number, coefficients, small_q=1e-08)

Return a relative electron scattering factor from Mott-Bethe.

The returned value is proportional to (Z - f_x(Q)) / |Q|**2 with Q in inverse Angstrom. The dimensional constant is omitted because pySED’s kinematic EELS visibility uses relative intensities by default. The Q -> 0 limit is evaluated analytically from the Cromer-Mann derivative.

pySED.probe_weights.xray_form_factor_weights(atom_types, qpoints_cartesian=None, coefficients_table=None, missing='atomic_number')

Return q-dependent X-ray form-factor weights.

Parameters

atom_types

Element symbols for each atom.

qpoints_cartesian

Q points in inverse Angstrom. If omitted, Q=0 weights are returned as a one-dimensional atom array.

coefficients_table

Optional mapping from atom symbol to Cromer-Mann coefficients. Entries override the built-in neutral-atom table.

missing

"atomic_number" falls back to atomic_number_weights(), "zero" inserts zero weights, and "raise" raises KeyError.

Quantum Correction

Quantum detailed-balance corrections for classical scattering spectra.

pySED.quantum_correction.BOLTZMANN_MEV_PER_K = 0.08617333262145

Boltzmann constant expressed as meV/K.

pySED.quantum_correction.PLANCK_MEV_THZ = 4.135667696

Planck constant expressed as meV per THz.

pySED.quantum_correction.WAVENUMBER_MEV = 0.1239841984332003

Energy of one inverse centimeter expressed in meV.

pySED.quantum_correction.apply_quantum_correction(intensity, axis, temperature, unit='thz', axis_index=-1)

Multiply a spectrum or map by the harmonic quantum correction factor.

pySED.quantum_correction.bose_population(axis, temperature, unit='thz')

Return the Bose-Einstein population for positive energy magnitudes.

pySED.quantum_correction.classical_to_quantum_factor(axis, temperature, unit='thz')

Return the harmonic classical-to-quantum correction factor.

The factor is

x / (1 - exp(-x)), where x = E / (k_B T).

For negative energy transfers the same signed expression obeys detailed balance. The zero-energy limit is evaluated with the Taylor expansion.

pySED.quantum_correction.convert_energy_axis(axis, source_unit='thz', target_unit='meV')

Convert an axis between supported frequency/energy units.

pySED.quantum_correction.energy_axis_from_mev(energy_mev, unit='thz')

Convert signed meV values to the requested unit.

pySED.quantum_correction.energy_axis_to_mev(axis, unit='thz')

Convert an energy/frequency axis to signed meV values.

Q-Point Advisor

Q-point utilities for finite periodic MD trajectories.

The routines in this module make the supercell commensurability condition explicit. For a primitive cell p and a simulation supercell S with S = P @ p, a reduced q point is compatible with the trajectory if

q_red @ P.T is an integer vector.

This is the same condition used by the existing Brillouin-zone path builder, but exposed as reusable API for SED, DSF, EELS, and experiment comparison.

class pySED.q_advisor.ExtendedZoneQPathPlan(report: QAdvisorReport, Q_reduced: ndarray, Q_cartesian: ndarray, qpoints_reduced: ndarray, qpoints_cartesian: ndarray, g_vectors_reduced: ndarray, g_vectors_cartesian: ndarray)

Bases: object

Pairwise Q = q + G plan for extended-zone scattering paths.

Q_cartesian: ndarray
Q_reduced: ndarray
g_vectors_cartesian: ndarray
g_vectors_reduced: ndarray
property num_points
qpoints_cartesian: ndarray
qpoints_reduced: ndarray
report: QAdvisorReport
to_dict()
to_records(flat=False)
write_csv(path)

Write the extended-zone path plan to path as CSV.

write_phonopy_qpoints(path)

Write folded commensurate q points for phonopy eigenvectors.

class pySED.q_advisor.QAdvisor(primitive_cell, supercell_cell, atol=1e-08)

Bases: object

Construct, validate, and map q points for a finite MD supercell.

advise_cartesian_qpoints(qpoints_cartesian, preserve_image=False)

Map Cartesian experimental Q points to nearest commensurate q points.

advise_experimental_path(qpoints, coordinates='reduced', preserve_image=True, suggest_supercell=True)

Return a full commensurability report for an experimental q path.

Parameters

qpoints

Requested q or Q points, either reduced coordinates in the primitive reciprocal basis or Cartesian vectors in inverse Angstrom.

coordinates

"reduced"/"fractional"/"crystal" or "cartesian"/"angstrom^-1".

preserve_image

Preserve the extended-zone reciprocal-lattice image when mapping to the nearest commensurate point. This should normally stay True for EELS, INS, and IXS comparison because form factors and basis interference depend on the full Q = q + G.

suggest_supercell

If True, recommend diagonal primitive-cell repeats that would make all requested reduced coordinates commensurate.

advise_map_q_axis(scattering_map, start_qpoint, end_qpoint, coordinates='cartesian', preserve_image=True, suggest_supercell=True)

Advise Q points implied by a calibrated experimental map q axis.

scattering_map may be any object with a one-dimensional q_axis attribute, including pySED.compare_experiment.ScatteringMap. The vector endpoints define the reciprocal-space path corresponding to the first and last q-axis values.

advise_qpoints(qpoints_reduced, preserve_image=False)
build_commensurate_path(start_reduced, end_reduced)
cartesian_to_reduced(qpoints_cartesian)
commensurate_grid(centered=False, max_bound=256)

Return all distinct commensurate q points modulo primitive G vectors.

For diagonal supercells this is the usual i/Nx, j/Ny, k/Nz grid. For non-diagonal integer repetition matrices, a bounded integer enumeration is used and stopped once abs(det(P)) unique q points are found.

estimate_selected_q_efficiency(qpoints_reduced=None, num_selected_qpoints=None, num_frames=None, num_atoms=None)

Estimate selected-Q savings against this supercell’s full q grid.

is_commensurate(qpoint_reduced, atol=None)
nearest_commensurate(qpoint_reduced, grid=None, preserve_image=False)
nearest_commensurate_cartesian(qpoint_cartesian, grid=None, preserve_image=False)

Map one Cartesian experimental Q point to the nearest allowed q point.

plan_extended_zone_q_path(qpoints, coordinates='reduced', q_policy='strict', max_error_reduced=None, max_error_cartesian=None)

Plan a pairwise extended-zone path for EELS, INS, or IXS.

The returned ExtendedZoneQPathPlan contains the nearest allowed extended-zone \(Q_i\), the folded commensurate \(q_i\), and the integer reciprocal vector \(G_i\) for each experimental point:

Q_i = q_i + G_i.

plan_map_q_axis(scattering_map, start_qpoint, end_qpoint, coordinates='cartesian', q_policy='strict', max_error_reduced=None, max_error_cartesian=None)

Plan Q=q+G points from an experimental map’s calibrated q axis.

reduced_to_cartesian(qpoints_reduced)
supercell_indices(qpoints_reduced)
validate_qpoints(qpoints_reduced, atol=None)
write_phonopy_qpoints(qpoints_reduced, path)
class pySED.q_advisor.QAdvisorReport(coordinate_system: str, preserve_image: bool, requested_reduced: ndarray, nearest_reduced: ndarray, requested_cartesian: ndarray, nearest_cartesian: ndarray, is_commensurate: ndarray, error_reduced: ndarray, error_cartesian: ndarray, integer_supercell_indices: ndarray, suggested_diagonal_supercell: ndarray)

Bases: object

Vectorized report for an experimental q or Q path.

The report keeps the requested points, the nearest commensurate points, the finite-supercell integer indices, and a diagonal-supercell recommendation in one object so scripts can print, export, or archive the q-selection decision before running SED, DSF, or EELS calculations.

property all_commensurate
coordinate_system: str
error_cartesian: ndarray
error_reduced: ndarray
integer_supercell_indices: ndarray
is_commensurate: ndarray
property max_error_cartesian
property max_error_reduced
nearest_cartesian: ndarray
nearest_reduced: ndarray
property num_commensurate
property num_points
preserve_image: bool
requested_cartesian: ndarray
requested_reduced: ndarray
suggested_diagonal_supercell: ndarray
summary()

Return scalar report metadata useful for logs or JSON exports.

to_dict()

Return a JSON-serializable report dictionary.

to_records(flat=False)

Return one serializable record per requested point.

to_table(precision=6)

Return a compact plain-text table for terminal logs.

write_csv(path)

Write the q-advisor point table to path as CSV.

class pySED.q_advisor.QPathSegments(qpoints_reduced: ndarray, path_axis: ndarray, segment_start_indices: ndarray, segment_end_indices: ndarray)

Bases: object

Selected-Q path assembled from one or more linear segments.

path_axis: ndarray
qpoints_reduced: ndarray
segment_end_indices: ndarray
segment_start_indices: ndarray
class pySED.q_advisor.QPointAdvice(requested_reduced: ndarray, nearest_reduced: ndarray, requested_cartesian: ndarray, nearest_cartesian: ndarray, is_commensurate: bool, error_reduced: float, error_cartesian: float, integer_supercell_index: ndarray)

Bases: object

Result of mapping one requested q point to the commensurate grid.

error_cartesian: float
error_reduced: float
integer_supercell_index: ndarray
is_commensurate: bool
nearest_cartesian: ndarray
nearest_reduced: ndarray
requested_cartesian: ndarray
requested_reduced: ndarray
class pySED.q_advisor.QPointMesh(qpoints_reduced: ndarray, axes: tuple, mesh_shape: tuple)

Bases: object

Selected-Q mesh in reduced reciprocal coordinates.

axes: tuple
mesh_shape: tuple
qpoints_reduced: ndarray
reshape_values(values)

Reshape per-Q values back onto the selected-Q mesh.

class pySED.q_advisor.SelectedQEfficiencyReport(num_selected_qpoints: int, num_full_qpoints: int, selected_fraction: float, qpoint_reduction_factor: float, num_frames: int = None, num_atoms: int = None, selected_phase_evaluations: float = None, full_phase_evaluations: float = None, selected_fft_work_units: float = None, full_fft_work_units: float = None)

Bases: object

Cost estimate for selected-Q evaluation relative to a full q grid.

full_fft_work_units: float = None
full_phase_evaluations: float = None
num_atoms: int = None
num_frames: int = None
num_full_qpoints: int
num_selected_qpoints: int
qpoint_reduction_factor: float
selected_fft_work_units: float = None
selected_fraction: float
selected_phase_evaluations: float = None
summary()

Return a JSON-serializable cost summary.

to_table(precision=6)

Return a compact plain-text table for logs or methods notes.

pySED.q_advisor.compute_repetition_matrix(primitive_cell, supercell_cell, atol=0.0001)

Return integer P such that supercell_cell = P @ primitive_cell.

pySED.q_advisor.estimate_selected_q_efficiency(num_selected_qpoints, num_full_qpoints, num_frames=None, num_atoms=None)

Estimate selected-Q savings relative to evaluating a full q grid.

The estimate follows the direct DSF cost model O(N_Q * N_t * N) + O(N_Q * N_t * log(N_t)). The phase-evaluation counts are exact under this model when num_frames and num_atoms are supplied; FFT work units are proportional estimates.

pySED.q_advisor.qpoints_from_mesh(h_axis, k_axis, l_axis)

Generate a pynamic-style selected-Q mesh in reduced coordinates.

Each axis may be a scalar or [start, stop, num]. The function does not enforce commensurability; pass the returned qpoints_reduced to QAdvisor before using the points with finite periodic trajectories.

pySED.q_advisor.qpoints_from_path_axis(q_axis, start_qpoint, end_qpoint)

Interpolate vector Q points along a calibrated scalar q-path axis.

Experimental maps often store only a scalar horizontal axis, for example a path distance or detector coordinate after calibration. This helper maps each scalar axis value linearly between two vector endpoints. The returned vectors use the same coordinate system as start_qpoint and end_qpoint.

pySED.q_advisor.qpoints_from_path_segments(starts, ends, steps, endpoint=False)

Generate selected Q points along one or more reduced-coordinate paths.

This follows the selected-path convention used by pynamic-structure-factor: each segment is sampled with steps points; by default the segment end point is omitted to avoid duplicate vertices when consecutive segments share endpoints. Use endpoint=True when every segment should include its final point.

pySED.q_advisor.reciprocal_lattice(cell)

Return reciprocal lattice vectors as rows, including the 2*pi factor.

pySED.q_advisor.reduced_delta(q1, q2)

Shortest periodic difference between reduced reciprocal coordinates.

pySED.q_advisor.split_extended_zone_qpoints(qpoints_reduced)

Split reduced extended-zone Q points into folded q and G.

The folded q points use the centered first Brillouin-zone image. The returned G vectors are integer reduced reciprocal-lattice vectors satisfying Q = q + G within floating-point tolerance.

pySED.q_advisor.suggest_diagonal_supercell(qpoints_reduced, max_denominator=96)

Suggest diagonal repeats so every supplied reduced q point is allowed.

pySED.q_advisor.wrap_reduced(q_red, centered=False)

Wrap reduced coordinates into [0, 1) or [-0.5, 0.5).

Dynamic Structure Factor

Dynamic structure factor calculations from MD trajectories.

This module implements the density-correlation route used for neutron and X-ray inelastic scattering. The computation is organized in terms of

rho(Q, t) = sum_i w_i(Q) exp(i Q dot r_i(t))

and estimates the coherent spectrum from the time Fourier transform of rho. This is equivalent to evaluating the intermediate scattering function and then Fourier transforming it, but avoids storing F(Q, t) explicitly.

class pySED.dsf.CurrentCorrelationResult(qpoints_cartesian: numpy.ndarray, frequencies_thz: numpy.ndarray, longitudinal: numpy.ndarray, transverse: numpy.ndarray, num_blocks: int, longitudinal_sem: numpy.ndarray = None, transverse_sem: numpy.ndarray = None, metadata: dict = None)

Bases: object

frequencies_thz: ndarray
longitudinal: ndarray
longitudinal_sem: ndarray = None
metadata: dict = None
num_blocks: int
qpoints_cartesian: ndarray
transverse: ndarray
transverse_sem: ndarray = None
class pySED.dsf.DSFResult(qpoints_cartesian: numpy.ndarray, frequencies_thz: numpy.ndarray, coherent: numpy.ndarray, incoherent: numpy.ndarray, total: numpy.ndarray, elastic_coherent: numpy.ndarray, elastic_incoherent: numpy.ndarray, num_blocks: int, metadata: dict, coherent_sem: numpy.ndarray = None, incoherent_sem: numpy.ndarray = None, total_sem: numpy.ndarray = None, elastic_coherent_sem: numpy.ndarray = None, elastic_incoherent_sem: numpy.ndarray = None)

Bases: object

coherent: ndarray
coherent_sem: ndarray = None
elastic_coherent: ndarray
elastic_coherent_sem: ndarray = None
elastic_incoherent: ndarray
elastic_incoherent_sem: ndarray = None
frequencies_thz: ndarray
incoherent: ndarray
incoherent_sem: ndarray = None
metadata: dict
num_blocks: int
qpoints_cartesian: ndarray
total: ndarray
total_sem: ndarray = None
class pySED.dsf.PartialDSFResult(qpoints_cartesian: numpy.ndarray, frequencies_thz: numpy.ndarray, species: tuple, partial: numpy.ndarray, partial_sem: numpy.ndarray, weighted_total: numpy.ndarray, weighted_total_sem: numpy.ndarray, num_blocks: int, metadata: dict)

Bases: object

frequencies_thz: ndarray
metadata: dict
num_blocks: int
partial: ndarray
partial_sem: ndarray
qpoints_cartesian: ndarray
species: tuple
weighted_total: ndarray
weighted_total_sem: ndarray
pySED.dsf.compute_coherent_dsf_via_correlation(positions, qpoints_cartesian, dt, coherent_weights=None, num_blocks=1, positive_only=True, backend='cpu')

Reference coherent DSF from the density autocorrelation function.

This routine is intentionally more explicit and slower than compute_dsf(). It first constructs the finite-block circular intermediate scattering function

F(Q, lag) = mean_t rho(Q, t + lag) rho(Q, t).conjugate()

and then Fourier transforms F in time. It is mainly useful for validating that the production estimator based on abs(FFT_t[rho(Q, t)])**2 is consistent with the correlation-function formulation.

pySED.dsf.compute_current_correlations(positions, velocities, qpoints_cartesian, dt, weights=None, num_blocks=1, positive_only=True, backend='cpu')

Compute longitudinal and transverse current spectra.

Spectra are evaluated independently for each time block, normalized by block_size * num_atoms, then averaged. When num_blocks > 1, the result includes standard errors of the block-averaged spectra.

pySED.dsf.compute_dsf(positions, qpoints_cartesian, dt, atom_types=None, experiment='neutron', coherent_weights=None, incoherent_cross_sections=None, xray_form_factor_table=None, xray_missing='atomic_number', num_blocks=1, positive_only=True, backend='cpu')

Compute coherent/incoherent dynamic structure factors.

Parameters

positions

Array with shape (num_frames, num_atoms, 3) in Angstrom.

qpoints_cartesian

Q points in inverse Angstrom, including the 2*pi convention.

dt

Sampling interval in seconds.

backend

"cpu"/"numpy" by default. Use "cupy" or "gpu" only when CuPy is installed for the local CUDA runtime.

pySED.dsf.compute_partial_dsf(positions, qpoints_cartesian, dt, atom_types, species_weights=None, num_blocks=1, positive_only=True, backend='cpu')

Compute species-pair coherent partial DSF matrices.

The returned partial array has shape (num_qpoints, num_species, num_species, num_frequencies) and contains the block-averaged cross spectra

FFT[rho_A(Q,t)] * conj(FFT[rho_B(Q,t))).

weighted_total is the coherent spectrum reconstructed from the partials using the supplied species weights.

pySED.dsf.neutron_weights(atom_types, include_incoherent=True)
pySED.dsf.resolve_scattering_weights(atom_types=None, qpoints_cartesian=None, experiment='neutron', coherent_weights=None, incoherent_cross_sections=None, xray_form_factor_table=None, xray_missing='atomic_number')
pySED.dsf.xray_atomic_number_weights(atom_types, qpoints_cartesian=None)

Return an approximate X-ray weight using atomic number.

This is a safe default when no tabulated form-factor data are supplied. For publication-quality X-ray comparison, pass q-dependent form factors through coherent_weights.

pySED.dsf.xray_form_factor_weights(atom_types, qpoints_cartesian=None, coefficients_table=None, missing='atomic_number')

Return q-dependent X-ray form-factor weights.

This thin wrapper exposes pySED.probe_weights through the historical DSF namespace.

Mode Visibility

Mode-resolved one-phonon visibility for neutron and X-ray scattering.

class pySED.mode_visibility.OnePhononDecompositionMap(Q_reduced: ndarray, Q_cartesian: ndarray, energy_axis: ndarray, intensity: ndarray, atom_intensity: ndarray, direction_intensity: ndarray, atom_interference_intensity: ndarray, visibility: ndarray, atom_visibility: ndarray, direction_visibility: ndarray, atom_interference: ndarray, metadata: dict)

Bases: object

Energy-resolved atom/direction/interference one-phonon diagnostics.

Q_cartesian: ndarray
Q_reduced: ndarray
atom_intensity: ndarray
atom_interference: ndarray
atom_interference_intensity: ndarray
atom_visibility: ndarray
direction_intensity: ndarray
direction_visibility: ndarray
energy_axis: ndarray
intensity: ndarray
metadata: dict
visibility: ndarray
class pySED.mode_visibility.OnePhononMap(Q_reduced: ndarray, Q_cartesian: ndarray, energy_axis: ndarray, intensity: ndarray, visibility: ndarray, metadata: dict)

Bases: object

Energy-resolved coherent one-phonon visibility map.

Q_cartesian: ndarray
Q_reduced: ndarray
energy_axis: ndarray
intensity: ndarray
metadata: dict
visibility: ndarray
class pySED.mode_visibility.OnePhononVisibility(qpoints_reduced: ndarray, g_vectors_reduced: ndarray, Q_reduced: ndarray, Q_cartesian: ndarray, amplitude: ndarray, visibility: ndarray, atom_amplitude: ndarray, atom_visibility: ndarray, direction_amplitude: ndarray, direction_visibility: ndarray, atom_interference: ndarray, scattering_weights: ndarray, metadata: dict)

Bases: object

Coherent one-phonon mode visibility and diagnostic amplitudes.

Q_cartesian: ndarray
Q_reduced: ndarray
amplitude: ndarray
atom_amplitude: ndarray
atom_interference: ndarray
atom_visibility: ndarray
direction_amplitude: ndarray
direction_visibility: ndarray
g_vectors_reduced: ndarray
metadata: dict
qpoints_reduced: ndarray
scattering_weights: ndarray
visibility: ndarray
pySED.mode_visibility.build_one_phonon_decomposition_map_from_mode_spectra(visibility, mode_spectra, energy_axis)

Build energy-resolved atom/direction/interference INS/IXS maps.

pySED.mode_visibility.build_one_phonon_map(visibility, mode_frequencies, energy_axis, broadening, broadening_kind='lorentzian')

Convert one-phonon visibility into a broadened harmonic map.

pySED.mode_visibility.build_one_phonon_map_from_mode_spectra(visibility, mode_spectra, energy_axis)

Weight branch-resolved spectra into coherent INS/IXS intensity maps.

mode_spectra must have shape (num_energy, num_q, num_modes) and can be taken directly from pySED.eigen_sed.EigenSEDResult.sed.

pySED.mode_visibility.compute_one_phonon_visibility(qpoints_reduced, g_vectors_reduced, primitive_cell, basis_positions_reduced, masses, eigenvectors, atom_types=None, experiment='neutron', scattering_weights=None, mode_frequencies=None, frequency_power=0, xray_form_factor_table=None, xray_missing='atomic_number', pairwise_g_vectors=False)

Compute coherent one-phonon INS/IXS visibility for each mode.

The elementary complex contribution is

A[b, alpha] = w_b(Q) Q_alpha e_balpha(q, nu) exp(i 2*pi Q_red dot tau_b) / sqrt(m_b).

frequency_power=-1 adds the common harmonic one-phonon 1/omega intensity prefactor. The default 0 reports the pure polarization, probe-weight, mass, and interference visibility.

pySED.mode_visibility.compute_one_phonon_visibility_for_q_path(qpoints_reduced, g_vectors_reduced, primitive_cell, basis_positions_reduced, masses, eigenvectors, atom_types=None, experiment='neutron', scattering_weights=None, mode_frequencies=None, frequency_power=0, xray_form_factor_table=None, xray_missing='atomic_number')

Compute coherent INS/IXS one-phonon visibility for a pairwise Q path.

pySED.mode_visibility.gaussian(axis, center, sigma)
pySED.mode_visibility.lorentzian(axis, center, hwhm)

Visibility Diagnostics

Mode-level visibility diagnostics for experiment-facing scattering maps.

class pySED.visibility_diagnostics.ModeVisibilityDiagnostics(visibility: ndarray, relative_visibility: ndarray, atom_sum: ndarray, direction_sum: ndarray, basis_interference: ndarray, direction_interference: ndarray, basis_interference_fraction: ndarray, direction_interference_fraction: ndarray, dominant_atom_index: ndarray, dominant_direction_index: ndarray, dominant_reason: ndarray, q_error_reduced: ndarray, q_error_cartesian: ndarray, atom_labels: list, direction_labels: list, mode_labels: list, metadata: dict)

Bases: object

Branch-level explanation of EELS/INS/IXS mode visibility.

The arrays have shape (num_q, num_g, num_modes) unless noted otherwise. The diagnostic quantities are derived from the complex visibility decomposition

I = |sum_{b,alpha} A_{b,alpha}|^2.

atom_labels: list
atom_sum: ndarray
basis_interference: ndarray
basis_interference_fraction: ndarray
direction_interference: ndarray
direction_interference_fraction: ndarray
direction_labels: list
direction_sum: ndarray
dominant_atom_index: ndarray
dominant_direction_index: ndarray
dominant_reason: ndarray
metadata: dict
mode_labels: list
q_error_cartesian: ndarray
q_error_reduced: ndarray
reason_counts()

Return a dictionary counting the dominant reason labels.

relative_visibility: ndarray
to_records(include_all=True, relative_threshold=None)

Return a list of row dictionaries suitable for tables or CSV.

Parameters

include_allbool

If False, only rows with relative visibility less than relative_threshold are emitted.

relative_thresholdfloat or None

Relative-visibility cutoff for weak-branch rows. When None, the value stored in metadata["relative_floor"] is used.

visibility: ndarray
weak_records(relative_threshold=None)

Return only branches classified as weak by relative visibility.

write_csv(path, include_all=True, relative_threshold=None)

Write diagnostic records to a CSV table.

pySED.visibility_diagnostics.diagnose_mode_visibility(visibility_decomposition, q_advice=None, atom_labels=None, mode_labels=None, visibility_floor=1e-14, relative_floor=0.001, interference_floor=0.8, q_error_floor=1e-10)

Diagnose why each branch is visible or weak in EELS/INS/IXS.

visibility_decomposition can be an pySED.eels.EELSVisibilityDecomposition or a pySED.mode_visibility.OnePhononVisibility. It must expose visibility, atom_visibility and direction_visibility. The returned object quantifies atom/basis interference and Cartesian-direction selection without changing the underlying scattering intensity.

Eigenvector SED

Eigenvector-resolved spectral energy density.

class pySED.eigen_sed.EigenSEDResult(frequencies_thz: numpy.ndarray, qpoints_reduced: numpy.ndarray, sed: numpy.ndarray, mode_frequencies: numpy.ndarray, metadata: dict)

Bases: object

frequencies_thz: ndarray
metadata: dict
mode_frequencies: ndarray
qpoints_reduced: ndarray
sed: ndarray
class pySED.eigen_sed.EigenvectorSet(qpoints_reduced: numpy.ndarray, frequencies: numpy.ndarray, eigenvectors: numpy.ndarray, source: str = 'unknown')

Bases: object

eigenvectors: ndarray
frequencies: ndarray
qpoints_reduced: ndarray
source: str = 'unknown'
pySED.eigen_sed.compute_eigen_sed(velocities, unitcell_vectors, basis_index, masses, primitive_cell, supercell_cell, eigenvectors, dt, num_blocks=1, target_qpoints=None, validate_q=True, backend='cpu')

Project velocities onto phonon eigenvectors and compute branch SED.

velocities must have shape (num_frames, num_atoms, 3). The basis_index array maps each atom to a one-based primitive basis label. unitcell_vectors must contain the equilibrium cell-vector position of each repeated primitive cell in Angstrom.

pySED.eigen_sed.read_phonopy_band_yaml(path)

Read q points, frequencies, and eigenvectors from a phonopy band.yaml.

pySED.eigen_sed.validate_eigenvector_qpoints(eigenvectors, advisor, target_qpoints=None, atol=1e-06)

Electron Kinematics

Electron-beam kinematics for calibrating q-EELS momentum axes.

pySED.electron_kinematics.angular_resolution_to_q_sigma(sigma_angle, beam_energy_ev, angle_unit='mrad')

Convert an angular resolution width to transverse sigma_q.

The returned value is in inverse Angstrom and can be passed to pySED.compare_experiment.prepare_map_comparison() as sigma_q when the scattering-map q axis is a Cartesian path distance in inverse Angstrom.

pySED.electron_kinematics.eels_qpoints_from_angle_axis(angle_axis, beam_energy_ev, direction=(1.0, 0.0), energy_loss_ev=0.0, angle_unit='mrad', include_longitudinal=False)

Return a q-EELS line-scan Q path from a scalar scattering-angle axis.

pySED.electron_kinematics.electron_beta(beam_energy_ev)

Return relativistic electron speed as v/c.

pySED.electron_kinematics.electron_momentum_pc_ev(beam_energy_ev)

Return relativistic electron momentum times c in eV.

pySED.electron_kinematics.electron_wavelength_angstrom(beam_energy_ev)

Return relativistic de Broglie wavelength in Angstrom.

pySED.electron_kinematics.electron_wavenumber_per_angstrom(beam_energy_ev)

Return electron wave number 2*pi/lambda in inverse Angstrom.

pySED.electron_kinematics.longitudinal_momentum_transfer(energy_loss_ev, beam_energy_ev)

Return the small-loss longitudinal momentum transfer in inverse Angstrom.

The approximation is q_parallel = DeltaE / (hbar v) with v = beta c. For vibrational losses this term is usually much smaller than the transverse detector momentum but it is useful for documenting the experiment geometry.

pySED.electron_kinematics.scattering_angles_to_qpoints(theta_x, theta_y=None, beam_energy_ev=200000.0, energy_loss_ev=0.0, angle_unit='rad', include_longitudinal=True)

Convert small electron scattering angles to Cartesian Q points.

theta_x and theta_y are detector scattering angles. The transverse momentum transfer is reported with the experimental sign convention Q_perp = k0 * theta where k0 = 2*pi/lambda. If include_longitudinal is true, the third component is DeltaE / (hbar v); otherwise it is zero.

Kinematic q-EELS

Kinematic q-EELS visibility and map construction.

class pySED.eels.EELSDecompositionMap(Q_reduced: numpy.ndarray, Q_cartesian: numpy.ndarray, energy_axis: numpy.ndarray, intensity: numpy.ndarray, atom_intensity: numpy.ndarray, direction_intensity: numpy.ndarray, atom_interference_intensity: numpy.ndarray, visibility: numpy.ndarray, atom_visibility: numpy.ndarray, direction_visibility: numpy.ndarray, atom_interference: numpy.ndarray, metadata: dict)

Bases: object

Q_cartesian: ndarray
Q_reduced: ndarray
atom_intensity: ndarray
atom_interference: ndarray
atom_interference_intensity: ndarray
atom_visibility: ndarray
direction_intensity: ndarray
direction_visibility: ndarray
energy_axis: ndarray
intensity: ndarray
metadata: dict
visibility: ndarray
class pySED.eels.EELSMap(Q_reduced: numpy.ndarray, Q_cartesian: numpy.ndarray, energy_axis: numpy.ndarray, intensity: numpy.ndarray, visibility: numpy.ndarray)

Bases: object

Q_cartesian: ndarray
Q_reduced: ndarray
energy_axis: ndarray
intensity: ndarray
visibility: ndarray
class pySED.eels.EELSVisibility(qpoints_reduced: numpy.ndarray, g_vectors_reduced: numpy.ndarray, Q_reduced: numpy.ndarray, Q_cartesian: numpy.ndarray, visibility: numpy.ndarray, metadata: dict)

Bases: object

Q_cartesian: ndarray
Q_reduced: ndarray
g_vectors_reduced: ndarray
metadata: dict
qpoints_reduced: ndarray
visibility: ndarray
class pySED.eels.EELSVisibilityDecomposition(qpoints_reduced: numpy.ndarray, g_vectors_reduced: numpy.ndarray, Q_reduced: numpy.ndarray, Q_cartesian: numpy.ndarray, amplitude: numpy.ndarray, visibility: numpy.ndarray, atom_amplitude: numpy.ndarray, atom_visibility: numpy.ndarray, direction_amplitude: numpy.ndarray, direction_visibility: numpy.ndarray, atom_interference: numpy.ndarray, metadata: dict)

Bases: object

Q_cartesian: ndarray
Q_reduced: ndarray
amplitude: ndarray
atom_amplitude: ndarray
atom_interference: ndarray
atom_visibility: ndarray
direction_amplitude: ndarray
direction_visibility: ndarray
g_vectors_reduced: ndarray
metadata: dict
qpoints_reduced: ndarray
visibility: ndarray
pySED.eels.build_eels_decomposition_map_from_mode_spectra(decomposition, mode_spectra, energy_axis)

Convert mode-resolved EELS diagnostics into energy-resolved maps.

decomposition should come from compute_mode_visibility_decomposition(). The returned maps preserve the non-additive diagnostics:

total = sum_atom(atom_intensity) + atom_interference_intensity.

pySED.eels.build_eels_map(visibility, mode_frequencies, energy_axis, broadening, broadening_kind='lorentzian')

Convert mode visibility into an energy-resolved q-EELS map.

pySED.eels.build_eels_map_from_mode_spectra(visibility, mode_spectra, energy_axis)

Weight branch-resolved spectral functions into an EELS intensity map.

mode_spectra must have shape (num_energy, num_q, num_modes). This matches pySED.eigen_sed.EigenSEDResult.sed, allowing branch-resolved MD spectra to be converted directly into extended-zone q-EELS maps:

I(Q, energy) = sum_mode visibility(Q, mode) * A(q, mode, energy).

pySED.eels.compute_mode_visibility(qpoints_reduced, g_vectors_reduced, primitive_cell, basis_positions_reduced, masses, eigenvectors, electron_form_factors=None, atom_types=None, electron_form_factor_model='unit', electron_form_factor_table=None, electron_missing='raise', pairwise_g_vectors=False)

Compute branch visibility for extended-zone kinematic q-EELS.

The implemented factor is

abs(sum_b f_b(Q) (Q dot e_bv(q)) exp(i 2*pi Q_red dot tau_b) / sqrt(m_b))**2.

pySED.eels.compute_mode_visibility_decomposition(qpoints_reduced, g_vectors_reduced, primitive_cell, basis_positions_reduced, masses, eigenvectors, electron_form_factors=None, atom_types=None, electron_form_factor_model='unit', electron_form_factor_table=None, electron_missing='raise', pairwise_g_vectors=False)

Compute complex q-EELS visibility contributions.

The primitive contribution is

A[b, alpha] = f_b(Q) Q_alpha e_balpha(q, nu) exp(i 2*pi Q_red dot tau_b) / sqrt(m_b).

Atom and direction amplitudes are coherent sums of A[b, alpha]. Their squared magnitudes are diagnostic quantities; they do not add up to the total visibility when there is destructive or constructive interference.

pySED.eels.compute_mode_visibility_for_q_path(qpoints_reduced, g_vectors_reduced, primitive_cell, basis_positions_reduced, masses, eigenvectors, electron_form_factors=None, atom_types=None, electron_form_factor_model='unit', electron_form_factor_table=None, electron_missing='raise')

Compute EELS visibility for a pairwise experimental path.

Unlike compute_mode_visibility(), this function interprets qpoints_reduced[i] and g_vectors_reduced[i] as one experimental point Q_i = q_i + G_i. The returned visibility has shape (num_points, 1, num_modes) so it remains compatible with build_eels_map_from_mode_spectra() without creating the full q-by-G Cartesian product.

pySED.eels.fold_to_first_bz(qpoints_reduced)
pySED.eels.gaussian(axis, center, sigma)
pySED.eels.lorentzian(axis, center, hwhm)

Experiment Comparison

Utilities for comparing simulated scattering maps with experiments.

class pySED.compare_experiment.MapComparison(simulated: numpy.ndarray, experimental: numpy.ndarray, residual: numpy.ndarray, rmse: float, mae: float, correlation: float, simulated_map: object = None, experimental_map: object = None)

Bases: object

correlation: float
experimental: ndarray
experimental_map: object = None
mae: float
residual: ndarray
rmse: float
simulated: ndarray
simulated_map: object = None
class pySED.compare_experiment.ScatteringMap(q_axis: numpy.ndarray, energy_axis: numpy.ndarray, intensity: numpy.ndarray, metadata: dict = None)

Bases: object

energy_axis: ndarray
intensity: ndarray
metadata: dict = None
q_axis: ndarray
pySED.compare_experiment.align_intensity_map(intensity, source_q, source_energy, target_q, target_energy)

Interpolate a 2D q-energy map onto target experimental axes.

pySED.compare_experiment.align_to_experiment(simulated_map, experimental_map)

Return the simulated map interpolated onto the experimental axes.

pySED.compare_experiment.apply_quantum_correction_to_map(scattering_map, temperature, energy_unit='thz')

Apply the harmonic classical-to-quantum correction to a map.

pySED.compare_experiment.broaden_energy(intensity, sigma_points, axis=-1)
pySED.compare_experiment.compare_maps(simulated, experimental, normalization='max')
pySED.compare_experiment.comparison_linecut_records(comparison, q_indices=None, energy_indices=None)

Return fixed-q and fixed-energy line-cut records.

The records use the normalized arrays stored in pySED.compare_experiment.MapComparison, so they match the residual and scalar metrics used in the paper-ready comparison.

pySED.compare_experiment.comparison_peak_records(comparison)

Return peak-tracking rows along the energy axis for every q point.

pySED.compare_experiment.comparison_residual_map(comparison)

Return the residual as a calibrated ScatteringMap.

pySED.compare_experiment.comparison_summary(comparison)

Return JSON-ready scalar metrics for a map comparison.

pySED.compare_experiment.convolve_q_resolution(intensity, sigma_q_points, sigma_energy_points=0.0)
pySED.compare_experiment.convolve_resolution_units(scattering_map, sigma_q=None, sigma_energy=None)

Apply Gaussian q/energy resolution using physical axis units.

pySED.compare_experiment.crop_image(image, crop=None)

Crop an image with (row_start, row_stop, col_start, col_stop).

pySED.compare_experiment.image_to_grayscale(image, channel_weights=(0.299, 0.587, 0.114))

Convert a grayscale/RGB/RGBA image array to a 2D intensity array.

pySED.compare_experiment.interpolate_energy_axis(intensity, source_energy, target_energy, axis=-1)
pySED.compare_experiment.linecut(intensity, index, axis=0)
pySED.compare_experiment.load_intensity_map(path, delimiter=None)

Load an experimental intensity map from .npy or text/CSV.

pySED.compare_experiment.load_scattering_map_image(path, q_range, energy_range, crop=None, invert=False, origin='upper', normalization=None, channel_weights=(0.299, 0.587, 0.114), metadata=None)

Load an image file and calibrate it into a ScatteringMap.

pySED.compare_experiment.map_value_records(scattering_map, value_name='intensity')

Return flattened q-energy map records for CSV export.

pySED.compare_experiment.normalize_intensity(intensity, method='max', eps=1e-30)
pySED.compare_experiment.prepare_map_comparison(simulated_map, experimental_map, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, energy_unit='thz')

Align, optionally quantum-correct, broaden, normalize, and compare maps.

pySED.compare_experiment.scattering_map_from_image(image, q_range, energy_range, crop=None, invert=False, origin='upper', normalization=None, channel_weights=(0.299, 0.587, 0.114), metadata=None)

Calibrate an image array into a ScatteringMap.

q_range is mapped left-to-right across image columns. energy_range is mapped bottom-to-top for origin="upper" images, matching ordinary image files where row zero is the top of the image.

pySED.compare_experiment.track_peak_positions(intensity, energy_axis, axis=-1)
pySED.compare_experiment.write_map_comparison_report(comparison, output_dir, prefix='comparison', write_residual=True, write_peaks=True, linecut_q_indices=None, linecut_energy_indices=None)

Write paper-ready comparison metrics and tables.

The report consists of a JSON summary plus optional flattened CSV tables for the residual map, q-resolved peak tracking, and fixed-q/fixed-energy line cuts.

Scattering Plots

Plotting helpers for paper-ready scattering-map comparisons.

pySED.scattering_plot.plot_linecuts(simulated_map, experimental_map=None, q_indices=None, ax=None, labels=None, energy_label='Energy', intensity_label='Intensity', save_path=None, dpi=300)

Plot energy line cuts at selected q indices.

pySED.scattering_plot.plot_map_comparison(comparison, q_label='Q path', energy_label='Energy', cmap='magma', residual_cmap='coolwarm', q_indices=None, figsize=(10.0, 6.2), save_path=None, dpi=300)

Create a simulated/experimental/residual map and line-cut figure.

pySED.scattering_plot.plot_scattering_map(scattering_map, ax=None, title=None, cmap='magma', vmin=None, vmax=None, q_label='Q path', energy_label='Energy', colorbar=True, save_path=None, dpi=300)

Plot a single q-energy scattering map.

Returns (fig, ax, image) so callers can further tune labels, ticks, or panel annotations before saving.

Scattering Workflows

High-level workflows for experiment-facing scattering maps.

class pySED.scattering_workflow.CurrentCorrelationWorkflowResult(advisor: QAdvisor, q_advice: list, qpoints_reduced: ndarray, qpoints_cartesian: ndarray, current: object, scattering_map: ScatteringMap = None, comparison: object = None)

Bases: object

Outputs from q-advised longitudinal/transverse current spectra.

advisor: QAdvisor
comparison: object = None
current: object
q_advice: list
qpoints_cartesian: ndarray
qpoints_reduced: ndarray
scattering_map: ScatteringMap = None
class pySED.scattering_workflow.DSFWorkflowResult(advisor: QAdvisor, q_advice: list, qpoints_reduced: ndarray, qpoints_cartesian: ndarray, dsf: object, scattering_map: ScatteringMap = None, comparison: object = None)

Bases: object

Outputs from q-advised DSF calculation.

advisor: QAdvisor
comparison: object = None
dsf: object
q_advice: list
qpoints_cartesian: ndarray
qpoints_reduced: ndarray
scattering_map: ScatteringMap = None
class pySED.scattering_workflow.EELSWorkflowResult(advisor: QAdvisor, q_advice: list, eigen_sed: object, visibility: object, eels_map: object, scattering_map: ScatteringMap, comparison: object = None, q_plan: object = None)

Bases: object

Outputs from the MD-to-extended-zone-EELS workflow.

advisor: QAdvisor
comparison: object = None
eels_map: object
eigen_sed: object
q_advice: list
q_plan: object = None
scattering_map: ScatteringMap
visibility: object
class pySED.scattering_workflow.OnePhononWorkflowResult(advisor: QAdvisor, q_advice: list, eigen_sed: object, visibility: object, one_phonon_map: object, scattering_map: ScatteringMap, comparison: object = None, q_plan: object = None)

Bases: object

Outputs from eigen-SED-to-INS/IXS one-phonon workflow.

advisor: QAdvisor
comparison: object = None
eigen_sed: object
one_phonon_map: object
q_advice: list
q_plan: object = None
scattering_map: ScatteringMap
visibility: object
class pySED.scattering_workflow.PartialDSFWorkflowResult(advisor: QAdvisor, q_advice: list, qpoints_reduced: ndarray, qpoints_cartesian: ndarray, partial_dsf: object, scattering_map: ScatteringMap = None, comparison: object = None)

Bases: object

Outputs from q-advised species-partial DSF calculation.

advisor: QAdvisor
comparison: object = None
partial_dsf: object
q_advice: list
qpoints_cartesian: ndarray
qpoints_reduced: ndarray
scattering_map: ScatteringMap = None
pySED.scattering_workflow.compute_current_correlation_workflow(positions, velocities, qpoints, primitive_cell, supercell_cell, dt, qpoint_coordinates='reduced', q_policy='strict', max_error_reduced=None, max_error_cartesian=None, map_component='longitudinal', q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz', **current_kwargs)

Validate/map requested Q points before computing current spectra.

pySED.scattering_workflow.compute_dsf_workflow(positions, qpoints, primitive_cell, supercell_cell, dt, qpoint_coordinates='reduced', q_policy='strict', max_error_reduced=None, max_error_cartesian=None, map_component='total', q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz', **dsf_kwargs)

Validate or map requested Q points before computing DSF.

q_policy="strict" rejects non-commensurate points. "nearest" maps each requested point to the nearest commensurate point while preserving the extended-zone reciprocal-lattice image, so Q = q + G is not folded back to the first Brillouin zone.

pySED.scattering_workflow.compute_eels_workflow(velocities, unitcell_vectors, basis_index, masses, primitive_cell, supercell_cell, eigenvectors, basis_positions_reduced, g_vectors_reduced, dt, num_blocks=1, sed_backend='cpu', target_qpoints_reduced=None, electron_form_factors=None, atom_types=None, electron_form_factor_model='unit', electron_form_factor_table=None, electron_missing='raise', pairwise_g_vectors=False, g_index=0, q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz')

Run the commensurate-q, eigen-SED, and kinematic EELS workflow.

This function is intentionally a workflow wrapper around the lower-level modules. It keeps the paper-facing sequence explicit:

q_advisor -> eigen_sed -> EELS visibility -> extended-zone map.

pySED.scattering_workflow.compute_eels_workflow_from_angle_axis(velocities, unitcell_vectors, basis_index, masses, primitive_cell, supercell_cell, eigenvectors, basis_positions_reduced, angle_axis, beam_energy_ev, dt, direction=(1.0, 0.0), energy_loss_ev=0.0, angle_unit='mrad', include_longitudinal=False, q_policy='strict', max_error_reduced=None, max_error_cartesian=None, num_blocks=1, sed_backend='cpu', electron_form_factors=None, atom_types=None, electron_form_factor_model='unit', electron_form_factor_table=None, electron_missing='raise', q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz')

Run q-EELS workflow from a calibrated detector scattering-angle axis.

The angle axis is converted to Cartesian momentum transfer with pySED.electron_kinematics.eels_qpoints_from_angle_axis(), then the standard extended-zone q-path workflow is used.

pySED.scattering_workflow.compute_eels_workflow_from_q_path(velocities, unitcell_vectors, basis_index, masses, primitive_cell, supercell_cell, eigenvectors, basis_positions_reduced, experimental_qpoints, dt, qpoint_coordinates='cartesian', q_policy='strict', max_error_reduced=None, max_error_cartesian=None, num_blocks=1, sed_backend='cpu', electron_form_factors=None, atom_types=None, electron_form_factor_model='unit', electron_form_factor_table=None, electron_missing='raise', q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz')

Run the EELS workflow directly from an experimental extended-zone Q path.

This wrapper performs the full experiment-facing sequence:

experimental Q -> q_plan(Q=q+G) -> eigen-SED at folded q -> pairwise EELS map.

pySED.scattering_workflow.compute_one_phonon_workflow(velocities, unitcell_vectors, basis_index, masses, primitive_cell, supercell_cell, eigenvectors, basis_positions_reduced, g_vectors_reduced, dt, atom_types=None, experiment='neutron', scattering_weights=None, frequency_power=0, num_blocks=1, sed_backend='cpu', target_qpoints_reduced=None, pairwise_g_vectors=False, g_index=0, q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz')

Run commensurate-q, eigen-SED, and one-phonon INS/IXS workflow.

pySED.scattering_workflow.compute_one_phonon_workflow_from_q_path(velocities, unitcell_vectors, basis_index, masses, primitive_cell, supercell_cell, eigenvectors, basis_positions_reduced, experimental_qpoints, dt, qpoint_coordinates='cartesian', q_policy='strict', max_error_reduced=None, max_error_cartesian=None, atom_types=None, experiment='neutron', scattering_weights=None, frequency_power=0, num_blocks=1, sed_backend='cpu', q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz')

Run coherent INS/IXS one-phonon workflow from an experimental Q path.

pySED.scattering_workflow.compute_partial_dsf_workflow(positions, qpoints, primitive_cell, supercell_cell, dt, atom_types, qpoint_coordinates='reduced', q_policy='strict', max_error_reduced=None, max_error_cartesian=None, map_component='weighted_total', species_pair=None, complex_part='real', q_axis=None, experimental_map=None, sigma_q=None, sigma_energy=None, normalization='max', quantum_temperature=None, output_energy_unit='thz', **partial_kwargs)

Validate/map requested Q points before computing species-partial DSF.

pySED.scattering_workflow.current_correlation_to_scattering_map(current_result, component='longitudinal', q_axis=None, source_energy_unit='thz', output_energy_unit='thz')

Convert a longitudinal/transverse current spectrum to a q-energy map.

pySED.scattering_workflow.dsf_to_scattering_map(dsf_result, component='total', q_axis=None, source_energy_unit='thz', output_energy_unit='thz')

Convert a DSF result component into a q-energy scattering map.

pySED.scattering_workflow.eels_map_to_scattering_map(eels_map, g_index=0, q_axis=None, source_energy_unit='thz', output_energy_unit='thz')

Select one extended-zone G branch as a 2D q-energy map.

The returned q axis is the cumulative Cartesian path distance unless a user-supplied q_axis is provided.

pySED.scattering_workflow.one_phonon_map_to_scattering_map(one_phonon_map, g_index=0, q_axis=None, source_energy_unit='thz', output_energy_unit='thz')

Select one extended-zone G branch from an INS/IXS one-phonon map.

pySED.scattering_workflow.partial_dsf_to_scattering_map(partial_result, component='weighted_total', species_pair=None, complex_part='real', q_axis=None, source_energy_unit='thz', output_energy_unit='thz')

Convert a partial DSF component into a q-energy scattering map.

component="weighted_total" returns the probe-weighted coherent total. component="partial" selects one species pair from the complex partial matrix and returns its real part, imaginary part, or magnitude.

pySED.scattering_workflow.q_path_axis(qpoints_cartesian)

Return cumulative path distance for ordered Cartesian Q points.

Scattering Export

Export helpers for paper-ready scattering workflow bundles.

pySED.scattering_export.workflow_export_summary(result, diagnostics=None)

Return a JSON-ready summary for a scattering workflow result.

pySED.scattering_export.write_scattering_workflow_bundle(result, output_dir, prefix='workflow', atom_labels=None, mode_labels=None, include_all_visibility=True, visibility_relative_threshold=None, linecut_q_indices=None, linecut_energy_indices=None, write_scattering_map=True)

Write a reproducible bundle for a DSF/EELS/INS/IXS workflow result.

The bundle gathers q-advisor decisions, folded phonopy q points, simulated maps, visibility diagnostics, and experimental comparison tables when those objects are present on result.

Validation

Validation helpers for scattering implementations.

class pySED.validation.DSFValidationCase(positions: ndarray, qpoints_cartesian: ndarray, coherent_weights: ndarray, dt: float, num_blocks: int = 4, description: str = 'synthetic deterministic trajectory')

Bases: object

Small deterministic DSF validation problem.

coherent_weights: ndarray
description: str = 'synthetic deterministic trajectory'
dt: float
num_blocks: int = 4
positions: ndarray
qpoints_cartesian: ndarray
class pySED.validation.SpectrumComparison(passed: bool, candidate_shape: tuple, reference_shape: tuple, normalization: str, scale_factor: float, rmse: float, mae: float, max_abs: float, normalized_rmse: float, correlation: float, peak_frequency_mae: float = None, reason: str = None)

Bases: object

Metrics for comparing a pySED spectrum with an external reference.

candidate_shape: tuple
correlation: float
mae: float
max_abs: float
normalization: str
normalized_rmse: float
passed: bool
peak_frequency_mae: float = None
reason: str = None
reference_shape: tuple
rmse: float
scale_factor: float
to_dict()
pySED.validation.build_dsf_validation_report(case=None, tolerance=1e-12)

Build a structured DSF validation report.

pySED.validation.compare_arrays(candidate, reference, atol=1e-12, rtol=1e-12)

Return scalar comparison metrics between two arrays.

pySED.validation.compare_external_dsf_reference(reference_path, reference_key='coherent', reference_frequency_key=None, case=None, component='coherent', normalization='least_squares', rmse_tolerance=None)

Compare pySED DSF with an external dynasor/pynamic-style .npz output.

pySED.validation.compare_reference_spectrum(candidate, reference, candidate_frequencies=None, reference_frequencies=None, normalization='least_squares', rmse_tolerance=None)

Compare a spectrum against an external reference spectrum.

The last axis is treated as frequency. If both frequency grids are supplied and differ, the reference is interpolated onto the candidate grid before scalar metrics are computed.

pySED.validation.compute_pysed_validation_pair(case=None)

Compute pySED direct and explicit-correlation DSF for one case.

pySED.validation.export_dsf_validation_case(path, case=None, include_pysed_reference=True)

Export a deterministic validation case as an .npz file.

pySED.validation.load_dsf_validation_case(path)

Load a validation case exported by export_dsf_validation_case().

pySED.validation.optional_scattering_package_report()

Report optional package availability for external DSF validation.

pySED.validation.package_status(module_name)

Return installation metadata for an optional validation dependency.

pySED.validation.run_internal_dsf_reference_validation(case=None, tolerance=1e-12)

Validate pySED’s direct estimator against its explicit correlation path.

pySED.validation.synthetic_dsf_validation_case(num_frames=64)

Return a deterministic trajectory and weighted Q points.

pySED.validation.validation_environment()

Return reproducibility metadata for a validation report.

pySED.validation.write_validation_report(report, path)

Write a JSON validation report.