kika.cov
The cov module provides functionality for working with covariance matrices from various sources.
- kika.cov.read_coverx(file_path: str, ascending: bool = True, energy_unit: str = 'eV') CrossSectionCovariance[source]
Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.
The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.
- Parameters:
- Returns:
Parsed covariance data
- Return type:
CrossSectionCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found in the file
InvalidDataFormatError – If the binary structure is corrupted
- kika.cov.write_coverx(data: CrossSectionCovariance, file_path: str, fmt: str = 'binary', title: str = '', endian: str = '>') None[source]
Write a
CrossSectionCovarianceto a COVERX covariance file (text or binary).
- kika.cov.read_covfil(file_path: str, energy_unit: str = 'eV') CrossSectionCovariance | LegendreCovariance[source]
Parse an NJOY-generated COVFIL/GENDF covariance file.
Returns a
CrossSectionCovariancefor MF33 files (cross-section covariances) or anLegendreCovariancefor MF34 files (angular-distribution covariances).MF3 cross sections are stored in
CrossSectionCovariance.cross_sections(MF33 case only).- Parameters:
- Returns:
Parsed covariance data.
- Return type:
CrossSectionCovariance or LegendreCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found.
- kika.cov.write_covfil(data: CrossSectionCovariance | LegendreCovariance, file_path: str, tape_label: str = '', temperature: float | None = None, awr: float | None = None) None[source]
Write a
CrossSectionCovarianceorLegendreCovarianceto an NJOY COVFIL/GENDF text file.- Parameters:
data (CrossSectionCovariance or LegendreCovariance) – Covariance data to write.
file_path (str) – Output file path.
tape_label (str, optional) – Label for the tape header line (max 66 chars). Default
''.temperature (float, optional) – Temperature in K written into MF1 MT451 CONT record. If
None(default), usesdata.metadata['temperature'](set byread_covfil()), falling back to0.0.awr (float, optional) – Atomic weight ratio written into MF1 MT451 HEAD and each MF33/MF34 section HEAD. If
None(default), usesdata.metadata['awr'](set byread_covfil()), falling back to0.0.
- kika.cov.read_boxer(file_path: str, energy_unit: str = 'eV') CrossSectionCovariance[source]
Read a BOXER card-image (ASCII) covariance file and return a CrossSectionCovariance.
BOXER is produced by NJOY’s COVR module. It stores energy boundaries, cross-section vectors, standard-deviation vectors, and covariance or correlation matrices in a compressed card-image format.
- kika.cov.write_boxer(data: CrossSectionCovariance, file_path: str, hlibid: str = '', hdescr: str = '', nvf: int = 10) None[source]
Write a
CrossSectionCovarianceto a BOXER card-image (ASCII) file.- Parameters:
data (CrossSectionCovariance) – Covariance data to write.
file_path (str) – Output file path.
hlibid (str, optional) – Library identifier (3 chars max). Default
''.hdescr (str, optional) – Description (32 chars max). Default
''.nvf (int, optional) – Value format code (7–14). Default 10 (
1P8E10.3).
- kika.cov.read_scale_covmat(file_path: str, ascending: bool = True, energy_unit: str = 'eV') CrossSectionCovariance
Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.
The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.
- Parameters:
- Returns:
Parsed covariance data
- Return type:
CrossSectionCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found in the file
InvalidDataFormatError – If the binary structure is corrupted
- kika.cov.read_njoy_covmat(file_path: str, energy_unit: str = 'eV') CrossSectionCovariance | LegendreCovariance
Parse an NJOY-generated COVFIL/GENDF covariance file.
Returns a
CrossSectionCovariancefor MF33 files (cross-section covariances) or anLegendreCovariancefor MF34 files (angular-distribution covariances).MF3 cross sections are stored in
CrossSectionCovariance.cross_sections(MF33 case only).- Parameters:
- Returns:
Parsed covariance data.
- Return type:
CrossSectionCovariance or LegendreCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found.
- class kika.cov.LegendreCovariance(isotope_rows: ~typing.List[int] = <factory>, reaction_rows: ~typing.List[int] = <factory>, l_rows: ~typing.List[int] = <factory>, isotope_cols: ~typing.List[int] = <factory>, reaction_cols: ~typing.List[int] = <factory>, l_cols: ~typing.List[int] = <factory>, energy_grids: ~typing.List[~typing.List[float]] = <factory>, matrices: ~typing.List[~numpy.ndarray] = <factory>, is_relative: ~typing.List[bool] = <factory>, frame: ~typing.List[str] = <factory>, energy_unit: str = 'eV', metadata: ~typing.Dict[str, ~typing.Any] = <factory>, legendre_coefficients: ~typing.Dict[~typing.Tuple[int, int, int], ~numpy.ndarray] = <factory>, mt_metadata: ~typing.Dict[~typing.Tuple[int, int], ~typing.Dict[str, ~typing.Any]] = <factory>)[source]
Bases:
objectFormat-agnostic covariance of Legendre expansion coefficients.
The canonical fields (matrices, energy grids, isotope/reaction/L tags, frame, is_relative) hold the data in a representation that is shared across source formats. Format-specific extras travel in two metadata dictionaries so that round-trips back to the same format produce a compatible (and where possible identical) section.
Canonical fields
- isotope_rows, reaction_rows, l_rowsList[int]
Row tags (ZA, MT, Legendre order) per matrix.
- isotope_cols, reaction_cols, l_colsList[int]
Column tags per matrix.
- energy_gridsList[List[float]]
Energy boundaries for each matrix (NE points → matrix is (NE-1)x(NE-1) or rectangular for cross-L blocks).
- matricesList[np.ndarray]
Covariance matrix per (isotope, reaction, L) row/col tuple.
- is_relativeList[bool]
Truewhen the matrix stores relative covariance,Falsefor absolute (LB=0).- frameList[str]
Reference frame:
"same-as-MF4"(LCT=0),"LAB"(LCT=1),"CM"(LCT=2), or"unknown LCT=…".- energy_unitstr
"eV"(default) or"MeV".- legendre_coefficientsDict[(isotope, mt, l), np.ndarray]
Optional cell-averaged nominal coefficients on the matrix grids.
Format-specific metadata
- metadataDict[str, Any]
Whole-object extras. Conventional keys:
"source_format":"endf","covfil","gendf", …"source_path": original file path (string)any other format-specific defaults the user wants to round-trip.
- mt_metadataDict[(isotope, mt), Dict[str, Any]]
Per-section header data. ENDF MF34 keys:
"za": ZA = 1000*Z + A (float)"awr": atomic-weight ratio"mat": ENDF MAT number"ltt": Legendre representation flag (1 = a_1…, 2 = a_0…).
Populated by
from_endf()/MF34MT.to_ang_covmat()and consulted byto_mf34()to fill the MF34 header. Caller overrides onto_mf34()always win.
- add_matrix(isotope_row: int, reaction_row: int, l_row: int, isotope_col: int, reaction_col: int, l_col: int, matrix: ndarray, energy_grid: List[float], is_relative: bool, frame: str)[source]
Add an angular covariance matrix to the collection.
- Parameters:
isotope_row (int) – Row isotope ID
reaction_row (int) – Row reaction MT number
l_row (int) – Row Legendre coefficient index
isotope_col (int) – Column isotope ID
reaction_col (int) – Column reaction MT number
l_col (int) – Column Legendre coefficient index
matrix (np.ndarray) – Covariance matrix
energy_grid (List[float]) – Energy grid for this covariance matrix
is_relative (bool) – True if matrix values are relative, False if absolute (LB=0 present)
frame (str) – Reference frame: “same-as-MF4”, “LAB”, “CM”, or “unknown LCT=X”
- attach_magnitude_covariance(xs_covariance, *, isotope: int, mt: int, magnitude_mt: int | None = None, atol: float = 1e-12) LegendreCovariance[source]
Fill the null (0, 0) block from MF33, so the L=0 row can be read.
Returns a copy; the receiver is left alone because
MF34MT.to_ang_covmat()caches the object it hands out and a mutation here would leak into every later caller.The MF33 self-covariance is inserted on its own energy grid, not projected onto the MF34 one.
compute_union_energy_grids()merges every grid that mentions the (isotope, mt, 0) triplet, so the L=0 axis becomesunion(MF33 grid, a_0 grid)– a common refinement of both, which is the only grid on which the piecewise-constant lift of both legs is exact. The two are genuinely interleaved in practice: for U-238 ENDF/B-VIII.0, 35 of the 76 MF33/MT2 edges are absent from the MF34 grid and 37 of its 78 are absent from MF33’s, so projecting either onto the other would coarsen it.- Parameters:
xs_covariance (CrossSectionCovariance) – Typically
endf.mf[33].mt[mt].to_xs_covmat(). Must carry a relative self-block for(isotope, magnitude_mt).isotope (int) – The MF34 section whose (0, 0) block is being filled.
mt (int) – The MF34 section whose (0, 0) block is being filled.
magnitude_mt (int, optional) – MT to read from MF33. Defaults to mt – the magnitude that pairs with MT2’s a_l is MT2’s own cross section, and likewise for MT51. Override only to couple a section to a different reaction.
atol (float, default 1e-12) – Tolerance for the “existing (0, 0) block is null” check.
- Raises:
ValueError – If the section has no a_0 blocks, if its (0, 0) block is not null (the file already states the magnitude self-covariance, and adding MF33’s would double count the magnitude variance), if MF33 has no self-block for the reaction, or if that block is absolute while the a_0 family is relative.
- cholesky_decomposition(*, space: str = 'log', psd_method: str = 'auto', jitter_scale: float = 1e-10, max_jitter_ratio: float = 0.001, verbose: bool = True, logger=None) ndarray[source]
Robust Cholesky factor L such that M ≈ L L^T.
- Parameters:
space (str) – “linear” or “log” space for decomposition
psd_method (str) – PSD correction: “auto” (default; clip when negatives are tiny, else Higham), “higham”, “clip”, or “none” (jitter on Cholesky failure).
jitter_scale (float) – Base jitter scale (only used when psd_method=”jitter”).
max_jitter_ratio (float) – Maximum jitter relative to matrix norm.
verbose (bool) – Whether to log progress
logger (optional) – Logger instance for output
- Returns:
Lower triangular Cholesky factor L
- Return type:
np.ndarray
- property clipped_correlation_matrix: ndarray
Return the correlation matrix clipped to [-1, 1] range. Diagonal elements are forced to 1.0, undefined entries become NaN.
- compute_union_energy_grids(atol: float = 1e-12)[source]
Compute union energy grids for all parameter triplets.
This method creates a unified energy grid for each (isotope, reaction, legendre) triplet by merging all energy grids that involve that triplet, removing duplicates within tolerance.
- copy() LegendreCovariance[source]
Return a deep copy of this object.
- property correlation_matrix: ndarray
Return the correlation matrix computed from the covariance matrix. Diagonal elements are forced to 1.0, undefined entries become NaN.
- property covariance_matrix: ndarray
Return the full covariance matrix.
If
ensure_psd()has been called, the cached PSD matrix is returned directly instead of re-assembling from sub-matrices.- Returns:
Full covariance matrix of shape (N*G_max, N*G_max)
- Return type:
np.ndarray
- eigen_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, logger=None) Tuple[ndarray, ndarray][source]
Eigendecomposition with PSD correction.
- Parameters:
- Returns:
Eigenvalues and eigenvectors
- Return type:
Tuple[np.ndarray, np.ndarray]
- energy_unit: str = 'eV'
- ensure_psd(*, preserve_diagonal: bool = True, max_iter: int = 1000, tol: float = 1e-10, eigval_floor: float = 0.0, verbose: bool = True, logger=None) Tuple[LegendreCovariance, dict][source]
Return a copy whose
covariance_matrixis the nearest PSD matrix.Uses Higham’s alternating-projection algorithm. The original sub-matrices are not modified; instead, the assembled PSD matrix is cached and returned by the
covariance_matrix/log_covariance_matrixproperties.- Parameters:
preserve_diagonal (bool) – If True, preserve the original variances (diagonal elements).
max_iter (int) – Maximum number of Higham iterations.
tol (float) – Convergence tolerance on relative Frobenius change.
eigval_floor (float) – Floor for eigenvalues in the PSD projection step.
verbose (bool) – Whether to print diagnostic information.
logger (optional) – Logger instance for file output.
- Returns:
(psd_copy, info) where psd_copy has the PSD cache set and info is the diagnostic dict from
nearest_psd_higham.- Return type:
Tuple[LegendreCovariance, dict]
- filter_by_isotope_reaction(isotope: int, mt: int) LegendreCovariance[source]
Return a new LegendreCovariance containing only matrices for the specified isotope and MT reaction.
This method filters the covariance matrices to include only those where both row and column parameters match the specified isotope and MT reaction.
- classmethod from_covariance_suite(suite, nuclide: int = 0, energy_unit: str = 'eV') LegendreCovariance[source]
Every MF34 section of a GNDS
covarianceSuite, as one carrier.The covariance half of what phase 4 set out to do: the multigroup collapse can be driven from a
reactionSuiteon both sides now — a_l throughlegendre_source_from_model(), and the covariance through here.The model is treated as one more source format, on the same footing as GENDF, COVFIL, BOXER and COVERX, exactly as
from_covariance_section()already does. Duck-typed rather than imported, and for the same reason:kika.covdoes not depend on the model, and phase 4’s P4 had just finished removing the last import-time reason it might.A suite rather than a section, unlike the cross-section twin, because the two are keyed differently. There, several MF35 bands share one MT and would be indistinguishable inside one object. Here the key is
(isotope, MT, l)and every MF34 section carries a distinct one, so a whole file assembles without collision — which is what the collapse needs, since it walks the blocks looking for(l_row, l_col)pairs.Sections whose
rowData.ENDF_MFMTis not34/…are skipped, so an MF31 or MF33 section living in the same suite is left alone rather than swept in as an angular block.- Parameters:
suite – A
CovarianceSuite: anything iterable of sections, or exposingcovarianceSections. Each section needsform.matrix,form.rowGrid,form.isRelativeandrowData.ENDF_MFMT, withrowData.legendreOrdergiving l.nuclide (int, optional) – ZAID to file the matrices under, used when
section.provenance.zais absent. The model identifies a covariance by href rather than by a material header.energy_unit (str, optional) – Unit of
form.rowGrid. The model keeps ENDF’s native eV.
Notes
⚠ The model states two grids and this carrier stores one. Where a section’s row and column grids differ, the model is the one telling the truth and this conversion loses the distinction — the same asymmetry
kika.sampling.model_blocks._mf34_entriesdocuments from the sampling side. A warning is raised rather than the difference being absorbed silently. No MF34 seen so far states two.⚠ A section that states no single matrix raises. It used to be
continue, which dropped the section and returned a smaller carrier that looked complete — the one place in the library where meeting a §25.2mixedwas silent rather than loud. What raises now, and why kika will not merge the components for you, iskika._covariance_forms.require_single_matrix().
- classmethod from_covfil(file_path: str | Path, energy_unit: str = 'eV') LegendreCovariance[source]
Create a LegendreCovariance instance from an NJOY-generated COVFIL/GENDF file.
- Parameters:
- Returns:
LegendreCovariance instance loaded from the file
- Return type:
LegendreCovariance
- Raises:
TypeError – If the file contains MF33 data instead of MF34
- classmethod from_endf(file_path: str | Path, energy_unit: str = 'eV') LegendreCovariance[source]
Create a LegendreCovariance instance from an ENDF file containing MF34 data.
This is a convenience class method that reads an ENDF file, extracts MF34 (angular distribution covariance) data, and converts it to a LegendreCovariance object.
- Parameters:
- Returns:
LegendreCovariance instance with angular distribution covariance data
- Return type:
LegendreCovariance
- Raises:
FileNotFoundError – If the file does not exist
ValueError – If the file does not contain MF34 data
Examples
>>> mf34_covmat = LegendreCovariance.from_endf('path/to/endf_file.txt') >>> print(f"Loaded {mf34_covmat.num_matrices} angular covariance matrices") >>> # Or specify MeV if needed >>> mf34_covmat_mev = LegendreCovariance.from_endf('path/to/file.txt', energy_unit='MeV')
Notes
This method internally: 1. Reads the ENDF file using read_endf() 2. Extracts MF34 data 3. Converts to LegendreCovariance using the to_ang_covmat() method
See also
read_endfFunction to read ENDF files
- get_ll_prime_correlations(isotope: int, mt: int) Dict[str, Any][source]
Extract per-energy l-l’ correlation blocks from ENDF MF34 data.
For each energy bin, builds the L×L covariance sub-block from all stored (l_row, l_col) sub-matrices that share the same energy grid, then converts to correlation.
- Parameters:
- Returns:
‘energy_grid’: np.ndarray — bin boundaries (N+1 points) ‘l_values’: list of int — sorted Legendre orders present ‘correlations’: list of np.ndarray — one L×L correlation matrix per energy bin ‘mean_abs_offdiag’: float — mean |off-diagonal| across all bins
- Return type:
- get_uncertainties_for_legendre_coefficient(isotope: int, mt: int, l_coefficient: int | List[int]) Dict[str, ndarray] | None | Dict[int, Dict[str, ndarray] | None][source]
Extract standard uncertainties (square root of diagonal variance) for Legendre coefficient(s).
IMPORTANT: MF34 data is typically stored as RELATIVE covariances (fractional uncertainties δA_ℓ/A_ℓ). This method returns the uncertainties as stored in MF34, along with an ‘is_relative’ flag.
To convert relative uncertainties to absolute: σ_abs = σ_rel × |A_ℓ| where A_ℓ are the Legendre coefficients from ENDF MF=4 data.
- Parameters:
- Returns:
- For single int: Dictionary containing:
’energies’: np.ndarray - Energy bin boundaries (N+1 points for N bins) in eV or MeV
’uncertainties’: np.ndarray - Uncertainties (√diagonal of covariance) for each bin
’is_relative’: bool - True if relative (δA_ℓ/A_ℓ), False if absolute (δA_ℓ)
For list of ints: Dictionary mapping L coefficient to uncertainty data (or None if not found).
- Return type:
Notes
If is_relative=True, you must convert to absolute uncertainties by multiplying by the Legendre coefficients A_ℓ from ENDF MF=4 before using in propagation formulas.
The LB flag in ENDF-6 format determines if data is relative (LB=1,2,5) or absolute (LB=0).
Energies are returned as BIN BOUNDARIES, not bin centers. Each uncertainty value applies to the energy bin defined by consecutive boundary pairs [E[i], E[i+1]).
- get_union_energy_grids()[source]
- has_uniform_energy_grid() bool[source]
Check if all matrices have the same energy grid.
- Returns:
True if all energy grids are identical, False otherwise. Returns True for empty collections (vacuous truth).
- Return type:
- property isotopes: Set[int]
Get the set of unique isotope IDs in the covariance matrices.
- Returns:
Sorted list of unique isotope IDs
- Return type:
Set[int]
- property legendre_indices: Set[int]
Get the set of unique Legendre coefficient indices in the covariance matrices.
- Returns:
Sorted list of unique Legendre coefficient indices
- Return type:
Set[int]
- property log_covariance_matrix: ndarray
Return the log-space covariance matrix. Converts relative covariance to log-space using log1p transformation.
- magnitude_cross_summary(isotope: int, mt: int, *, atol: float = 1e-12) Dict[str, Any][source]
Describe the a_0 (L=0) blocks this carrier holds for one section.
ENDF-6 Sec. 34.1 lets a file express the covariance between the cross section magnitude and the Legendre coefficients through the a_0 coefficient, even though a_0 = 1 in the ENDF system, and Sec. 34.2 reserves LTT=3 for a section where L or L1 = 0 appears anywhere. Sec. 34.3 then says the (0, 0) magnitude self-block belongs to MF33 and must not be repeated here, so a conforming file writes it null.
That null is exactly what makes the L=0 row unplottable on its own:
correlation_matrixdivides bysqrt(C_00), so every entry of the L=0 row and column becomes NaN, including the (0, L1) cross terms that do carry data.attach_magnitude_covariance()repairs it by supplying the missing self-block from MF33.A file may also declare LTT=3 and then write the cross blocks as numerical zero – ENDF/B-VIII.1 does this for U-235 and U-238, where VIII.0 carried real terms.
cross_is_nullseparates the two so a caller can say “this file has no cross terms” instead of drawing an empty block.- Parameters:
- Returns:
present– any (0, L1) or (L, 0) block exists.self_present– the (0, 0) block exists.self_is_null– (0, 0) is zero, i.e. MF33 still owes it.cross_orders– sorted L1 with a (0, L1) block, 0 excluded.cross_max_abs– largest absolute cross-term value.cross_is_null– every cross block is zero to within atol.- Return type:
- property num_matrices: int
Get the number of covariance matrices stored.
- Returns:
Number of matrices
- Return type:
- plot_covariance_heatmap(nuclide: int | str, mt: int, legendre_coeffs: int | List[int] | Tuple[int, int], ax: Axes | None = None, *, matrix_type: str = 'corr', figsize: Tuple[float, float] = (6, 6), dpi: int = 300, font_family: str = 'serif', vmax: float | None = None, vmin: float | None = None, show_uncertainties: bool = False, cmap: any | None = None, scale: str = 'log', energy_range: Tuple[float, float] | None = None, title: str | None = 'default', **imshow_kwargs) Figure[source]
Draw a covariance or correlation matrix heatmap for MF34 angular distribution data.
- Parameters:
nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)
mt (int) – Reaction MT number
legendre_coeffs (int, list of int, or tuple of (row_l, col_l)) – Legendre coefficient(s). Can be: - Single int: diagonal block for that L - List of ints: diagonal blocks for those L values - Tuple of (row_l, col_l): off-diagonal block between row and column L
ax (plt.Axes, optional) – Matplotlib axes to draw into (deprecated, only used when show_uncertainties=False)
matrix_type (str, default "corr") – Type of matrix to plot: “corr”/”correlation” for correlation matrix, or “cov”/”covariance” for covariance matrix
figsize (tuple) – Figure size in inches (width, height)
dpi (int) – Dots per inch for figure resolution
font_family (str) – Font family for text elements
vmax (float, optional) – Color scale limits
vmin (float, optional) – Color scale limits
show_uncertainties (bool) – Whether to show uncertainty plots above the heatmap
cmap (str or matplotlib.colors.Colormap, optional) – Colormap to use for the heatmap. Can be a string name of any matplotlib colormap (e.g., ‘viridis’, ‘plasma’, ‘RdYlBu’, ‘coolwarm’) or a matplotlib Colormap object. If None, defaults to ‘RdYlGn’ for correlation matrices and ‘viridis’ for covariance matrices.
scale (str, default "log") – Energy axis scale: “log”/”logarithmic” or “lin”/”linear”
energy_range (tuple of float, optional) – Energy range (min, max) for filtering. Values in eV.
title (str or None, default "default") – Plot title. If “default”, auto-generates from nuclide, MT, and Legendre coefficient. If a string, uses that as the title. If None, suppresses the title.
**imshow_kwargs – Additional arguments passed to imshow (deprecated)
- Returns:
The matplotlib figure containing the heatmap and optional uncertainty plots
- Return type:
plt.Figure
- plot_uncertainties(isotope: int, mt: int, legendre_coeffs: int | List[int], ax: Axes | None = None, *, uncertainty_type: str = 'relative', style: str = 'default', figsize: Tuple[float, float] = (8, 5), dpi: int = 100, font_family: str = 'serif', legend_loc: str = 'best', energy_range: Tuple[float, float] | None = None, **kwargs) Figure[source]
Plot uncertainties for MF34 angular distribution data for specific Legendre coefficients.
This method extracts and plots the diagonal uncertainties from the covariance matrix for the specified isotope, MT reaction, and Legendre coefficients.
- Parameters:
isotope (int) – Isotope ID
mt (int) – Reaction MT number
legendre_coeffs (int or list of int) – Legendre coefficient(s) to plot uncertainties for. Can be a single int or a list of ints.
ax (plt.Axes, optional) – Matplotlib axes to draw into. If None, creates new figure.
uncertainty_type (str, default "relative") – Type of uncertainty to plot: “relative” (%) or “absolute”
style (str, default "default") – Plot style: ‘default’, ‘dark’, ‘paper’, ‘publication’, ‘presentation’
figsize (tuple, default (8, 5)) – Figure size in inches (width, height)
dpi (int, default 100) – Dots per inch for figure resolution
font_family (str, default "serif") – Font family for text elements
legend_loc (str, default "best") – Legend location
energy_range (tuple of float, optional) – Energy range (min, max) for x-axis. If None, uses the full data range. Values are used directly without clamping to data range.
**kwargs – Additional arguments passed to matplotlib plot functions
- Returns:
The matplotlib figure containing the uncertainty plots
- Return type:
plt.Figure
Examples
Plot relative uncertainties for Legendre coefficients L=1,2,3:
>>> fig = mf34_covmat.plot_uncertainties(isotope=92235, mt=2, ... legendre_coeffs=[1, 2, 3]) >>> fig.show()
Plot absolute uncertainties for a single Legendre coefficient:
>>> fig = mf34_covmat.plot_uncertainties(isotope=92235, mt=2, ... legendre_coeffs=1, ... uncertainty_type="absolute") >>> fig.show()
- property reactions: Set[int]
Get the set of unique reaction MT numbers in the covariance matrices.
- Returns:
Sorted list of unique reaction MT numbers
- Return type:
Set[int]
- remap_mt(old_mt: int, new_mt: int) LegendreCovariance[source]
Return a copy with all occurrences of old_mt replaced by new_mt.
This is useful when NJOY GENDF files use different MT numbering (e.g. MT=251 for elastic scattering) that needs to be mapped back to the standard ENDF MT numbers (e.g. MT=2).
- summary() DataFrame[source]
Create a summary DataFrame with one row per matrix.
- Returns:
DataFrame with columns: isotope_row, reaction_row, L_row, isotope_col, reaction_col, L_col, NE (len(energy_grid)), M (NE-1), is_relative, frame
- Return type:
pd.DataFrame
- svd_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, full_matrices: bool = False, logger=None) Tuple[ndarray, ndarray, ndarray][source]
SVD with PSD pre-processing.
- Parameters:
space (str) – “linear” or “log” space for decomposition
clip_negatives (bool) – Deprecated. Use psd_method instead.
psd_method (str or None) – “auto” (default), “higham”, “clip”, or “none”.
verbose (bool) – Whether to log progress
full_matrices (bool) – Whether to return full-sized U and V matrices
logger (optional) – Logger instance for output
- Returns:
U, singular values, V^T matrices
- Return type:
Tuple[np.ndarray, np.ndarray, np.ndarray]
- to_covfil(file_path: str | Path, tape_label: str = '', temperature: float = 0.0) None[source]
Write this LegendreCovariance to an NJOY COVFIL/GENDF text file.
- to_dataframe() DataFrame[source]
Convert the angular covariance matrix data to a pandas DataFrame.
- Returns:
DataFrame containing the covariance matrix data with columns: ISO_H, REAC_H, L_H, ISO_V, REAC_V, L_V, ENE, STD
- Return type:
pd.DataFrame
- to_endf(source_endf: str | Path, output_path: str | Path, isotope: int, mt: int, *, replace_existing: bool = True, update_directory: bool = True, **mf34_overrides) str[source]
Write a single (isotope, MT) MF34 section into an ENDF file.
Convenience wrapper that calls
to_mf34()to build the section andkika.endf.writers.write_mf34_to_file()to splice it into a template file. Extra keyword arguments are forwarded toto_mf34()(za,awr,mat,ltt,frame).
- to_heatmap_data(nuclide: int | str, mt: int, legendre_coeffs: int | List[int] | Tuple[int, int], *, matrix_type: str = 'corr', scale: str = 'log', energy_range: Tuple[float, float] | None = None, **kwargs) LegendreHeatmapData[source]
Prepare MF34 covariance heatmap data for PlotBuilder rendering.
This method handles the complex MF34 matrix structure including per-Legendre energy grids and prepares data for visualization.
- Parameters:
nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)
mt (int) – MT reaction number
legendre_coeffs (int, list of int, or tuple of (row_l, col_l)) – Legendre coefficient(s). Can be: - Single int: diagonal block for that L - List of ints: diagonal blocks for those L values - Tuple of (row_l, col_l): off-diagonal block between row and column L
matrix_type (str, default 'corr') – Type of matrix: ‘corr’/’correlation’ for correlation matrix, or ‘cov’/’covariance’ for covariance matrix
scale (str, default 'log') – Energy axis scale: ‘log’/’logarithmic’ or ‘lin’/’linear’
energy_range (tuple of float, optional) – Energy window (emin, emax). Only bins overlapping the window are kept.
**kwargs – Additional parameters (reserved for future use)
- Returns:
Heatmap data object ready for PlotBuilder.add_heatmap()
- Return type:
LegendreHeatmapData
Examples
>>> # Simple usage with PlotBuilder >>> from kika.plotting import PlotBuilder >>> heatmap_data = mf34_covmat.to_heatmap_data( ... nuclide=92235, mt=2, legendre_coeffs=[1, 2, 3] ... ) >>> fig = PlotBuilder(style='light').add_heatmap(heatmap_data) >>> fig.show()
>>> # Can also use string symbols >>> heatmap_data = mf34_covmat.to_heatmap_data( ... nuclide='U235', mt=2, legendre_coeffs=1, matrix_type='cov' ... )
- to_mf34(isotope: int, mt: int, *, za: float | None = None, awr: float | None = None, mat: int | None = None, ltt: int | None = None, frame: str | None = None) MF34MT[source]
Build an MF34MT from the entries tagged
(isotope, mt).Matrices with
isotope_row == isotopeandreaction_row == mtare grouped byreaction_col(MT1) into one subsection each, and within a subsection by(L, L1)into one sub-subsection per pair. For symmetric self-correlation (MT1 == MT) only the upper triangleL <= L1is emitted. Diagonal blocks (L == L1) are written as LB=5 and off-diagonal blocks as LB=6.- Parameters:
isotope (int) – Selector keys; matrices with matching row tags are included.
mt (int) – Selector keys; matrices with matching row tags are included.
za (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from
self.mt_metadata[(isotope, mt)]if available, then from sensible defaults (za=isotope,awr=1.0,mat=0,lttinferred — 2 if any L=0 pair is present, else 1).awr (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from
self.mt_metadata[(isotope, mt)]if available, then from sensible defaults (za=isotope,awr=1.0,mat=0,lttinferred — 2 if any L=0 pair is present, else 1).mat (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from
self.mt_metadata[(isotope, mt)]if available, then from sensible defaults (za=isotope,awr=1.0,mat=0,lttinferred — 2 if any L=0 pair is present, else 1).ltt (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from
self.mt_metadata[(isotope, mt)]if available, then from sensible defaults (za=isotope,awr=1.0,mat=0,lttinferred — 2 if any L=0 pair is present, else 1).frame (optional) – Override the per-matrix frame (LCT) for every emitted sub-subsection. Accepts
"same-as-MF4","LAB", or"CM".
- Return type:
MF34MT
Notes
Multiple LIST records (NI > 1) per sub-subsection in the original file are collapsed to a single LB=5/LB=6 record on the matrix’s stored grid; the numeric content is preserved but the original per-record split is not.
- to_plot_data(nuclide: int | str, mt: int, order: int, sigma: float = 1.0, uncertainty_type: str = 'relative', label: str = None, **styling_kwargs)[source]
Create a PlotData object for Legendre coefficient uncertainties.
This is a convenience method to easily convert MF34 covariance data into a plottable format using the new plotting infrastructure.
- Parameters:
nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)
mt (int) – Reaction MT number
order (int) – Legendre polynomial order
sigma (float, default 1.0) – Sigma level for uncertainty scaling (e.g., 1.0 for 1σ, 2.0 for 2σ)
uncertainty_type (str, default 'relative') – Type of uncertainty: ‘relative’ (%) or ‘absolute’
label (str, optional) – Custom label for the plot. If None, auto-generates from isotope and order. Note: Energy values are returned in eV (native ENDF-6 format) to ensure compatibility when combining with MF4 data.
**styling_kwargs – Additional styling kwargs (color, linestyle, linewidth, etc.)
- Returns:
Tuple containing: - coeff_data: Legendre coefficient data if available in
legendre_coefficients, else None - unc_data: Uncertainty data for the Legendre coefficients- Return type:
tuple of (LegendreCoeffPlotData or None, LegendreUncertaintyPlotData)
- Raises:
ValueError – If uncertainty data is not available for the specified parameters
Examples
>>> # Extract uncertainty data from LegendreCovariance - both notation styles work >>> mf34_covmat = endf.mf[34].mt[2].to_ang_covmat() >>> coeff_data, unc_data = mf34_covmat.to_plot_data(nuclide=26056, mt=2, order=1) >>> # Note: coeff_data will be None (MF34 only has uncertainties, not values) >>> >>> # Build a plot with just uncertainties >>> from kika.plotting import PlotBuilder >>> fig = PlotBuilder().add_data(unc_data).build()
- kika.cov.MF34CovMat
alias of
LegendreCovariance
- class kika.cov.CrossSectionCovariance(num_groups: int = 0, energy_grid: ~typing.List[float] | None = None, energy_grids: ~typing.List[~typing.List[float]] = <factory>, isotope_rows: ~typing.List[int] = <factory>, reaction_rows: ~typing.List[int] = <factory>, isotope_cols: ~typing.List[int] = <factory>, reaction_cols: ~typing.List[int] = <factory>, matrices: ~typing.List[~numpy.ndarray] = <factory>, is_relative: ~typing.List[bool] = <factory>, cross_sections: ~typing.Dict[~typing.Tuple[int, int], ~numpy.ndarray] = <factory>, energy_unit: str = 'eV', metadata: ~typing.Dict[str, ~typing.Any] = <factory>)[source]
Bases:
objectFormat-agnostic multigroup cross-section covariance.
- num_groups
Number of energy groups in the covariance matrices
- Type:
- energy_grid
List of energy grid boundaries (if available)
- Type:
Optional[List[float]]
- isotope_rows
List of row isotope IDs
- Type:
List[int]
- reaction_rows
List of row reaction MT numbers
- Type:
List[int]
- isotope_cols
List of column isotope IDs
- Type:
List[int]
- reaction_cols
List of column reaction MT numbers
- Type:
List[int]
- matrices
List of covariance matrices (one for each row-column combination)
- Type:
List[np.ndarray]
- energy_unit
Energy unit for energy_grid: ‘eV’ (default) or ‘MeV’
- Type:
- add_matrix(isotope_row: int, reaction_row: int, isotope_col: int, reaction_col: int, matrix: ndarray, energy_grid: List[float] | None = None, is_relative: bool | None = None) None[source]
Add a covariance matrix to the collection.
- Parameters:
isotope_row (int) – Row isotope ID
reaction_row (int) – Row reaction MT number
isotope_col (int) – Column isotope ID
reaction_col (int) – Column reaction MT number
matrix (np.ndarray) – Covariance matrix
energy_grid (list of float, optional) – Energy grid for this matrix. If not provided, validates against
self.num_groups(multigroup mode).is_relative (bool, optional) – Whether the matrix is relative (True) or absolute (False).
- cholesky_decomposition(*, space: str = 'log', psd_method: str = 'auto', jitter_scale: float = 1e-10, max_jitter_ratio: float = 0.001, verbose: bool = True, logger=None) ndarray[source]
Robust Cholesky factor L such that M ≈ L Lᵀ.
- clean_cov(isotope: int) CrossSectionCovariance[source]
Return a new CrossSectionCovariance containing only sub-matrices for isotope, always dropping reaction 1 and applying the mid/high-range rules:
4 → 51-91, 103 → 600-649, 104 → 650-699, 105 → 700-749, 106 → 750-799, 107 → 800
- property clipped_correlation_matrix: ndarray
Return the correlation matrix clipped to [-1, 1] range.
Diagonal elements are forced to 1.0, undefined entries become NaN.
- Returns:
Correlation matrix with values clipped to valid range [-1, 1]
- Return type:
np.ndarray
- copy() CrossSectionCovariance[source]
Return a deep copy of this CrossSectionCovariance instance.
- Returns:
Deep copy of the current covariance matrix object
- Return type:
CrossSectionCovariance
- property correlation_matrix: ndarray
Return the correlation matrix (unclipped).
Diagonal elements are forced to 1.0, undefined entries become NaN. No off-diagonal correction is applied.
- Returns:
Correlation matrix with no clipping applied
- Return type:
np.ndarray
- property covariance_matrix: ndarray
Return the full covariance matrix of shape (N·G) × (N·G), where N = number of unique (iso,rxn) blocks and G = num_groups.
If
ensure_psd()has been called, the cached PSD matrix is returned directly.
- eigen_block_contributions(idx: int | None = None, which: str = 'min', top_n: int = 10, tol: float = 1e-12, symmetric: bool = True, relative: bool = False) Dict[str, Any][source]
Measure how each (block_i, block_j) sub-matrix of the covariance matrix contributes to a chosen eigenvalue λ.
- Parameters:
idx (Index of the eigenvalue to inspect (overrides which).)
which (If idx is None, choose ‘max’ or ‘min’ eigenvalue.)
top_n (Return only the top_n largest |contribution| entries.)
tol (Skip blocks with |contribution| ≤ tol.)
symmetric (If True, include only pairs with i ≤ j (avoids duplicates).)
relative (If True, report each block’s share of |λ|.)
- Returns:
‘index’ : eigenvalue index used. ‘eigenvalue’ : eigenvalue λ. ‘contributions’: list of dicts:
- Return type:
A dict with
- eigen_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, logger=None) Tuple[ndarray, ndarray][source]
Eigendecomposition with PSD correction.
- energy_unit: str = 'eV'
- ensure_psd(*, preserve_diagonal: bool = True, max_iter: int = 1000, tol: float = 1e-10, eigval_floor: float = 0.0, verbose: bool = True, logger=None) Tuple[CrossSectionCovariance, Dict[str, Any]][source]
Return a copy whose
covariance_matrixis the nearest PSD matrix.Uses Higham’s alternating-projection algorithm. The original sub-matrices are not modified; instead, the assembled PSD matrix is cached and returned by the
covariance_matrix/log_covariance_matrixproperties.- Parameters:
preserve_diagonal (bool) – If True, preserve the original variances (diagonal elements).
max_iter (int) – Maximum number of Higham iterations.
tol (float) – Convergence tolerance on relative Frobenius change.
eigval_floor (float) – Floor for eigenvalues in the PSD projection step.
verbose (bool) – Whether to print diagnostic information.
logger (optional) – Logger instance for file output.
- Returns:
(psd_copy, info) where psd_copy has the PSD cache set and info is the diagnostic dict from
nearest_psd_higham.- Return type:
Tuple[CrossSectionCovariance, dict]
- filter_by_isotope(isotope: int) CrossSectionCovariance[source]
Return a new CrossSectionCovariance containing only sub-matrices and cross-sections for the given isotope. All reactions for that isotope are retained.
Similar to clean_cov, but without dropping any reactions.
- filter_by_isotopes(isotopes: Sequence[int]) CrossSectionCovariance[source]
Return a new CrossSectionCovariance containing only matrices where BOTH row and column isotopes are in the specified list. Preserves cross-isotope blocks.
- Parameters:
isotopes (Sequence[int]) – List of isotope ZAIDs to include
- Returns:
Filtered CrossSectionCovariance containing only matrices involving the specified isotopes
- Return type:
CrossSectionCovariance
- filter_by_reactions(mts: Sequence[int], isotope: int | None = None) CrossSectionCovariance[source]
Return a new CrossSectionCovariance containing only matrices whose row and column reactions are both in mts. When per-matrix
energy_gridsare available (e.g. ENDF MF33), the returned object hasnum_groupsandenergy_gridset from the diagonal block of the first requested MT so that all plotting methods work directly.
- fix_covariance(*, level: str = 'soft', high_val_thresh: float = 5.0, clamp_target: float = 1.0, accept_tol: float = -0.0001, clamp_max_iter: int = 10, max_steps: int = 40, verbose: bool = True, clamp_detail: bool = False, logger=None) Tuple[CrossSectionCovariance, Dict[str, Any]][source]
Clean the covariance matrix until it is positive-(semi)definite.
- Parameters:
level – ‘soft’ - clamp variances only ‘medium’ - clamp then drop the worst block pairs ‘hard’ - clamp then drop the worst reactions (all blocks)
high_val_thresh – Diagonal variances above this threshold are clamped.
clamp_target – Target variance assigned to clamped diagonals (off-diagonals rescaled by sqrt(target/|old|) to preserve correlations).
accept_tol – Minimum eigenvalue tolerated for acceptance.
clamp_max_iter – Maximum clamping passes before switching strategy.
max_steps – Maximum block-removal iterations (if used).
verbose – Forwarded to the removal routine.
clamp_detail – Dump every off-diagonal before/after pair on each clamp event. Very noisy — opt in only to debug a specific clamp.
logger – Optional logger instance for file output. If None, uses print().
- classmethod from_boxer(file_path: str | Path, energy_unit: str = 'eV') CrossSectionCovariance[source]
Create a CrossSectionCovariance instance from a BOXER card-image covariance file.
- Parameters:
- Returns:
CrossSectionCovariance instance loaded from the file
- Return type:
CrossSectionCovariance
See also
read_boxerUnderlying function that performs the parsing
- classmethod from_covariance_section(section, nuclide: int = 0, mt: int | None = None, energy_unit: str = 'eV') CrossSectionCovariance[source]
Create a CrossSectionCovariance from one GNDS §25.2 covariance section.
The model is treated as one more source format here, on the same footing as GENDF, COVFIL, BOXER and COVERX — which is what the model layer is for. The immediate reason it exists is MF35: its bands reach the model directly (
decodeMF35MT) and never pass through this class, so before this there was no way to hand one toplot_covariance_heatmap. It is not MF35-specific: any section with a matrix and a row grid works, which includes MF31, MF33 and MF34.One section, one matrix, on purpose. The bands of a single MF35 file have different orders (84, 641, 641, 641, 641 on ENDF/B-VIII.1 U-235) and all carry the same MT, so a whole suite loaded into one object would hold several matrices indistinguishable by the
(nuclide, mt)key every selector here uses, and a plot would quietly draw the first. Loop over the sections and call this per band instead.- Parameters:
section – A
CovarianceSection: anything exposingform.matrix,form.rowGridandform.isRelative, with an optionalrowData.ENDF_MFMT. Duck-typed rather than imported so thatkika.covkeeps not depending on the model.nuclide (int, optional) – ZAID to file the matrix under. The model identifies a covariance by its href rather than by a material header, so there is nothing to read this from and it is the caller’s to supply.
mt (int, optional) – Reaction number. Defaults to the MT half of
rowData.ENDF_MFMT.energy_unit (str, optional) – Unit of
form.rowGrid. The model keeps ENDF’s native eV.
- Returns:
A single-matrix instance, ready for
plot_covariance_heatmap.- Return type:
CrossSectionCovariance
Examples
>>> suite, _ = decodeCovarianceSuite(endf) >>> band = CrossSectionCovariance.from_covariance_section( ... suite.covarianceSections[0], nuclide=92235) >>> plot_covariance_heatmap(band, nuclide=92235, mt=18)
- classmethod from_coverx(file_path: str | Path, ascending: bool = True, energy_unit: str = 'eV') CrossSectionCovariance[source]
Create a CrossSectionCovariance instance from a COVERX covariance file (text or binary).
- Parameters:
- Returns:
CrossSectionCovariance instance loaded from the file
- Return type:
CrossSectionCovariance
See also
read_coverxUnderlying function that performs the parsing
- classmethod from_covfil(file_path: str | Path, energy_unit: str = 'eV') CrossSectionCovariance[source]
Create a CrossSectionCovariance instance from an NJOY-generated COVFIL/GENDF file.
- Parameters:
- Returns:
CrossSectionCovariance instance loaded from the file
- Return type:
CrossSectionCovariance
- Raises:
TypeError – If the file contains MF34 data instead of MF33
See also
read_covfilUnderlying function that performs the parsing
- classmethod from_gendf(file_path: str | Path, energy_unit: str = 'eV') CrossSectionCovariance[source]
Create a CrossSectionCovariance instance from an NJOY-generated GENDF covariance file.
This is a convenience class method that wraps the read_njoy_covmat function to provide a more object-oriented API consistent with other data classes.
- Parameters:
- Returns:
CrossSectionCovariance instance loaded from the file
- Return type:
CrossSectionCovariance
Examples
>>> covmat = CrossSectionCovariance.from_gendf('path/to/file.gendf') >>> print(f"Loaded {covmat.num_matrices} matrices") >>> # Or specify MeV if the file uses MeV >>> covmat_mev = CrossSectionCovariance.from_gendf('path/to/file.gendf', energy_unit='MeV')
See also
read_covfilUnderlying function that performs the parsing
- get_cov_connections(nuclide: int | str, mt: int, *, depth: int = 1, require_nonzero: bool = True, tol: float = 0.0, max_neighbors: int | None = None) Dict[str, Any][source]
Main method for your UI graph.
Returns a JSON-friendly payload with: - center node - nodes + edges within BFS depth - (if depth >= 2) a grouped mapping of second-level nodes per first-level neighbor,
ideal for your “mini nodes next to each neighbor”.
- Parameters:
depth (int) – 1 -> show center + its neighbors 2 -> also include neighbors-of-neighbors
require_nonzero (bool) – True: only create edges if the covariance block has any |value| > tol False: edge exists if the block exists (even if all zeros)
tol (float) – Threshold for nonzero detection when require_nonzero=True
max_neighbors (Optional[int]) – If set, cap the number of neighbors expanded per node (safety for huge graphs)
- get_uncertainty(zaid: int, mt: int, energy_mev: float | None = None) float | ndarray[source]
Get relative uncertainty (standard deviation / mean) for a specific isotope and reaction.
The covariance matrices store relative covariances, so the square root of the diagonal elements directly gives relative uncertainties (as fractions).
- Parameters:
- Returns:
Relative uncertainty (as fraction, e.g., 0.05 for 5%). If energy_mev is provided, returns a single float. If energy_mev is None, returns array of uncertainties for all groups.
- Return type:
float or np.ndarray
- Raises:
ValueError – If the specified (zaid, mt) pair is not found in the covariance data
- property isotopes: Set[int]
Get the set of unique isotope IDs in the covariance matrices.
- Returns:
Set of unique isotope IDs
- Return type:
Set[int]
- list_cov_nodes(*, require_nonzero: bool = False, tol: float = 0.0) List[Dict[str, Any]][source]
Return all nodes (zaid, mt) known to the covariance store. If require_nonzero=True, nodes are restricted to those that participate in at least one non-zero block (or have a non-zero diagonal block).
- property log_covariance_matrix: ndarray
Return the log-space covariance matrix.
Converts relative covariance to log-space using log1p transformation.
- Returns:
Log-space covariance matrix
- Return type:
np.ndarray
- num_groups: int = 0
- property num_matrices: int
Get the number of covariance matrices stored.
- Returns:
Number of matrices
- Return type:
- plot_covariance_heatmap(nuclide: int | str, mt: int | Sequence[int] | Tuple[int, int], ax: Axes = None, *, matrix_type: str = 'corr', figsize: Tuple[float, float] = (6, 6), dpi: int = 300, font_family: str = 'serif', vmax: float = None, vmin: float = None, show_uncertainties: bool = True, scale: str = 'log', energy_range: Tuple[float, float] | None = None, title: str | None = 'default', **imshow_kwargs) Figure[source]
Draw a covariance or correlation matrix heatmap for a specified isotope and MT reaction(s).
This method now uses the modern PlotBuilder-based implementation from kika.plotting.covariance for cleaner, more maintainable code.
- Parameters:
nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)
mt (int, sequence of int, or tuple of (row_mt, col_mt)) – MT reaction number(s). Can be: - Single int: diagonal block for that MT - Sequence of ints: diagonal blocks for those MTs - Tuple of (row_mt, col_mt): off-diagonal block between row and column MT
ax (plt.Axes, optional) – Matplotlib axes to draw into (deprecated, kept for compatibility)
matrix_type (str, default "corr") – Type of matrix to plot: “corr”/”correlation” for correlation matrix, or “cov”/”covariance” for covariance matrix
figsize (tuple) – Figure size in inches (width, height)
dpi (int) – Dots per inch for figure resolution
font_family (str) – Font family for text elements
vmax (float, optional) – Color scale limits
vmin (float, optional) – Color scale limits
show_uncertainties (bool) – Whether to show uncertainty plots above the heatmap
scale (str, default "log") – Energy axis scale: “log”/”logarithmic” or “lin”/”linear”
energy_range (tuple of float, optional) – Energy range (min, max) for filtering. Values in eV.
title (str or None, default "default") – Plot title. If “default”, auto-generates from nuclide and MT. If a string, uses that as the title. If None, suppresses the title.
**imshow_kwargs – Additional arguments passed to imshow (deprecated)
- Returns:
The matplotlib figure containing the heatmap and optional uncertainty plots
- Return type:
plt.Figure
- plot_multigroup_xs(nuclide: int | str | Sequence[int | str], mt: int | Sequence[int], ax: Axes = None, *, energy_range: Tuple[float, float] | None = None, show_uncertainties: bool = False, sigma: float = 1.0, style: str = 'default', figsize: Tuple[float, float] = (8, 5), dpi: int = 300, font_family: str = 'serif', legend_loc: str = 'best', xscale: str = 'log', yscale: str = 'linear', title: str | None = 'default', **step_kwargs) Figure[source]
Plot multigroup cross sections with optional uncertainty bands.
This method now uses the modern PlotBuilder-based implementation from kika.plotting.covariance for cleaner, more maintainable code.
- plot_uncertainties(nuclide: int | str | Sequence[int | str], mt: int | Sequence[int], *, energy_range: Tuple[float, float] | None = None, style: str = 'default', figsize: Tuple[float, float] = (8, 5), dpi: int = 300, font_family: str = 'serif', legend_loc: str = 'best', xscale: str = 'log', yscale: str = 'linear', title: str | None = 'default', **step_kwargs) Figure[source]
Plot relative uncertainties for one or more (ZAID, MT) pairs.
This method now uses the modern PlotBuilder-based implementation from kika.plotting.covariance for cleaner, more maintainable code.
- project_to_grid(target_bin_edges_ev: ndarray, *, xs_source: Any | None = None, target_mt: int | None = None, target_isotope: int | None = None) ndarray[source]
Project a self-self covariance entry onto a target bin-edge grid.
Selects the matrix where
reaction_row == reaction_col == target_mt(defaulting to the most common MT in the covariance) and projects every matching self-self contribution onto the target grid viakika.processing.multigroup(piecewise-constant overlap).- Parameters:
target_bin_edges_ev (np.ndarray) – Target bin edges in eV (length
N+1, strictly increasing).xs_source (MF3MT or CrossSection, optional) – Pointwise σ(E) used to convert any relative-form matrices to absolute. Required when at least one matched entry has
is_relative=True; otherwise that entry is skipped with a warning.target_mt (int, optional) – MT to look up. Defaults to the first
reaction_rowsvalue.target_isotope (int, optional) – Isotope (ZA) to look up. Defaults to the first
isotope_rowsvalue.
- Returns:
Absolute covariance (b²), shape
(N, N), symmetric.- Return type:
np.ndarray
Notes
PSD enforcement is the caller’s responsibility. Use
kika.cov.decomposition.cholesky_decompositiondownstream when a positive-definite factor is needed.
- property reactions: Set[int]
Get the set of unique reaction MT numbers in the covariance matrices.
- Returns:
Set of unique reaction MT numbers
- Return type:
Set[int]
- reactions_by_isotope(isotope: int | None = None) Dict[int, List[int]] | List[int][source]
Get a mapping of isotopes to their available reactions, or list of reactions for a specific isotope.
- remove_matrix(isotope: int, reaction_pairs: List[Tuple[int, int]], exceptions: List[Tuple[int, int]] | None = None) CrossSectionCovariance[source]
Return a new CrossSectionCovariance without the specified matrices for a given isotope, but always keep any pairs listed in exceptions. Also removes cross section entries for diagonal or wildcard reactions that are removed.
- report_large_values(threshold: float = 1.0, top_n: int = 30, return_text: bool = False) Tuple[str, dict] | None[source]
Scan each block of the relative covariance matrices and generate a detailed report of entries that exceed the specified threshold.
If return_text is True, returns a tuple: (report_text, summary_dict) where summary_dict contains: - zaid: the main isotope ZAID - name: human-readable symbol - count: number of entries > threshold - max_value: largest flagged value
- sanitize_by_correlation(*, max_abs_corr: float = 1.0, zero_threshold: float = 1.5, report_tol: float = 1e-06, eigen_floor: float = 1e-12, project_psd: bool = True, psd_method: str = 'auto', verbose: bool = True)[source]
Clip out-of-range correlations, zero out huge outliers, inspect eigenvalues, and (optionally) project to PSD.
- Parameters:
max_abs_corr – Any correlation with |ρ| > max_abs_corr (but ≤ zero_threshold) is clipped to ±max_abs_corr.
zero_threshold – Any |ρ| > zero_threshold is set to 0.
report_tol – Minimum |old–new| change to actually log.
eigen_floor – Floor for eigenvalues when projecting to PSD.
project_psd – Whether to do the PSD projection here.
verbose – Print detailed diagnostics.
- svd_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, full_matrices: bool = False, logger=None) Tuple[ndarray, ndarray, ndarray][source]
SVD with PSD pre-processing.
- to_boxer(file_path: str | Path, hlibid: str = '', hdescr: str = '', nvf: int = 10) None[source]
Write this CrossSectionCovariance to a BOXER card-image (ASCII) file.
- to_coverx(file_path: str | Path, fmt: str = 'binary', title: str = '') None[source]
Write this CrossSectionCovariance to a COVERX covariance file (text or binary).
- to_covfil(file_path: str | Path, tape_label: str = '', temperature: float = 0.0) None[source]
Write this CrossSectionCovariance to an NJOY COVFIL/GENDF text file.
- to_dataframe() DataFrame[source]
Convert the covariance matrix data to a pandas DataFrame.
Includes an extra row at the beginning to store the energy grid if available.
- Returns:
DataFrame containing the covariance matrix data with columns: ISO_H, REAC_H, ISO_V, REAC_V, STD
- Return type:
pd.DataFrame
- to_heatmap_data(nuclide: int | str | Sequence[int | str], mt: int | Sequence[int] | Tuple[int, int], *, matrix_type: str = 'corr', scale: str = 'log', energy_range: Tuple[float, float] | None = None, **kwargs) CovarianceHeatmapData[source]
Prepare covariance heatmap data for PlotBuilder rendering.
This method extracts the relevant matrix data, computes uncertainties, and packages everything into a CovarianceHeatmapData object that can be rendered by PlotBuilder.add_heatmap().
- Parameters:
nuclide (int, str, or sequence of int/str) – Isotope identifier(s). Can be: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’) - List of ZAIDs or strings for multi-isotope heatmaps (e.g., [‘Fe54’, ‘Fe56’])
mt (int, sequence of int, or tuple of (row_mt, col_mt)) – MT reaction number(s). Can be: - Single int: diagonal block for that MT - Sequence of ints: diagonal blocks for those MTs - Tuple of (row_mt, col_mt): off-diagonal block between row and column MT
matrix_type (str, default 'corr') – Type of matrix: ‘corr’/’correlation’ for correlation matrix, or ‘cov’/’covariance’ for covariance matrix
scale (str, default 'log') – Energy axis scale: ‘log’/’logarithmic’ or ‘lin’/’linear’
energy_range (tuple of float, optional) – Energy window (emin, emax). Only bins overlapping the window are kept.
**kwargs – Additional parameters (reserved for future use)
- Returns:
Heatmap data object ready for PlotBuilder.add_heatmap()
- Return type:
CovarianceHeatmapData
Examples
>>> # Simple usage with PlotBuilder >>> from kika.plotting import PlotBuilder >>> heatmap_data = covmat.to_heatmap_data(nuclide=92235, mt=[2, 18, 102]) >>> fig = PlotBuilder(style='light').add_heatmap(heatmap_data) >>> fig.show()
>>> # Can also use string symbols >>> heatmap_data = covmat.to_heatmap_data(nuclide='U235', mt=2, matrix_type='cov')
>>> # Multi-isotope heatmap >>> heatmap_data = covmat.to_heatmap_data(nuclide=['Fe54', 'Fe56'], mt=[2, 18])
- to_plot_data(nuclide: int | str, mt: int, sigma: float = 1.0, label: str = None, **styling_kwargs)[source]
Create PlotData objects for multigroup cross sections with uncertainties.
This unified method extracts both nominal cross section data and uncertainty data. Both are returned as PlotData objects that can be plotted independently or combined.
- Parameters:
nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)
mt (int) – Reaction MT number
sigma (float, optional) – Number of sigma levels for uncertainty bands (default: 1.0 for 1σ).
label (str, optional) – Custom label for the plot. If None, auto-generates from ZAID and MT.
**styling_kwargs – Additional styling kwargs (color, linestyle, linewidth, etc.)
- Returns:
xs_data: Cross section data for plotting (or None if not available)
unc_data: Uncertainty data as percentages (or None if not available)
- Return type:
tuple of (MultigroupCrossSectionPlotData, MultigroupUncertaintyPlotData)
- Raises:
ValueError – If the specified (nuclide, mt) pair is not found in either cross sections or covariance data
Examples
>>> # Extract data - both integer ZAID and string notation work >>> covmat = read_njoy_covmat('file.gendf') >>> xs_data, unc_data = covmat.to_plot_data(nuclide=26056, mt=2) >>> xs_data, unc_data = covmat.to_plot_data(nuclide='Fe56', mt=2) >>> >>> # Use with PlotBuilder: >>> from kika.plotting import PlotBuilder >>> >>> # Option 1: Plot just cross sections >>> fig1 = PlotBuilder().add_data(xs_data).build() >>> >>> # Option 2: Plot cross sections with uncertainty shading >>> fig2 = PlotBuilder().add_data(xs_data, uncertainty=unc_data).build() >>> >>> # Option 3: Plot just uncertainties as a line >>> fig3 = PlotBuilder().add_data(unc_data).build()
- transfer_reactions(source: CrossSectionCovariance, reactions: Sequence[Tuple[int, int]], *, cross_correlation: str = 'both-present', grid_atol: float = 1e-06, grid_rtol: float = 1e-06, replace: bool = True) TransferResult[source]
Copy selected reaction blocks from
sourceinto a copy ofself.selfis the destination and is never mutated; a newCrossSectionCovarianceis returned inside theTransferResult.- Parameters:
source (CrossSectionCovariance) – The covariance to take blocks from.
reactions (sequence of (isotope_zaid, mt)) – Reactions to transfer. For each, the diagonal self-block and the associated cross-section vector are copied, plus off-diagonal cross-correlation blocks according to
cross_correlation.cross_correlation ({'both-present', 'diagonal-only', 'always'}) –
How to handle off-diagonal blocks of a transferred reaction:
'both-present'(default): copy a cross block only when both partner reactions exist in the destination after the transfer; otherwise record it incross_dropped.'diagonal-only': never copy cross blocks.'always': copy every cross block the reaction participates in, even if the partner reaction is absent (may leave dangling refs).
grid_atol (float) – Tolerances for the energy-grid equality check (after eV/MeV normalisation).
grid_rtol (float) – Tolerances for the energy-grid equality check (after eV/MeV normalisation).
replace (bool) – If True, a transferred reaction that already exists in the destination has its existing blocks/cross-section removed first.
- Returns:
The merged covariance plus a report of what was transferred/dropped.
- Return type:
TransferResult
- Raises:
ValueError – If the group structures are incompatible, or
cross_correlationis not a recognised mode.
- kika.cov.CovMat
alias of
CrossSectionCovariance
- class kika.cov.TransferResult(covariance: ~kika.cov.cross_section_covariance.CrossSectionCovariance, diagonal_transferred: ~typing.List[~typing.Tuple[int, int]] = <factory>, cross_transferred: ~typing.List[~typing.Tuple[int, int, int, int]] = <factory>, cross_dropped: ~typing.List[~typing.Tuple[~typing.Tuple[int, int, int, int], str]] = <factory>, reactions_replaced: ~typing.List[~typing.Tuple[int, int]] = <factory>, cross_sections_transferred: ~typing.List[~typing.Tuple[int, int]] = <factory>, missing_in_source: ~typing.List[~typing.Tuple[int, int]] = <factory>)[source]
Bases:
objectOutcome of
CrossSectionCovariance.transfer_reactions().- covariance
The merged covariance (a new object; inputs are not mutated).
- Type:
CrossSectionCovariance
- diagonal_transferred
Self-blocks copied from the source.
- Type:
list of (isotope, mt)
- cross_transferred
Off-diagonal cross-correlation blocks copied from the source.
- Type:
list of (iso_row, mt_row, iso_col, mt_col)
- cross_dropped
Cross blocks that were not copied, with the reason.
- Type:
list of ((iso_row, mt_row, iso_col, mt_col), reason)
- reactions_replaced
Destination reactions whose existing blocks were removed before re-adding.
- Type:
list of (isotope, mt)
- cross_sections_transferred
Reactions whose associated cross-section vector was copied.
- Type:
list of (isotope, mt)
- missing_in_source
Requested reactions not found in the source.
- Type:
list of (isotope, mt)
- covariance: CrossSectionCovariance
- kika.cov.nearest_psd_higham(A: ndarray, *, preserve_diagonal: bool = True, max_iter: int | None = None, tol: float | None = None, eigval_floor: float = 0.0, verbose: bool = True, logger=None, log_every: int = 50) Tuple[ndarray, Dict][source]
Find the nearest positive semi-definite matrix using Higham’s alternating projection algorithm with Dykstra’s correction.
When
preserve_diagonal=Truethe algorithm iterates between projecting onto the PSD cone and restoring the original diagonal (variances), converging to the nearest PSD matrix that keeps all variances unchanged.Reference: N.J. Higham, “Computing a nearest symmetric positive semidefinite matrix”, Linear Algebra and its Applications, 1988.
- Parameters:
A (np.ndarray) – Input symmetric matrix (need not be PSD).
preserve_diagonal (bool) – If True, run the full Dykstra iteration that preserves the diagonal. If False, perform a single eigenvalue-clipping projection.
max_iter (int, optional) – Maximum number of alternating-projection iterations.
None(default) auto-selects: 1000 for n ≤ 2000, 200 for n > 2000. Sparse multigroup covariances (n > 2000, ~95 % non-finite from below-threshold reactions) plateau by iter ~100 and the cholesky path then falls back to clip — running 1000 iterations just burns wall-time without improving the projection.tol (float, optional) – Convergence tolerance on relative Frobenius change between iterations.
None(default) auto-selects: 1e-10 for matrices ≤ 1000×1000, 1e-8 for larger ones — at n ≳ 1000, rel_change ≲ 1e-8 is already at float-precision noise from the O(n³) eigendecomposition, so a stricter tol just guarantees no convergence.eigval_floor (float) – Floor for eigenvalues in the PSD projection step (0.0 for exact PSD, small positive for strict PD).
verbose (bool) – Whether to print diagnostic information.
logger (optional) – Logger instance for file output.
log_every (int) – Emit a progress line every N iterations (set to 0 to disable). Used to track convergence on long runs where each iteration is an O(n³) eigendecomposition.
- Returns:
(X_psd, info) where info contains diagnostic metadata: - iterations, converged, frobenius_distance, relative_frobenius_error,
max_diagonal_change, eigenvalue_range_before, eigenvalue_range_after, n_negative_eigenvalues_before
- Return type:
Tuple[np.ndarray, dict]
- kika.cov.verify_psd_projection(original_matrix: ndarray, projected_matrix: ndarray, info: Dict, verbose: bool = True, logger=None) Tuple[float, float, float][source]
Verify PSD projection quality by comparing original and projected matrices.
Reports Frobenius distance, maximum diagonal change, and maximum correlation change between the original and projected matrices.
- Parameters:
- Returns:
(relative_frobenius_error_pct, max_diagonal_change_pct, max_correlation_change)
- Return type:
Submodules
kika.cov.parse_covmat
- exception kika.cov.parse_covmat.EmptyParsingError[source]
Bases:
ExceptionRaised when no data was extracted during parsing.
- exception kika.cov.parse_covmat.InvalidDataFormatError[source]
Bases:
ExceptionRaised when the data format is invalid or corrupted.
- kika.cov.parse_covmat.read_coverx(file_path: str, ascending: bool = True, energy_unit: str = 'eV') CrossSectionCovariance[source]
Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.
The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.
- Parameters:
- Returns:
Parsed covariance data
- Return type:
CrossSectionCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found in the file
InvalidDataFormatError – If the binary structure is corrupted
- kika.cov.parse_covmat.write_coverx(data: CrossSectionCovariance, file_path: str, fmt: str = 'binary', title: str = '', endian: str = '>') None[source]
Write a
CrossSectionCovarianceto a COVERX covariance file (text or binary).
- kika.cov.parse_covmat.read_boxer(file_path: str, energy_unit: str = 'eV') CrossSectionCovariance[source]
Read a BOXER card-image (ASCII) covariance file and return a CrossSectionCovariance.
BOXER is produced by NJOY’s COVR module. It stores energy boundaries, cross-section vectors, standard-deviation vectors, and covariance or correlation matrices in a compressed card-image format.
- kika.cov.parse_covmat.write_boxer(data: CrossSectionCovariance, file_path: str, hlibid: str = '', hdescr: str = '', nvf: int = 10) None[source]
Write a
CrossSectionCovarianceto a BOXER card-image (ASCII) file.- Parameters:
data (CrossSectionCovariance) – Covariance data to write.
file_path (str) – Output file path.
hlibid (str, optional) – Library identifier (3 chars max). Default
''.hdescr (str, optional) – Description (32 chars max). Default
''.nvf (int, optional) – Value format code (7–14). Default 10 (
1P8E10.3).
- kika.cov.parse_covmat.read_covfil(file_path: str, energy_unit: str = 'eV') CrossSectionCovariance | LegendreCovariance[source]
Parse an NJOY-generated COVFIL/GENDF covariance file.
Returns a
CrossSectionCovariancefor MF33 files (cross-section covariances) or anLegendreCovariancefor MF34 files (angular-distribution covariances).MF3 cross sections are stored in
CrossSectionCovariance.cross_sections(MF33 case only).- Parameters:
- Returns:
Parsed covariance data.
- Return type:
CrossSectionCovariance or LegendreCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found.
- kika.cov.parse_covmat.write_covfil(data: CrossSectionCovariance | LegendreCovariance, file_path: str, tape_label: str = '', temperature: float | None = None, awr: float | None = None) None[source]
Write a
CrossSectionCovarianceorLegendreCovarianceto an NJOY COVFIL/GENDF text file.- Parameters:
data (CrossSectionCovariance or LegendreCovariance) – Covariance data to write.
file_path (str) – Output file path.
tape_label (str, optional) – Label for the tape header line (max 66 chars). Default
''.temperature (float, optional) – Temperature in K written into MF1 MT451 CONT record. If
None(default), usesdata.metadata['temperature'](set byread_covfil()), falling back to0.0.awr (float, optional) – Atomic weight ratio written into MF1 MT451 HEAD and each MF33/MF34 section HEAD. If
None(default), usesdata.metadata['awr'](set byread_covfil()), falling back to0.0.
- kika.cov.parse_covmat.read_scale_covmat(file_path: str, ascending: bool = True, energy_unit: str = 'eV') CrossSectionCovariance
Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.
The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.
- Parameters:
- Returns:
Parsed covariance data
- Return type:
CrossSectionCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found in the file
InvalidDataFormatError – If the binary structure is corrupted
- kika.cov.parse_covmat.read_njoy_covmat(file_path: str, energy_unit: str = 'eV') CrossSectionCovariance | LegendreCovariance
Parse an NJOY-generated COVFIL/GENDF covariance file.
Returns a
CrossSectionCovariancefor MF33 files (cross-section covariances) or anLegendreCovariancefor MF34 files (angular-distribution covariances).MF3 cross sections are stored in
CrossSectionCovariance.cross_sections(MF33 case only).- Parameters:
- Returns:
Parsed covariance data.
- Return type:
CrossSectionCovariance or LegendreCovariance
- Raises:
EmptyParsingError – If no valid covariance matrices were found.
kika.cov.covmat
Backward-compatibility shim — real code lives in cross_section_covariance.py.
kika.cov.decomposition
Shared matrix decomposition methods for covariance matrix classes.
This module provides decomposition functionality that can be used by both CrossSectionCovariance and LegendreCovariance classes without code duplication.
- class kika.cov.decomposition.CovarianceMatrixProtocol(*args, **kwargs)[source]
Bases:
ProtocolProtocol defining the interface required for decomposition methods.
- property covariance_matrix: ndarray
Return the linear-space covariance matrix.
- property log_covariance_matrix: ndarray
Return the log-space covariance matrix.
- kika.cov.decomposition.clip_projection(matrix: ndarray, *, floor: float = 0.0, symmetrise: bool = True, passthrough_when_clean: bool = False, eigvals: ndarray | None = None, eigvecs: ndarray | None = None, verbose: bool = True, logger=None, label: str = 'clip') Tuple[ndarray, Dict][source]
V·max(Λ, floor)·Vᵀ— the PSD projection, written down once.Four copies of these three lines were in the tree: two inside
nearest_psd_higham(), one insideclip_and_rescale(), one insidesvd_decomposition(), and a fifth inkika.cov.conditioning._clipped(). Consolidating them turned up something worth stating rather than quietly averaging away: they were not the same projection. They differ in three ways, each of which is load-bearing at the last bits, so each is a parameter here and every call site keeps exactly the arithmetic it had.floor. Higham and
clip_rescaleclip toeigval_floor(a small positive number), the sampling paths clip to0.0. A strictly positive floor makes the result positive definite rather than semi-definite, which is what a Cholesky downstream needs and what an SVD does not.symmetrise.
(V·Λ·Vᵀ + ·ᵀ)/2is not a no-op: the product is mathematically symmetric and numerically is not, and Higham’s inner loop deliberately skips the fold-up because Dykstra’s correction is taken on the raw projection.passthrough_when_clean.
svd_decompositionleaves a matrix with no negative eigenvalue untouched rather than rebuilding it from its own eigendecomposition. That is what makespsd_method="clip"a true no-op on an already-PSD matrix, and it is whatkika.cov.conditioning.apply_plan()has to reproduce for a conditioned matrix drawn withpsd_method="none"to be bit-identical to the same matrix drawn withpsd_method="clip".
A @ np.diag(w)andA * w[None, :]are bit-identical for finite A — the gemm adds exact zeros — so the two spellings the copies used are not a fourth difference, and the faster one is used here.- Parameters:
eigvals – A decomposition of matrix already in hand, reused rather than recomputed. Callers that need the eigendecomposition for their own reasons —
psd_method="auto", which resolves on the spectrum, and Higham’s loop, which counts SVD fallbacks — pass it in.eigvecs – A decomposition of matrix already in hand, reused rather than recomputed. Callers that need the eigendecomposition for their own reasons —
psd_method="auto", which resolves on the spectrum, and Higham’s loop, which counts SVD fallbacks — pass it in.
- Returns:
infocarriesn_negative,min_eigenvalueandclipped(False when passthrough_when_clean returned the input unchanged).- Return type:
(matrix, info)
- kika.cov.decomposition.cap_variance_congruence(cov_mat: ndarray, max_variance: float, *, param_pairs=None, num_groups: int = 0, bins=None, verbose: bool = True, logger=None, label: str = '') Tuple[ndarray, Dict][source]
Cap diagonal variances via congruence transform, preserving correlations and PSD.
For each diagonal entry
σ²_i > max_variance, compute a scale factors_i = sqrt(max_variance / σ²_i)and apply the one-shot congruence transformΣ_capped = diag(s) @ Σ @ diag(s).- Parameters:
cov_mat (np.ndarray) – Covariance matrix (modified in-place is NOT done; a copy is returned).
max_variance (float) – Maximum allowed diagonal variance.
param_pairs (list of (zaid, mt) tuples, optional) – For logging which parameters were capped.
num_groups (int) – Number of energy groups per reaction (for index→group mapping).
bins (array-like, optional) – Energy bin edges (for logging energy ranges).
verbose (logging controls.)
logger (logging controls.)
label (logging controls.)
- Returns:
cov_capped (np.ndarray) – Capped covariance matrix.
info (dict) –
n_capped,capped_entrieslist,max_original_variance.
- kika.cov.decomposition.flag_threshold_bins(cov_mat: ndarray, mt_thresholds: Dict[int, float], param_pairs: Sequence[Tuple[int, int]], num_groups: int, bins: Sequence[float], *, space: str = 'log', min_groups_for_median: int = 3, verbose: bool = True, logger=None, label: str = '') Tuple[List[int], List[float], List[Dict]][source]
Identify threshold-spanning bins from per-MT reaction thresholds (e.g., extracted from ACE σ(E)) and compute a per-bin replacement variance.
For each (zaid, mt) entry in
param_pairswhose MT appears inmt_thresholds, locate the unique groupgsuch thatbins[g] <= E_thresh < bins[g+1]. That bin straddles the reaction threshold and is the canonical NJOY artifact location: ⟨σ⟩ is pulled down by the below-threshold tail, blowing up the relative uncertainty.The replacement target for that bin’s diagonal variance is the median of the diagonal over the same MT’s other groups (finite, positive). Returns indices and targets only — actual rescaling is performed by
rescale_threshold_bins_congruence.- Parameters:
cov_mat ((n,n) np.ndarray) – Covariance matrix (linear or log space; matching
space).mt_thresholds (dict) –
{mt: E_threshold}in the same energy units asbins(MeV here).param_pairs (list of (zaid, mt) tuples) – Maps row/col blocks to (isotope, reaction).
num_groups (int) – Energy groups per (zaid, mt) block.
bins (array-like) – Group boundaries, length
num_groups + 1.space ({"log", "linear"}) – Only used in the log lines (interpretation of σ²).
min_groups_for_median (int) – Skip a flagged bin when fewer than this many same-MT neighbors are usable; the bin remains for the global cap to handle.
- Returns:
flagged_indices (list of int) – Flat covariance indices for bins flagged as threshold-spanning.
targets (list of float) – Target variance per flagged index (one-to-one with
flagged_indices).detection_log (list of dict) – Per-flagged-bin detail (zaid, mt, group, energy_lo, energy_hi, E_thresh, current_variance, target_variance, n_groups_used).
- kika.cov.decomposition.rescale_threshold_bins_congruence(cov_mat: ndarray, flagged_indices: Sequence[int], targets: Sequence[float], *, param_pairs=None, num_groups: int = 0, bins=None, verbose: bool = True, logger=None, label: str = '') Tuple[ndarray, Dict][source]
Rescale specific diagonal entries to per-index target variances via a congruence transform. One-sided: never inflates a variance.
- For each flagged index
iwith targett_i: - s_i = sqrt(t_i / σ²_i) if σ²_i > t_i
1.0 otherwise
Then
Σ' = diag(s) @ Σ @ diag(s). PSD-preserving, correlation-preserving.This complements
cap_variance_congruence(which applies a single global cap): use this for targeted rescaling of bins identified as artifacts (e.g. NJOY threshold-spanning bins flagged byflag_threshold_bins).- Parameters:
cov_mat ((n,n) np.ndarray) – Covariance matrix. A copy is returned; input is not modified.
flagged_indices (sequence of int) – Flat covariance indices to (potentially) rescale.
targets (sequence of float) – Target variance for each flagged index, one-to-one with
flagged_indices.param_pairs (optional) – For per-entry logging (zaid/mt/group/energy ranges).
num_groups (optional) – For per-entry logging (zaid/mt/group/energy ranges).
bins (optional) – For per-entry logging (zaid/mt/group/energy ranges).
verbose (logging controls.)
logger (logging controls.)
label (logging controls.)
- Returns:
cov_rescaled (np.ndarray) – Rescaled covariance.
info (dict) –
n_flagged,n_actually_rescaled,rescaled_entries(list of dicts with index, zaid, mt, group, energy_lo, energy_hi, original_variance, target_variance, scale_factor).
- For each flagged index
- kika.cov.decomposition.flag_outlier_variance_bins(cov_mat: ndarray, param_pairs: Sequence[Tuple[int, int]], num_groups: int, bins: Sequence[float], *, outlier_factor: float = 1000.0, min_groups_for_median: int = 4, skip_indices: Sequence[int] | None = None, space: str = 'log', verbose: bool = True, logger=None, label: str = '') Tuple[List[int], List[float], List[Dict]][source]
Flag diagonal entries σ²_i >
outlier_factor× per-MT median(σ²) as statistical outliers. Target = per-MT median.Catches evaluator-written placeholder/filler variances that aren’t near a reaction threshold (so
flag_threshold_binswon’t catch them) and would otherwise saturate the global variance cap. Concrete failure mode: JEFF-4.0 Mn-55 MT=856 (lumped n,α ladder) carries σ²_rel ~ 10⁹ in bins above ~22 MeV where σ̄ collapses; the data is parsed as already-relative so the σ̄-floor in the abs→rel conversion never sees it.Same return signature as
flag_threshold_binssorescale_threshold_bins_congruenceconsumes the output unchanged.outlier_factoris intentionally large (default 1000) so only physically-impossible outliers are caught — bins at 5–10× the median represent legitimate heterogeneity in measurement quality and are left alone.- Parameters:
cov_mat ((n,n) np.ndarray)
param_pairs (list of (zaid, mt) tuples)
num_groups (int)
bins (array-like, length
num_groups + 1)outlier_factor (float) – Bins with σ² >
outlier_factor× per-MT median are flagged.min_groups_for_median (int) – Minimum same-MT bins (excluding the flagged one) required to compute the median target. If fewer, the bin is not flagged.
skip_indices (sequence of int, optional) – Flat indices to exclude from outlier consideration entirely (typically those already rescaled by
flag_threshold_bins).space (same as
flag_threshold_bins.)verbose (same as
flag_threshold_bins.)logger (same as
flag_threshold_bins.)label (same as
flag_threshold_bins.)
- kika.cov.decomposition.nearest_psd_higham(A: ndarray, *, preserve_diagonal: bool = True, max_iter: int | None = None, tol: float | None = None, eigval_floor: float = 0.0, verbose: bool = True, logger=None, log_every: int = 50) Tuple[ndarray, Dict][source]
Find the nearest positive semi-definite matrix using Higham’s alternating projection algorithm with Dykstra’s correction.
When
preserve_diagonal=Truethe algorithm iterates between projecting onto the PSD cone and restoring the original diagonal (variances), converging to the nearest PSD matrix that keeps all variances unchanged.Reference: N.J. Higham, “Computing a nearest symmetric positive semidefinite matrix”, Linear Algebra and its Applications, 1988.
- Parameters:
A (np.ndarray) – Input symmetric matrix (need not be PSD).
preserve_diagonal (bool) – If True, run the full Dykstra iteration that preserves the diagonal. If False, perform a single eigenvalue-clipping projection.
max_iter (int, optional) – Maximum number of alternating-projection iterations.
None(default) auto-selects: 1000 for n ≤ 2000, 200 for n > 2000. Sparse multigroup covariances (n > 2000, ~95 % non-finite from below-threshold reactions) plateau by iter ~100 and the cholesky path then falls back to clip — running 1000 iterations just burns wall-time without improving the projection.tol (float, optional) – Convergence tolerance on relative Frobenius change between iterations.
None(default) auto-selects: 1e-10 for matrices ≤ 1000×1000, 1e-8 for larger ones — at n ≳ 1000, rel_change ≲ 1e-8 is already at float-precision noise from the O(n³) eigendecomposition, so a stricter tol just guarantees no convergence.eigval_floor (float) – Floor for eigenvalues in the PSD projection step (0.0 for exact PSD, small positive for strict PD).
verbose (bool) – Whether to print diagnostic information.
logger (optional) – Logger instance for file output.
log_every (int) – Emit a progress line every N iterations (set to 0 to disable). Used to track convergence on long runs where each iteration is an O(n³) eigendecomposition.
- Returns:
(X_psd, info) where info contains diagnostic metadata: - iterations, converged, frobenius_distance, relative_frobenius_error,
max_diagonal_change, eigenvalue_range_before, eigenvalue_range_after, n_negative_eigenvalues_before
- Return type:
Tuple[np.ndarray, dict]
- kika.cov.decomposition.clip_and_rescale(M: ndarray, *, eigval_floor: float = 0.0, verbose: bool = False, logger=None) Tuple[ndarray, Dict[str, Any]][source]
Clip negative eigenvalues, then put the stated diagonal back.
The gap this closes.
cliprebuildsV·clip(Λ,0)·Vᵀ, which preserves the eigenvectors — the reason it is the right projection for these matrices, since the near-null direction carries a sum rule — but not the diagonal. Every clipped negative eigenvalue adds variance, spread over the components in proportion to its eigenvector, so a component the evaluation gives almost no variance comes out of the projection with some. Downstream that is not cosmetic: PFNS divides each drawn delta by its group probability, and the lowest groups of a fission spectrum hold ~1e-17 of the total, so a negligible absolute gain becomes a large perturbation ratio. The standing workaround is a 5σ clamp ingenerate_pfns_samples.Higham is the third option and it is not the answer either, though the reason first written here was wrong. “Did not finish four 122×122 bands in two minutes” was contention on a loaded box: re-measured quiet, one band converges in ~1900 iterations and ~13 s. Three numbers rule it out instead.
It preserves the diagonal in absolute norm — the post-loop eigenvalue clip below runs without restoring it, leaving
max_diagonal_changeat 1.4e-12 against a largest stated variance of 7.4e-5. Negligible, and the code says so. But the same 1.4e-12 is 0.55% of the smallest stated variances, and on a covariance whose diagonal spans decades that is the sense that matters.clip_rescaleis exact in both senses.It degrades
C·1by ~40x (4.2e-6 → 1.7e-4) whereclipimproves it.It costs ~10⁴x
clip.
Middling on both axes for four orders of magnitude more time, so
clipstays on the PFNS path.kika.cov.conditioning.predict_psd_repairs()measures all three on any given matrix, which is how these numbers were got.The fix is one line of algebra. With
d = sqrt(diag(C₀)/diag(C_clip)), the matrixdiag(d)·C_clip·diag(d)has exactlydiag(C₀)on its diagonal, is a congruence transform of a PSD matrix and therefore still PSD, and costs O(n²) against Higham’s iterations. It does not preserve the eigenvectors exactly — nothing that changes the diagonal can — but it rescales rather than rotates, so a component with zero stated variance gets an identically zero row and column, which is stronger than whatclipgives it.Measured, and it does not do what it was proposed for. On the four Cf-252 MF35 bands the diagonal comes back exactly (max error 1.6e-8 → 1.4e-20) and the result stays PSD (λ_min ≈ -5e-20). But the sum rule does not survive it: the row-sum residual
max|Σ_j C_ij| / max|C|goes from 4.2e-6 to 2.2e-3, some 500x worse, on every band.That is structural rather than a tolerance to tune.
C·1 ≈ 0says the all-ones vector is a near-null eigenvector, and clipping keeps it null precisely because it preserves eigenvectors. The congruence gives(D C D)·1 = D C (D 1), which is zero only whendis constant — so restoring a non-uniform diagonal and preserving the sum rule are, for these matrices, the same choice made two ways.The 5σ clamp in
generate_pfns_samplestherefore stays: the rescale roughly halves the draws that reach it (172→113, 220→121, 274→127, 277→124 over the four bands at 64 samples) and removes none of them. Use this where the marginals matter and no sum rule does — MF33 and MF34 — and not on a normalised spectrum.Not reachable from
psd_method="auto": this is a different numerical answer, and the pipelines already running onclipmust not change underneath because a new option was added. Ask for it by name.- Returns:
infocarriesn_negative,min_eigenvalue,max_diagonal_error_beforeandmax_diagonal_error_after, so a caller can assert the restoration actually happened.- Return type:
(matrix, info)
- kika.cov.decomposition.cholesky_decomposition(cov_obj: CovarianceMatrixProtocol = None, *, space: str = 'log', psd_method: str = 'auto', jitter_scale: float = 1e-10, max_jitter_ratio: float = 0.001, verbose: bool = True, logger=None, matrix: ndarray = None) ndarray[source]
Robust Cholesky factor L such that M ≈ L L^T.
- Parameters:
cov_obj (CovarianceMatrixProtocol, optional) – Object containing covariance matrix data.
space (str) – “linear” or “log” space for decomposition
psd_method (str) –
PSD enforcement method when the matrix is not already PD: - “auto” (default): one eigendecomposition; if |λ_min|/λ_max is
below the module threshold use clip, otherwise Higham.
”higham”: iterative diagonal-preserving projection.
”clip”: zero negative eigenvalues, reconstruct, then Cholesky.
”none”: add diagonal jitter until Cholesky succeeds.
jitter_scale (float) – Base jitter scale for PSD correction
max_jitter_ratio (float) – Maximum jitter relative to matrix norm
verbose (bool) – Whether to log progress
logger (optional) – Logger instance for output
matrix (np.ndarray, optional) – If provided, decompose this matrix directly instead of extracting from cov_obj. Useful when the matrix has been pre-processed (e.g. variance-capped).
- Returns:
Lower triangular Cholesky factor L
- Return type:
np.ndarray
- kika.cov.decomposition.eigen_decomposition(cov_obj: CovarianceMatrixProtocol = None, *, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, logger=None, matrix: ndarray = None) Tuple[ndarray, ndarray][source]
Eigendecomposition with PSD correction.
- Parameters:
cov_obj (CovarianceMatrixProtocol, optional) – Object containing covariance matrix data
space (str) – “linear” or “log” space for decomposition
clip_negatives (bool) – Deprecated. Use psd_method instead. Kept for backward compatibility.
psd_method (str or None) – PSD correction method: “auto” (default), “higham”, “clip”, or “none”. If None, resolved from clip_negatives: True → “auto”, False → “none”. “auto” picks clip when |λ_min|/λ_max is below the module threshold, otherwise Higham.
verbose (bool) – Whether to log progress
logger (optional) – Logger instance for output
matrix (np.ndarray, optional) – If provided, decompose this matrix directly instead of extracting from cov_obj.
- Returns:
Eigenvalues and eigenvectors
- Return type:
Tuple[np.ndarray, np.ndarray]
- kika.cov.decomposition.svd_decomposition(cov_obj: CovarianceMatrixProtocol = None, *, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, full_matrices: bool = False, logger=None, matrix: ndarray = None) Tuple[ndarray, ndarray, ndarray][source]
SVD with PSD pre-processing.
- Parameters:
cov_obj (CovarianceMatrixProtocol, optional) – Object containing covariance matrix data
space (str) – “linear” or “log” space for decomposition
clip_negatives (bool) – Deprecated. Use psd_method instead. Kept for backward compatibility.
psd_method (str or None) – PSD correction method: “auto” (default), “higham”, “clip”, or “none”. If None, resolved from clip_negatives: True → “auto”, False → “none”. “auto” picks clip when |λ_min|/λ_max is below the module threshold, otherwise Higham.
verbose (bool) – Whether to log progress
full_matrices (bool) – Whether to return full-sized U and V matrices
logger (optional) – Logger instance for output
matrix (np.ndarray, optional) – If provided, decompose this matrix directly instead of extracting from cov_obj.
- Returns:
U, singular values, V^T matrices
- Return type:
Tuple[np.ndarray, np.ndarray, np.ndarray]
- kika.cov.decomposition.compute_correlation(cov_obj: CovarianceMatrixProtocol, *, clip: bool = False, force_diagonal: bool = True) ndarray[source]
Compute correlation matrix from covariance matrix.
- Parameters:
- Returns:
Correlation matrix with optional clipping and diagonal forcing
- Return type:
np.ndarray
- kika.cov.decomposition.verify_cholesky_decomposition(original_matrix: ndarray, L: ndarray, space: str, verbose: bool = True, logger=None) Tuple[float, float, float][source]
Verify Cholesky decomposition quality by reconstructing L @ L.T.
- Parameters:
- Returns:
Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)
- Return type:
- kika.cov.decomposition.verify_eigen_decomposition(original_matrix: ndarray, eigvals: ndarray, eigvecs: ndarray, space: str, verbose: bool = True, logger=None) Tuple[float, float, float][source]
Verify eigendecomposition quality by reconstructing V @ Λ @ V.T.
- Parameters:
- Returns:
Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)
- Return type:
- kika.cov.decomposition.verify_svd_decomposition(original_matrix: ndarray, U: ndarray, S: ndarray, Vt: ndarray, space: str, verbose: bool = True, logger=None) Tuple[float, float, float][source]
Verify SVD quality by reconstructing U @ Σ @ V.T.
- Parameters:
original_matrix (np.ndarray) – Original covariance matrix
U (np.ndarray) – Left singular vectors
S (np.ndarray) – Singular values
Vt (np.ndarray) – Right singular vectors (transposed)
space (str) – Space (“linear” or “log”) for logging
verbose (bool) – Whether to log results
logger (optional) – Logger instance for output
- Returns:
Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)
- Return type:
- kika.cov.decomposition.verify_pca_decomposition(original_matrix: ndarray, eigvals: ndarray, eigvecs: ndarray, k: int, space: str, verbose: bool = True, logger=None) Tuple[float, float, float][source]
Verify PCA decomposition quality by reconstructing the truncated approximation.
- Parameters:
original_matrix (np.ndarray) – Original covariance matrix
eigvals (np.ndarray) – All eigenvalues (sorted descending)
eigvecs (np.ndarray) – All eigenvectors (sorted by descending eigenvalue)
k (int) – Number of components kept in truncation
space (str) – Space (“linear” or “log”) for logging
verbose (bool) – Whether to log results
logger (optional) – Logger instance for output
- Returns:
Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)
- Return type:
- kika.cov.decomposition.verify_psd_projection(original_matrix: ndarray, projected_matrix: ndarray, info: Dict, verbose: bool = True, logger=None) Tuple[float, float, float][source]
Verify PSD projection quality by comparing original and projected matrices.
Reports Frobenius distance, maximum diagonal change, and maximum correlation change between the original and projected matrices.
- Parameters:
- Returns:
(relative_frobenius_error_pct, max_diagonal_change_pct, max_correlation_change)
- Return type: