ptyrad.analysis#
User-facing analysis of PtyRAD model.hdf5 outputs.
The main entry point is Analyzer. The module-level extract_*
functions in ptyrad.analysis.extract are re-exported here so callers
can work with a raw dict from ptyrad.io.load.load_ptyrad() without
instantiating the class — useful when only a sub-dict is on hand or when
processing many files in a streaming loop.
The geometry helpers (get_probe_center_positions(),
get_scanned_fov_bbox(), apply_fov()) are public on purpose:
they encode the top-left-vs-center crop_pos convention that is the
most error-prone part of the saved file format.
- class ptyrad.analysis.Analyzer(source)[source]#
Bases:
objectWrapper around a loaded PtyRAD
model.hdf5reconstruction.- Parameters:
source (str | os.PathLike | dict) – Either a path to a
.hdf5/.h5reconstruction output (the file is loaded viaptyrad.io.load.load_ptyrad()) or an already-loaded dict. When constructed from a dict the analyzer cannot retrieve HDF5 root attributes (provenance); that path returnsNonefromprovenancewith a one-timeUserWarning.
Notes
Construction is cheap: only the HDF5 datasets/groups are loaded into a nested dict (provenance is deferred, the
PtychoModelis not built untilbuild_model()is called).The class is a thin delegator: every
get_*method is a one-line forward to the corresponding pure function inptyrad.analysis.extract, so users who prefer raw dicts can skip the class entirely and call the functions directly.- build_model(device='cuda', overrides=None)[source]#
Rebuild a
PtychoModelfrom the savedparamsand weights.Runs
ptyrad.init.initializer.Initializeragainstdata['params']['init_params']to construct theinit_variablesdict, instantiates the model, copies the savedoptimizable_tensors(obja,objp,probe,probe_pos_shifts,obj_tilts,slice_thickness) into the fresh model, and finally restores the iteration histories (loss_iters,dz_iters,lr_iters,avg_tilt_iters,convergence_iters) so the rebuilt model is plottable viaplot_summary()/plot_forward_pass()and suitable for forward-pass diagnostics.The model and its
init_variablesare cached onselfafter a successful build.- Parameters:
device (str) – Torch device string (e.g.
'cpu','cuda','cuda:0').overrides (Mapping[str, Any] | None) –
Dict deep-merged into
paramsbefore initialization. The common use is to redirect the measurement file path when the original measurement data has moved:a.build_model( overrides={'init_params': {'meas_params': {'path': '/new/path.raw'}}} )
- Raises:
FileNotFoundError – If
init_params['meas_params']['path']does not resolve. The error names the missing path explicitly rather than failing deep in the dataloader.
- property data: dict#
- property dk: float#
- property dx: float#
- forward(indices, *, return_raw=False, enable_grad=False)[source]#
Run a forward pass on the rebuilt model.
Thin wrapper around
self.model.forward(indices, return_raw=...). Requiresbuild_model()to have been called first;indicesis coerced tonp.ndarraybefore being passed through.By default this runs under
torch.no_grad()because analysis and plotting calls usually do not need autograd graphs. Setenable_grad=Trueto record gradients for offline custom losses or refinement experiments.- Parameters:
return_raw (bool)
enable_grad (bool)
- get_fov_bbox()[source]#
Inclusive scanned-FOV bbox over probe-center positions.
Returns
(y_min, y_max, x_min, x_max)matching the slicing of the saved*_cropTIFFs.- Return type:
tuple[int, int, int, int]
- get_loss_curves()[source]#
Loss-history fields; see
extract.extract_loss_curves().- Return type:
dict
- get_object(fov='full', *, as_torch=False, device='cpu')[source]#
Complex object
obja * exp(1j * objp); seeextract.extract_object().- Parameters:
fov (Literal['full', 'crop'])
as_torch (bool)
device (str)
- get_object_amplitude(fov='full', *, as_torch=False, device='cpu')[source]#
Object amplitude
obja; seeextract.extract_object_amplitude().- Parameters:
fov (Literal['full', 'crop'])
as_torch (bool)
device (str)
- get_object_phase(fov='full', *, as_torch=False, device='cpu')[source]#
Object phase
objp; seeextract.extract_object_phase().- Parameters:
fov (Literal['full', 'crop'])
as_torch (bool)
device (str)
- get_probe(space='real', *, as_torch=False, device='cpu')[source]#
Complex probe modes; see
extract.extract_probe().- Parameters:
space (Literal['real', 'fourier'])
as_torch (bool)
device (str)
- get_probe_positions(fov='full', *, target='probe', units='px', include_sub_px_shifts=True, as_torch=False, device='cpu')[source]#
Probe-center or crop-window positions; see
extract.extract_probe_positions().include_sub_px_shiftscontrols whether the optimizedprobe_pos_shiftsoffsets are included.target='probe'returns probe centers;target='crop'returns crop-window top-left positions, which can be negative when combined withfov='crop'.Power users who hold only a sub-dict (e.g. just
optimizable_tensorsfrom a streaming loader) should call the standaloneextract.extract_probe_positions()with explicitmodel_attrs.- Parameters:
fov (Literal['full', 'crop'])
target (Literal['probe', 'crop'])
units (Literal['px', 'pixel', 'Ang'])
include_sub_px_shifts (bool)
as_torch (bool)
device (str)
- property is_multislice: bool#
- property lambd: float#
- property model#
The cached
PtychoModelinstance built viabuild_model(), orNone.
- property model_attrs: dict#
- property niter: int | None#
- property nslice: int#
- property omode: int#
- property opt_tensors: dict#
- property params: dict#
- property path: str | None#
- plot_dashboard(**kw)[source]#
Convergence dashboard via
ptyrad.plotting.plot_convergence_dashboard().Pulls
loss_iters,lr_iters,dz_iters,avg_tilt_iters,convergence_itersstraight from the loaded dict — no rebuilt model required.
- plot_forward_pass(indices, *, dp_power=0.5, **kw)[source]#
Forward-pass diagnostic via
ptyrad.plotting.plot_forward_pass().Requires a rebuilt model — call
build_model()first.- Parameters:
dp_power (float)
- plot_loss(**kw)[source]#
Plot the loss curve via
ptyrad.plotting.plot_loss_curves().
- plot_positions(fov='full', *, target='probe', **kw)[source]#
Scatter scan positions via
ptyrad.plotting.plot_scan_positions().See
get_probe_positions()for thefov/targetsemantics.- Parameters:
fov (Literal['full', 'crop'])
target (Literal['probe', 'crop'])
- plot_probe(space='real', amp_or_phase='amplitude', **kw)[source]#
Plot probe modes via
ptyrad.plotting.plot_probe_modes().spaceandamp_or_phasemap directly to that function’sreal_or_fourierandamp_or_phasearguments.- Parameters:
space (Literal['real', 'fourier'])
amp_or_phase (Literal['amplitude', 'phase'])
- plot_slice_thickness(**kw)[source]#
Plot slice-thickness evolution via
ptyrad.plotting.plot_slice_thickness().
- plot_summary(indices, **kw)[source]#
Summary figure via
ptyrad.plotting.plot_summary().Requires a rebuilt model — call
build_model()first.output_pathis read fromkw(default'.').
- plot_tilts(**kw)[source]#
Plot per-position object tilts via
ptyrad.plotting.plot_obj_tilts().
- property pmode: int#
- property probe_shape: tuple[int, int]#
- property provenance: dict | None#
Lazy-loaded provenance dict;
Noneif unavailable.Reads the
provenance_jsonroot HDF5 attr viaextract.extract_provenance()on first access and caches the result. ReturnsNone(with a one-timeUserWarning) when theAnalyzerwas constructed from an in-memory dict, since root attrs are not preserved byptyrad.io.load.load_ptyrad().
- ptyrad.analysis.apply_fov(arr, bbox)[source]#
Crop the last two dims of
arrto an inclusivebbox.bboxis(y_min, y_max, x_min, x_max)and both bounds are inclusive. Returnsarrunchanged whenbbox is None.- Parameters:
arr (ndarray)
bbox (tuple[int, int, int, int] | None)
- Return type:
ndarray
- ptyrad.analysis.extract_keys(data, delimiter='.')[source]#
Return a flat dotted-key listing of a loaded data dict.
Thin wrapper over
ptyrad.io.hierarchy.list_nested_keys()that accepts a nesteddict(the form returned byload_ptyrad) and yields keys like'optimizable_tensors.probe','model_attributes.crop_pos'.- Parameters:
data (Mapping[str, Any])
delimiter (str)
- Return type:
list[str]
- ptyrad.analysis.extract_loss_curves(data)[source]#
Pull the loss-history fields from a loaded data dict.
Returns a
dictwith these keys (missing fields becomeNone):loss_iters—(niter, 2)float64ndarray of(iter_number, total_loss)pairs.batch_losses—dict[str, list[float]]of per-loss batch values from the most recent iteration.avg_losses—dict[str, float]of averaged batch losses.niter— final iteration number (int).
- Parameters:
data (Mapping[str, Any])
- Return type:
dict[str, Any]
- ptyrad.analysis.extract_object(data_or_opt, *, fov='full', model_attrs=None, probe_shape=None, as_torch=False, device='cpu')[source]#
Return the complex object
obja * exp(1j * objp).Shape
(omode, Nz, Ny, Nx),complex64. The 4D shape is preserved even for single-slice runs (Nz == 1) so downstream code can branch onNzdirectly; squeeze yourself if you need 2D.fov='crop'clips to the scanned-FOV bbox (seeget_scanned_fov_bbox()); it requirescrop_posfrommodel_attrs.probe_shapeis auto-inferred fromopt_tensors['probe']and only needs to be passed when callers have stripped the probe out ofopt_tensors.- Parameters:
data_or_opt (Mapping[str, Any])
fov (Literal['full', 'crop'])
model_attrs (Mapping[str, Any] | None)
probe_shape (tuple[int, int] | None)
as_torch (bool)
device (str)
- ptyrad.analysis.extract_object_amplitude(data_or_opt, *, fov='full', model_attrs=None, probe_shape=None, as_torch=False, device='cpu')[source]#
Return the saved object amplitude
obja(no recomposition).Shape
(omode, Nz, Ny, Nx),float32.fovbehaves the same asextract_object().- Parameters:
data_or_opt (Mapping[str, Any])
fov (Literal['full', 'crop'])
model_attrs (Mapping[str, Any] | None)
probe_shape (tuple[int, int] | None)
as_torch (bool)
device (str)
- ptyrad.analysis.extract_object_phase(data_or_opt, *, fov='full', model_attrs=None, probe_shape=None, as_torch=False, device='cpu')[source]#
Return the saved object phase
objp(no recomposition).Shape
(omode, Nz, Ny, Nx),float32.fovbehaves the same asextract_object(). Sign convention followsPtychoModel: the complex object isobja * exp(1j * objp); no sign flip is applied here.- Parameters:
data_or_opt (Mapping[str, Any])
fov (Literal['full', 'crop'])
model_attrs (Mapping[str, Any] | None)
probe_shape (tuple[int, int] | None)
as_torch (bool)
device (str)
- ptyrad.analysis.extract_probe(data_or_opt, *, space='real', as_torch=False, device='cpu')[source]#
Return the probe modes. Shape
(pmode, Ny, Nx),complex64.space='real'returns the stored real-space complex wavefunction (the form held inoptimizable_tensors['probe']after theview_as_complexpost-process at save time).space='fourier'returnsfftshift(fft2(ifftshift(probe), norm='ortho'))along the last two axes. The fftshift sandwich matchesptyrad.plotting.plot_probe_modes()exactly so amplitudes and phases line up between getters and plotters; in particular the pre-fftshift to the corner avoids the checkerboard-phase artifact that a plainfft2would produce.- Parameters:
data_or_opt (Mapping[str, Any])
space (Literal['real', 'fourier'])
as_torch (bool)
device (str)
- ptyrad.analysis.extract_probe_positions(data_or_opt, model_attrs=None, *, fov='full', target='probe', units='px', include_sub_px_shifts=True, as_torch=False, device='cpu')[source]#
Return positions, shape
(N, 2)as[y, x],float32.- Parameters:
data_or_opt (Mapping[str, Any]) – Full data dict from
ptyrad.io.load.load_ptyrad(), or theoptimizable_tensorssub-dict.model_attrsmust be supplied when the latter is passed.model_attrs (Mapping[str, Any] | None) – Optional explicit
model_attributessub-dict. Must containcrop_pos(always) anddx(only whenunits='Ang').fov (Literal['full', 'crop']) –
'full'returns positions in the full saved-object coordinate frame.'crop'subtracts the probe-center scanned-FOV bbox top-left (seeget_scanned_fov_bbox()) so that, regardless oftarget, the returned positions live in the same coordinate frame as the array returned byextract_object(fov='crop'). Because that cropped FOV is anchored on probe centers,target='crop'can produce negative local coordinates near the top/left edge.target (Literal['probe', 'crop']) –
'probe'(default) returns probe-center positions (crop_pos + (Hp // 2, Wp // 2)).'crop'returns the top-left crop-window positions used byPtychoModel.get_obj_patches(). The two differ by exactly(Hp // 2, Wp // 2)and that offset survivesfov='crop'because the same bbox is subtracted in both cases.units (Literal['px', 'pixel', 'Ang']) –
'px'/'pixel'returns object-space pixels.'Ang'multiplies bymodel_attrs['dx']to return Ångströms.include_sub_px_shifts (bool) – When
True(default), addopt_tensors['probe_pos_shifts']if present. These are the optimized sub-pixel offsets relative to the integercrop_posgrid. Older saves without the key silently fall back to zero shifts.as_torch (bool)
device (str)
- ptyrad.analysis.extract_provenance(path)[source]#
Read the
provenance_jsonroot attribute from an HDF5 file.The provenance JSON is stored as a root-level HDF5 attribute, not as a dataset, so
ptyrad.io.load.load_ptyrad()silently drops it. This helper opens the file directly viaptyrad.io.provenance.load_provenance_from_h5()and returns the parsed dict (with keys like'probe','obj','pos','tilt'). ReturnsNonewhen the attribute is missing or the JSON parse fails.- Parameters:
path (str | PathLike)
- Return type:
dict | None
- ptyrad.analysis.get_probe_center_positions(crop_pos, probe_shape, probe_pos_shifts=None)[source]#
Convert top-left
crop_posto probe-center positions.The
crop_posstored inmodel.hdf5is the top-left corner of the probe window in object pixels — that is the form consumed byPtychoModel.get_obj_patches(), not the visual probe center. The probe center sits(Hp // 2, Wp // 2)further in.- Parameters:
crop_pos (array-like, shape (N, 2), int) – Top-left
[y, x]of each probe window in object pixels.probe_shape (tuple) – Probe shape; only the last two dims
(Hp, Wp)are used.probe_pos_shifts (array-like, shape (N, 2), optional) – Optimized sub-pixel offsets; added to the center positions when given.
- Returns:
Shape
(N, 2),float32,[y, x]order, in object pixels.- Return type:
np.ndarray
- ptyrad.analysis.get_scanned_fov_bbox(crop_pos, probe_shape)[source]#
Inclusive
(y_min, y_max, x_min, x_max)of probe-center positions.The bbox is computed from the integer probe-center positions (
crop_pos + probe_shape // 2) withoutprobe_pos_shifts— matching the bbox math inptyrad.io.save.save_results()so the result lines up exactly with the saved*_cropTIFFs (sliced as[y_min:y_max + 1, x_min:x_max + 1]).- Parameters:
crop_pos (ndarray)
probe_shape (tuple[int, int])
- Return type:
tuple[int, int, int, int]
Modules