Skip to content

torchsky.field

Experimental surface

These APIs are under active development. Names, defaults, and result objects may change between minor releases; pin a version if you depend on them in production.

Continuous sky models: domains, field model kinds and presets, observation encoding, fitting, rendering, residual diagnostics, and portable artifacts. The formulation behind these APIs is in the field formulation guide.

Guides: fields, formulation, encoders


This reference is generated from the package source. Names are grouped by the module that defines them; import every name from the public namespace torchsky.field unless the group text says otherwise.

Top-level namespace

FIELD_MODEL_KINDS module-attribute

FIELD_MODEL_KINDS: tuple[str, ...] = tuple(
    get_args(FieldModelKind)
)

FIELD_MODEL_PRESET_NAMES module-attribute

FIELD_MODEL_PRESET_NAMES = tuple(FIELD_MODEL_PRESETS)

RECOMMENDED_FIELD_MODEL_KINDS module-attribute

RECOMMENDED_FIELD_MODEL_KINDS: tuple[str, ...] = (
    "sky_lambda_feature_field",
    "sky_encoder_resmlp",
    "hashgrid_spectral_field",
)

distribution

distribution(
    field_model: Module,
    observation: FieldObservation,
    *,
    target: Literal[
        "observation", "samples"
    ] = "observation",
    **kwargs: Any,
) -> tuple[Tensor, Tensor]

Render predictive mean and log-variance.

preset

preset(
    name: str, domain: FieldDomain, **overrides: Any
) -> Module

Build a recommended field model by preset name.

render

render(
    field_model: Module,
    observation: FieldObservation,
    *,
    target: Literal["observation"] = "observation",
    **kwargs: Any,
) -> Tensor
render(
    field_model: Module,
    observation: FieldObservation,
    *,
    target: Literal["samples"],
    ra_deg: Tensor,
    dec_deg: Tensor,
    **kwargs: Any,
) -> Tensor
render(
    field_model: Module,
    observation: FieldObservation | None = None,
    *,
    target: Literal["cube", "healpix", "cube_via_healpix"],
    **kwargs: Any,
) -> Tensor
render(
    field_model: Module,
    observation: FieldObservation | None = None,
    *,
    target: RenderTarget = "observation",
    **kwargs: Any,
) -> Tensor

Render a field to an observation, coordinate samples, cube, or HEALPix map.

Parameters:

Name Type Description Default
field_model Module

Trained or initialized field module.

required
observation FieldObservation | None

Observation metadata required by observation and sample targets.

None
target RenderTarget

Output geometry selector.

'observation'
**kwargs Any

Target-specific coordinates, shape, WCS, or HEALPix options.

{}

Returns:

Type Description
Tensor

Rendered tensor on the model's execution device.

Examples:

>>> from torchsky.field import render
>>> callable(render)
True

sample

sample(
    field_model: Module,
    observation: FieldObservation,
    *,
    ra_deg: Tensor,
    dec_deg: Tensor,
    **kwargs: Any,
) -> Tensor

Evaluate a field on explicit sky-coordinate samples for one observation.

Core helpers

estimate_domain

estimate_domain(
    observations: Sequence[FieldObservation],
    *,
    coord_mode: FieldCoordinateMode = "local_tangent",
) -> FieldDomain

Estimate a normalized spatial-spectral domain covering observations.

Adaptive

ResidualTileBasisSpec dataclass

ResidualTileBasisSpec(
    centers: Tensor,
    scales: Tensor,
    amplitudes: Tensor,
    tiles: tuple[FieldResidualTile, ...],
)

Encoded centers, scales, and amplitudes for residual-tile basis atoms.

amplitudes instance-attribute

amplitudes: Tensor

centers instance-attribute

centers: Tensor

scales instance-attribute

scales: Tensor

tiles instance-attribute

tiles: tuple[FieldResidualTile, ...]

residual_tile_basis

residual_tile_basis(
    base_model: _BaseFieldModel,
    observation: FieldObservation,
    *,
    residual_product: FieldResidualProduct,
    tile_hw: tuple[int, int],
    max_tiles: int,
    min_support_fraction: float = 0.0,
    spectral_scale_encoded: float = 1.0,
    learn_scales: bool = False,
) -> ResidualTileBasisField

Build an adaptive residual-tile field from rendered residual evidence.

residual_tile_basis_spec

residual_tile_basis_spec(
    base_model: _BaseFieldModel,
    observation: FieldObservation,
    tiles: Sequence[FieldResidualTile],
    *,
    residual_product: FieldResidualProduct | None = None,
    spectral_scale_encoded: float = 1.0,
) -> ResidualTileBasisSpec

Convert residual-image tiles into encoded basis-atom parameters.

Artifacts

ArtifactManifest dataclass

ArtifactManifest(payload: Mapping[str, Any])

domain property

domain: FieldDomain

model_kind property

model_kind: FieldModelKind

model_kwargs property

model_kwargs: dict[str, Any]

payload instance-attribute

payload: Mapping[str, Any]

__post_init__

__post_init__() -> None

to_dict

to_dict() -> dict[str, Any]

LoadedArtifact dataclass

LoadedArtifact(
    manifest: FieldArtifactManifest,
    model: Module,
    optimizer_state: dict[str, Any] | None,
    amp_scaler_state: dict[str, Any] | None,
    rng_state: dict[str, Any] | None,
    sampling_rng_state: Tensor | None,
    completed_steps: int,
)

amp_scaler_state instance-attribute

amp_scaler_state: dict[str, Any] | None

completed_steps instance-attribute

completed_steps: int

manifest instance-attribute

model instance-attribute

model: Module

optimizer_state instance-attribute

optimizer_state: dict[str, Any] | None

rng_state instance-attribute

rng_state: dict[str, Any] | None

sampling_rng_state instance-attribute

sampling_rng_state: Tensor | None

load_artifact

load_artifact(
    path: str | Path,
    *,
    device: device | str | None = None,
    verify_inputs: bool = True,
) -> LoadedFieldArtifact

Load and validate a saved field model artifact.

save_artifact

save_artifact(
    path: str | Path,
    *,
    model: Module,
    model_kind: FieldModelKind,
    model_kwargs: Mapping[str, Any] | None = None,
    domain: FieldDomain | None = None,
    operator_manifest: Mapping[str, Any] | None = None,
    calibration_manifest: Mapping[str, Any] | None = None,
    fit_config: Mapping[str, Any] | None = None,
    input_identities: list[Mapping[str, Any]] | None = None,
    fit_result: FieldFitResult | None = None,
    overwrite: bool = False,
) -> FieldArtifactManifest

Save a field model, manifest, and optional training state.

Core

component_decomposition

component_decomposition(
    *,
    component_roles: Sequence[str],
    domain: FieldDomain | None = None,
    component_models: Sequence[Module] | None = None,
    component_model_kind: FieldModelKind = "sine_implicit",
    component_model_kwargs: dict[str, object] | None = None,
    surveys: Sequence[str] | None = None,
    instruments: Sequence[str] | None = None,
    observation_forward_contexts: Sequence[
        ForwardContext | None
    ]
    | None = None,
    dtype: dtype = float32,
    device: device | None = None,
) -> FieldComponentDecomposition

Build an explicit field decomposition model from component specs or modules.

model

model(
    kind: FieldModelKind, domain: FieldDomain, **kwargs: Any
) -> Module

Construct a field model of the requested kind on a fixed domain.

Parameters:

Name Type Description Default
kind FieldModelKind

Registered field-model kind.

required
domain FieldDomain

Spatial and spectral normalization domain.

required
**kwargs Any

Model-specific constructor options.

{}

Returns:

Type Description
Module

An untrained PyTorch field module.

Examples:

>>> from torchsky.field import Domain, model
>>> domain = Domain(0.0, 0.0, 1.0, 5000.0, 1000.0)
>>> field = model("sine_implicit", domain)
>>> field.domain is domain
True

Models component

FieldComponentDecomposition

FieldComponentDecomposition(
    component_models: Sequence[Module],
    *,
    component_roles: Sequence[str],
    surveys: Sequence[str] = (),
    instruments: Sequence[str] = (),
    dtype: dtype = float32,
    device: device | None = None,
)

Bases: _BaseFieldModel

Explicit shared/private decomposition over multiple field models.

component_gains instance-attribute

component_gains = Parameter(
    ones(len(component_roles), dtype=dtype, device=device)
)

component_models instance-attribute

component_models = ModuleList(models)

component_roles instance-attribute

component_roles = roles

mixer instance-attribute

mixer = SurveyInstrumentComponentMixer(
    component_roles=component_roles,
    surveys=surveys,
    instruments=instruments,
    dtype=dtype,
    device=device,
)

_context_labels

_context_labels(
    forward_context: ForwardContext | None,
) -> tuple[str | None, str | None]

_role_indices

_role_indices(
    roles: str | Sequence[str],
) -> tuple[int, ...]

component_prediction

component_prediction(
    ra_deg: Tensor,
    dec_deg: Tensor,
    wavelength_angstrom: Tensor | float,
    *,
    roles: str | Sequence[str],
    forward_context: ForwardContext | None = None,
    aggregate: bool = True,
) -> Tensor

decomposition_diagnostics

decomposition_diagnostics(
    observations: Sequence[FieldObservation],
    truth_model: "FieldComponentDecomposition",
    *,
    observation_forward_contexts: Sequence[
        ForwardContext | None
    ]
    | None = None,
    num_spectral_samples: int = 9,
    spectral_sampling: FieldSpectralSampling = "deterministic_quadrature",
    spectral_sampling_grid_size: int | None = None,
    generator: Generator | None = None,
    calibration_anchor_model: Module | None = None,
    calibration_anchor_roles: str
    | Sequence[str] = "shared",
) -> ComponentDecompositionDiagnostics

fix_component_scale_gauge_

fix_component_scale_gauge_(
    *, target_gain: float = 1.0, min_gain: float = 1e-12
) -> Tensor

Canonicalize explicit component gains without changing rendered predictions.

forward

forward(
    ra_deg: Tensor,
    dec_deg: Tensor,
    wavelength_angstrom: Tensor | float,
    *,
    forward_context: ForwardContext | None = None,
) -> Tensor

render_component_observation

render_component_observation(
    observation: FieldObservation,
    *,
    roles: str | Sequence[str],
    num_spectral_samples: int = 9,
    spectral_sampling: FieldSpectralSampling = "deterministic_quadrature",
    spectral_sampling_grid_size: int | None = None,
    generator: Generator | None = None,
    override_wavelengths: Tensor | None = None,
    override_weights: Tensor | None = None,
    forward_context: ForwardContext | None = None,
    aggregate: bool = True,
) -> Tensor

render_component_samples

render_component_samples(
    observation: FieldObservation,
    *,
    ra_deg: Tensor,
    dec_deg: Tensor,
    roles: str | Sequence[str],
    num_spectral_samples: int = 9,
    spectral_sampling: FieldSpectralSampling = "deterministic_quadrature",
    spectral_sampling_grid_size: int | None = None,
    generator: Generator | None = None,
    override_wavelengths: Tensor | None = None,
    override_weights: Tensor | None = None,
    forward_context: ForwardContext | None = None,
    aggregate: bool = True,
) -> Tensor

Observation builders

field_observations_from_bin_centers

field_observations_from_bin_centers(
    bin_centers: Tensor | Sequence[float],
    full_widths: Tensor | float,
    values: Tensor,
    *,
    spatial_header: dict[str, Any] | None = None,
    weight: Tensor | None = None,
    mask: Tensor | None = None,
    spectral_quantity: FieldSpectralQuantity = "wavelength",
    band_name_prefix: str = "bin",
    transmission_sigma: Tensor
    | Sequence[float | None]
    | float
    | None = None,
    psf_sigma_pix: float = 0.0,
    sky_background: Tensor | None = None,
    instrument_background: Tensor | None = None,
    detector_background: Tensor | None = None,
) -> list[FieldObservation]

Top-hat bins [center - width/2, center + width/2] (e.g. PHA summaries).

Bins may be non-contiguous or overlap in axis space; each row is still one independent [lo, hi] interval (unlike :func:field_observations_from_bin_edges, which assumes a single contiguous edge chain).

full_widths may be a scalar broadcast to every bin or a 1D tensor matching bin_centers.

field_observations_from_bin_edges

field_observations_from_bin_edges(
    bin_edges: Tensor | Sequence[float],
    values: Tensor,
    *,
    spatial_header: dict[str, Any] | None = None,
    weight: Tensor | None = None,
    mask: Tensor | None = None,
    spectral_quantity: FieldSpectralQuantity = "wavelength",
    band_name_prefix: str = "bin",
    transmission_sigma: Tensor
    | Sequence[float | None]
    | float
    | None = None,
    psf_sigma_pix: float = 0.0,
    sky_background: Tensor | None = None,
    instrument_background: Tensor | None = None,
    detector_background: Tensor | None = None,
) -> list[FieldObservation]

One :class:FieldObservation per bin from native bin_edges and binned values.

Parameters

bin_edges Length n_bin + 1, strictly increasing (same units as the spectral axis). values Shape (n_bin,) for a 1D spectrum (stored as 1×1 images) or (n_bin, H, W) for an IFU-style stack of channel maps (one map per bin). spatial_header WCS header for the 2D slice. Required when values is 3D (IFU). If omitted for 1D data, :func:make_tangent_plane_header is used with shape 1×1. weight, mask Optional maps matching each per-bin image (same shape as values after the leading spectral dimension is dropped per observation), or broadcastable spatial maps (H, W) shared across bins. spectral_quantity "energy" for X-ray channel edges in keV (or eV if you use that consistently), "wavelength" for optical/IR, etc. band_name_prefix Band names are f"{prefix}_{i:06d}". transmission_sigma Per-bin effective Gaussian width in the same units as bin_edges, or a scalar broadcast. Invalid or non-positive values are treated as unknown (None).

field_observations_from_ifu_cube

field_observations_from_ifu_cube(
    datacube: Tensor,
    bin_edges: Tensor | Sequence[float],
    spatial_header: dict[str, Any],
    *,
    spectral_axis: int = 0,
    weight: Tensor | None = None,
    mask: Tensor | None = None,
    spectral_quantity: FieldSpectralQuantity = "wavelength",
    band_name_prefix: str = "ifu",
    transmission_sigma: Tensor
    | Sequence[float | None]
    | float
    | None = None,
    psf_sigma_pix: float = 0.0,
    sky_background: Tensor | None = None,
    instrument_background: Tensor | None = None,
    detector_background: Tensor | None = None,
) -> list[FieldObservation]

IFU / image-spectral cube: (..., L, ...) with spectral axis at spectral_axis.

After moving that axis to the front, the array must be exactly (L, H, W). For a batch of cubes, slice to one field before calling.

make_tangent_plane_header

make_tangent_plane_header(
    height: int,
    width: int,
    *,
    crval_ra_deg: float = 180.0,
    crval_dec_deg: float = 0.0,
    cd11: float = -0.001,
    cd12: float = 0.0,
    cd21: float = 0.0,
    cd22: float = 0.001,
) -> dict[str, float | str | int]

Minimal RA---TAN / DEC--TAN header for height × width pixels.

Presets

FieldModelPreset dataclass

FieldModelPreset(
    name: str,
    kind: FieldModelKind,
    description: str,
    kwargs: Mapping[str, Any],
)

Named field-model recipe for users who do not need class-level details.

description instance-attribute

description: str

kind instance-attribute

kind: FieldModelKind

kwargs instance-attribute

kwargs: Mapping[str, Any]

name instance-attribute

name: str

make

make(domain: FieldDomain, **overrides: Any) -> Module

Build this preset for domain, with explicit keyword overrides.

get_field_model_preset

get_field_model_preset(name: str) -> FieldModelPreset

Return a named field-model preset.

Quality

ResidualProduct dataclass

ResidualProduct(
    prediction: Tensor,
    target: Tensor,
    residual: Tensor,
    weight: Tensor,
    support: Tensor,
    metrics: dict[str, float],
    band_name: str | None = None,
)

Rendered prediction, residual map, support mask, and scalar metrics.

band_name class-attribute instance-attribute

band_name: str | None = None

metrics instance-attribute

metrics: dict[str, float]

prediction instance-attribute

prediction: Tensor

residual instance-attribute

residual: Tensor

support instance-attribute

support: Tensor

target instance-attribute

target: Tensor

weight instance-attribute

weight: Tensor

ResidualTile dataclass

ResidualTile(
    y0: int,
    y1: int,
    x0: int,
    x1: int,
    residual_energy: float,
    residual_rmse: float,
    support_fraction: float,
    effective_weight_sum: float,
    band_name: str | None = None,
)

Support-aware residual energy summary for one image tile.

band_name class-attribute instance-attribute

band_name: str | None = None

effective_weight_sum instance-attribute

effective_weight_sum: float

residual_energy instance-attribute

residual_energy: float

residual_rmse instance-attribute

residual_rmse: float

support_fraction instance-attribute

support_fraction: float

x0 instance-attribute

x0: int

x1 instance-attribute

x1: int

y0 instance-attribute

y0: int

y1 instance-attribute

y1: int

metrics

metrics(
    prediction: Tensor,
    target: Tensor,
    *,
    weight: Tensor | None = None,
    mask: Tensor | None = None,
    residual_scales: Sequence[int] = (1, 2, 4),
) -> dict[str, float]

Compute weighted residual, SNR, sharpness, and multiscale residual metrics.

Metrics that require effective support are NaN when the nonnegative finite weight sum is zero. Support counts and fractions remain defined.

quality

quality(
    model: Module,
    observations: Sequence[FieldObservation],
    *,
    num_spectral_samples: int = 9,
    observation_forward_contexts: Sequence[
        ForwardContext | None
    ]
    | None = None,
    observation_exposure_poses: Sequence[
        ExposurePose | None
    ]
    | None = None,
    observation_operator_bundles: Sequence[
        ObservationOperatorBundle | None
    ]
    | None = None,
    residual_scales: Sequence[int] = (1, 2, 4),
    include_scorecard: bool = False,
    include_residual_products: bool = False,
    **render_kwargs: Any,
) -> dict[str, Any]

Render observations and summarize residual/SNR/sharpness metrics.

render_residual_product

render_residual_product(
    model: Module,
    observation: FieldObservation,
    *,
    num_spectral_samples: int = 9,
    forward_context: ForwardContext | None = None,
    exposure_pose: ExposurePose | None = None,
    operator_bundle: ObservationOperatorBundle
    | None = None,
    residual_scales: Sequence[int] = (1, 2, 4),
    **render_kwargs: Any,
) -> FieldResidualProduct

Render a field observation and return a structured residual product.

residual

residual(
    model: Module,
    observation: FieldObservation,
    *,
    num_spectral_samples: int = 9,
    forward_context: ForwardContext | None = None,
    exposure_pose: ExposurePose | None = None,
    operator_bundle: ObservationOperatorBundle
    | None = None,
    masked: bool = True,
    **render_kwargs: Any,
) -> Tensor

Render model into observation and return prediction minus image.

residual_product

residual_product(
    prediction: Tensor,
    target: Tensor,
    *,
    weight: Tensor | None = None,
    mask: Tensor | None = None,
    psf_spec: object | None = None,
    residual_scales: Sequence[int] = (1, 2, 4),
    band_name: str | None = None,
) -> FieldResidualProduct

Build a residual product for one predicted image and target image.

residual_tiles

residual_tiles(
    product: FieldResidualProduct,
    *,
    tile_hw: tuple[int, int],
    max_tiles: int | None = None,
    min_support_fraction: float = 0.0,
) -> tuple[FieldResidualTile, ...]

Rank fixed image tiles by exact weighted residual energy.

Registry

is_recommended_field_model(kind: str) -> bool

Return whether a field model kind is in the recommended subset.

Spectral budget

spectral_data_budget

spectral_data_budget(
    observations: Sequence[FieldObservation],
) -> SpectralDataBudget

Summarize wavelength coverage and variation scales in observations.

suggest_num_spectral_samples

suggest_num_spectral_samples(
    budget: SpectralDataBudget,
    *,
    spacing_to_scale_ratio: float = 0.35,
    floor: int = 3,
    cap: int = 256,
) -> int

Suggest a single num_spectral_samples for :func:fit_field_model / rendering.

For each observation with box span S and variation scale L (Gaussian σ capped by S, or S for uniform-in-box), require trapezoid node spacing h ≲ spacing_to_scale_ratio * L across the integration interval, i.e.

n ≳ ceil(S / h) + 1 when S > 0.

Default spacing_to_scale_ratio=0.35 targets on the order of three nodes per correlation scale (documented tradeoff, not a hard error bound).

Delta-band observations contribute floor (minimum meaningful count).

suggest_spectral_model_capacity

suggest_spectral_model_capacity(
    budget: SpectralDataBudget,
) -> SpectralModelCapacityHints

Map global span vs finest scale to encoder width and model-family hints.

spectroscopicity = wavelength_span / min_variation_scale grows when many narrow bands or small σ sit inside a wide global λ range.

Training

fit

fit(
    observations: Sequence[FieldObservation],
    model: Module,
    *,
    steps: int = 200,
    lr: float = 0.005,
    weight_decay: float = 0.0,
    ema_decay: float = 0.0,
    num_spectral_samples: int = 9,
    loss_kind: FieldLossKind = "mse",
    charbonnier_eps: float = 0.001,
    huber_delta: float = 0.05,
    loss_reduction: FieldLossReduction = "per_observation",
    lr_schedule: FieldLRSchedule = "constant",
    grad_clip_norm: float | None = None,
    curriculum: FieldCurriculum = "none",
    training_spectral_sampling: FieldSpectralSampling = "deterministic_quadrature",
    training_spectral_sampling_grid_size: int | None = None,
    spatial_sampling: FieldSpatialSampling = "center",
    spectral_anchor_observations_per_step: int = 0,
    spectral_anchor_num_samples: int | None = None,
    spectral_anchor_loss_weight: float = 0.0,
    component_anchor_terms: Sequence[
        FieldComponentAnchorTerm
    ]
    | None = None,
    component_anchor_num_spectral_samples: int
    | None = None,
    component_anchor_loss_kind: FieldLossKind | None = None,
    component_centroid_anchor_terms: Sequence[
        FieldCentroidAnchorTerm
    ]
    | None = None,
    lr_warmup_steps: int = 0,
    spatial_smoothness_weight: float = 0.0,
    spatial_smoothness_grid_size: int = 8,
    gradient_residual_weight: float = 0.0,
    observations_per_step: int | None = None,
    observation_sampling: FieldObservationSampling = "uniform",
    training_patch_size: int | None = None,
    training_patches_per_observation: int = 1,
    pixel_samples_per_observation: int | None = None,
    shared_sky_patch_sampling: bool = False,
    streaming_accumulation: bool = False,
    full_observations_per_step: int = 0,
    context_observations_per_step: int = 0,
    context_downsample_factor: int = 1,
    context_loss_weight: float = 0.0,
    spectral_observability_weight: float = 0.0,
    spectral_observability_probe_grid_size: int = 8,
    residual_head_penalty_weight: float = 0.0,
    hybrid_foam_sparsity_weight: float = 0.0,
    hybrid_correlation_weight: float = 0.0,
    hybrid_probe_grid_size: int = 8,
    operator_scale_gate_weight: float = 0.0,
    operator_scale_shift_weight: float = 0.0,
    noise_pretrain_kind: FieldNoisePretrainKind = "none",
    noise_pretrain_steps: int = 0,
    noise_pretrain_lr: float | None = None,
    noise_pretrain_observations_per_step: int | None = None,
    noise_pretrain_pixel_samples_per_observation: int = 1024,
    noise_pretrain_num_terms: int = 48,
    noise_pretrain_max_frequency: float = 8.0,
    freeze_field_encoder: bool = False,
    freeze_field_body: bool = False,
    amp_dtype: FieldAMPDtype = "none",
    amp_scaler_state: Mapping[str, Any] | None = None,
    device: device | str | None = None,
    observation_forward_contexts: Sequence[
        ForwardContext | None
    ]
    | None = None,
    observation_operator_bundles: Sequence[
        ObservationOperatorBundle
    ]
    | None = None,
    operator_calibration_terms: Sequence[
        OperatorCalibrationTerm
    ]
    | None = None,
    observation_exposure_poses: Sequence[
        ExposurePose | None
    ]
    | None = None,
    refine_poses: bool = False,
    anchor_first_pose: bool = True,
    render_spatial_tile_hw: tuple[int, int] | None = None,
    diagnostic_spatial_tile_hw: tuple[int, int]
    | None = None,
    optimizer_state: Mapping[str, Any] | None = None,
    sampling_rng_state: Tensor | None = None,
    _progress_callback: Callable[[int, float], None]
    | None = None,
) -> FieldFitResult

Fit observations, optionally recomputing tiled structured-noise renders.

With streaming_accumulation and render_spatial_tile_hw, structured noise uses one full-exposure likelihood and replays each renderer tile for its gradient. Renderer activations are bounded by tile size; prediction, residual, and noise-model storage still scale with exposure size. As with activation checkpointing, rendering must not mutate model state.

Parameters:

Name Type Description Default
observations Sequence[FieldObservation]

Non-empty sequence of field observations to fit.

required
model Module

Field module whose parameters are optimized in place.

required
steps int

Number of optimizer steps.

200
lr float

Initial learning rate.

0.005
device device | str | None

Optional training device.

None
loss_kind FieldLossKind

Per-pixel fitting loss.

'mse'
num_spectral_samples int

Spectral quadrature samples per observation.

9
render_spatial_tile_hw tuple[int, int] | None

Optional renderer tile height and width.

None

Returns:

Type Description
FieldFitResult

Fit diagnostics, optimizer state, and reproducibility state.

Examples:

>>> from torchsky.field import fit
>>> callable(fit)
True

fit_component_decomposition

fit_component_decomposition(
    observations: Sequence[FieldObservation],
    *,
    component_roles: Sequence[str],
    model: FieldComponentDecomposition | None = None,
    domain: FieldDomain | None = None,
    component_models: Sequence[Module] | None = None,
    component_model_kind: FieldModelKind = "sine_implicit",
    component_model_kwargs: dict[str, object] | None = None,
    surveys: Sequence[str] | None = None,
    instruments: Sequence[str] | None = None,
    observation_forward_contexts: Sequence[
        ForwardContext | None
    ]
    | None = None,
    dtype: dtype = float32,
    device: device | str | None = None,
    **fit_kwargs: Any,
) -> FieldFitResult

Construct and fit a field decomposition model on context-labeled observations.

fit_hybrid

fit_hybrid(
    observations: Sequence[FieldObservation],
    model: DiffuseFoamHybridField,
    *,
    diffuse_steps: int,
    foam_steps: int,
    joint_steps: int,
    joint_foam_sparsity_weight: float = 0.0,
    joint_correlation_weight: float = 0.0,
    **fit_kwargs: Any,
) -> FieldFitResult

Fit diffuse, localized, then joint hybrid stages on one operator path.

fit_noise_crossfit

fit_noise_crossfit(
    observations: Sequence[FieldObservation],
    cross_fitted_predictions: Sequence[Tensor],
    *,
    models: Sequence[StructuredDetectorNoise] | None = None,
    steps: int = 100,
    lr: float = 0.01,
    device: device | str | None = None,
) -> tuple[
    tuple[StructuredDetectorNoise, ...], tuple[float, ...]
]

Calibrate exposure noise only from leave-one-exposure-out predictions.

cross_fitted_predictions[e] must come from a scene fit that excluded observation e. The function cannot verify that provenance, so callers should record the exclusion split alongside the returned parameters. Models are frozen before return for the subsequent scene fit.

Types

ComponentAnchorTerm dataclass

ComponentAnchorTerm(
    observation: FieldObservation,
    target_image: Tensor | None = None,
    target_values: Tensor | None = None,
    reference_model: Module | None = None,
    roles: str | tuple[str, ...] = "shared",
    sample_ra_deg: Tensor | None = None,
    sample_dec_deg: Tensor | None = None,
    sample_x_pix: Tensor | None = None,
    sample_y_pix: Tensor | None = None,
    sample_weight: Tensor | None = None,
    override_wavelengths: Tensor | None = None,
    override_bandpass_weights: Tensor | None = None,
    forward_context: ForwardContext | None = None,
    loss_weight: float = 1.0,
)

Explicit calibration / standards anchor for selected field decomposition roles.

forward_context class-attribute instance-attribute

forward_context: ForwardContext | None = None

loss_weight class-attribute instance-attribute

loss_weight: float = 1.0

observation instance-attribute

observation: FieldObservation

override_bandpass_weights class-attribute instance-attribute

override_bandpass_weights: Tensor | None = None

override_wavelengths class-attribute instance-attribute

override_wavelengths: Tensor | None = None

reference_model class-attribute instance-attribute

reference_model: Module | None = None

roles class-attribute instance-attribute

roles: str | tuple[str, ...] = 'shared'

sample_dec_deg class-attribute instance-attribute

sample_dec_deg: Tensor | None = None

sample_ra_deg class-attribute instance-attribute

sample_ra_deg: Tensor | None = None

sample_weight class-attribute instance-attribute

sample_weight: Tensor | None = None

sample_x_pix class-attribute instance-attribute

sample_x_pix: Tensor | None = None

sample_y_pix class-attribute instance-attribute

sample_y_pix: Tensor | None = None

target_image class-attribute instance-attribute

target_image: Tensor | None = None

target_values class-attribute instance-attribute

target_values: Tensor | None = None

__post_init__

__post_init__() -> None

Domain dataclass

Domain(
    center_ra_deg: float,
    center_dec_deg: float,
    spatial_scale_deg: float,
    wavelength_center: float,
    wavelength_scale: float,
    coord_mode: FieldCoordinateMode = "local_tangent",
)

Sky + wavelength normalization box for continuous field models.

spatial_scale_deg is a footprint scale in degrees (from WCS pixel scale × extent). Sky-encoder fields use it when scale_fourier_to_domain=True to choose spherical Fourier min_frequency so bands resolve structure inside the patch.

center_dec_deg instance-attribute

center_dec_deg: float

center_ra_deg instance-attribute

center_ra_deg: float

coord_mode class-attribute instance-attribute

coord_mode: FieldCoordinateMode = 'local_tangent'

spatial_scale_deg instance-attribute

spatial_scale_deg: float

wavelength_center instance-attribute

wavelength_center: float

wavelength_scale instance-attribute

wavelength_scale: float

__post_init__

__post_init__() -> None

FieldObservation dataclass

FieldObservation(
    image: Tensor,
    header: dict,
    weight: Tensor | None = None,
    mask: Tensor | None = None,
    err: Tensor | None = None,
    var_poisson: Tensor | None = None,
    var_rnoise: Tensor | None = None,
    var_flat: Tensor | None = None,
    contamination_prior: Tensor | None = None,
    amplifier_index: Tensor | None = None,
    wavelength: float | None = None,
    spectral_min: float | None = None,
    spectral_max: float | None = None,
    transmission_center: float | None = None,
    transmission_sigma: float | None = None,
    transmission_wavelength: Tensor | None = None,
    transmission_throughput: Tensor | None = None,
    psf_sigma_pix: float = 0.0,
    psf_spec: PSFSpec | None = None,
    sky_background: Tensor | None = None,
    instrument_background: Tensor | None = None,
    detector_background: Tensor | None = None,
    background_offset: float = 0.0,
    band_name: str | None = None,
    spectral_quantity: FieldSpectralQuantity | None = None,
    detector_x0_pix: float = 0.0,
    detector_y0_pix: float = 0.0,
    detector_to_sky: DetectorToSkyTransform | None = None,
    passband: Passband | None = None,
    value_unit: str | None = None,
    value_semantics: str | None = None,
    spectral_unit: str | None = None,
    detector_law: DetectorLaw | None = None,
    gain_electrons_per_adu: Tensor | float | None = None,
    gain_provenance_id: str | None = None,
    exposure_time_s: float | None = None,
    collecting_area_m2: float | None = None,
    additive_components: tuple[AdditiveComponent, ...] = (),
    processing_state: AdditiveProcessingState | None = None,
)

One band-limited 2D map plus spectral metadata for field fitting.

Use :mod:torchsky.map.field.observation_builders to turn binned 1D spectra, IFU channel cubes, or X-ray-style center/width summaries into observation lists. Set spectral_quantity when the axis is energy (e.g. keV) rather than wavelength.

Optional transmission_wavelength / transmission_throughput give a tabulated throughput curve (same units as the spectral axis, dimensionless throughput). When both are set, bandpass quadrature integrates against this curve (trapezoid rule on the quadrature grid, with linear interpolation of throughput) instead of the uniform-in-band or Gaussian fallbacks from transmission_sigma.

_cached_coordinate_signature class-attribute instance-attribute

_cached_coordinate_signature: tuple[Any, ...] | None = (
    field(default=None, init=False, repr=False)
)

_cached_dec_deg class-attribute instance-attribute

_cached_dec_deg: Tensor | None = field(
    default=None, init=False, repr=False
)

_cached_ra_deg class-attribute instance-attribute

_cached_ra_deg: Tensor | None = field(
    default=None, init=False, repr=False
)

additive_components class-attribute instance-attribute

additive_components: tuple[AdditiveComponent, ...] = ()

amplifier_index class-attribute instance-attribute

amplifier_index: Tensor | None = None

background_offset class-attribute instance-attribute

background_offset: float = 0.0

band_name class-attribute instance-attribute

band_name: str | None = None

collecting_area_m2 class-attribute instance-attribute

collecting_area_m2: float | None = None

contamination_prior class-attribute instance-attribute

contamination_prior: Tensor | None = None

detector_background class-attribute instance-attribute

detector_background: Tensor | None = None

detector_law class-attribute instance-attribute

detector_law: DetectorLaw | None = None

detector_to_sky class-attribute instance-attribute

detector_to_sky: DetectorToSkyTransform | None = None

detector_x0_pix class-attribute instance-attribute

detector_x0_pix: float = 0.0

detector_y0_pix class-attribute instance-attribute

detector_y0_pix: float = 0.0

err class-attribute instance-attribute

err: Tensor | None = None

exposure_time_s class-attribute instance-attribute

exposure_time_s: float | None = None

gain_electrons_per_adu class-attribute instance-attribute

gain_electrons_per_adu: Tensor | float | None = None

gain_provenance_id class-attribute instance-attribute

gain_provenance_id: str | None = None

header instance-attribute

header: dict

image instance-attribute

image: Tensor

instrument_background class-attribute instance-attribute

instrument_background: Tensor | None = None

mask class-attribute instance-attribute

mask: Tensor | None = None

passband class-attribute instance-attribute

passband: Passband | None = None

processing_state class-attribute instance-attribute

processing_state: AdditiveProcessingState | None = None

psf_sigma_pix class-attribute instance-attribute

psf_sigma_pix: float = 0.0

psf_spec class-attribute instance-attribute

psf_spec: PSFSpec | None = None

sky_background class-attribute instance-attribute

sky_background: Tensor | None = None

spectral_max class-attribute instance-attribute

spectral_max: float | None = None

spectral_min class-attribute instance-attribute

spectral_min: float | None = None

spectral_quantity class-attribute instance-attribute

spectral_quantity: FieldSpectralQuantity | None = None

spectral_unit class-attribute instance-attribute

spectral_unit: str | None = None

transmission_center class-attribute instance-attribute

transmission_center: float | None = None

transmission_sigma class-attribute instance-attribute

transmission_sigma: float | None = None

transmission_throughput class-attribute instance-attribute

transmission_throughput: Tensor | None = None

transmission_wavelength class-attribute instance-attribute

transmission_wavelength: Tensor | None = None

value_semantics class-attribute instance-attribute

value_semantics: str | None = None

value_unit class-attribute instance-attribute

value_unit: str | None = None

var_flat class-attribute instance-attribute

var_flat: Tensor | None = None

var_poisson class-attribute instance-attribute

var_poisson: Tensor | None = None

var_rnoise class-attribute instance-attribute

var_rnoise: Tensor | None = None

wavelength class-attribute instance-attribute

wavelength: float | None = None

weight class-attribute instance-attribute

weight: Tensor | None = None

__post_init__

__post_init__() -> None

clear_coordinate_cache

clear_coordinate_cache() -> None

Drop cached WCS coordinate grids after mutating image shape or header.

coordinate_cache_signature

coordinate_cache_signature(
    shape: tuple[int, int],
) -> tuple[Any, ...]

Return a lightweight signature for cached WCS coordinate grids.

to

to(
    device: device | str | None = None,
    dtype: dtype | None = None,
) -> "FieldObservation"

Return an independently moved observation with semantic dtypes preserved.

Floating detector planes follow dtype. Boolean masks and integer amplifier indices retain their semantic dtype. Nested modules are copied before moving, so the source observation is not mutated. Coordinate caches are intentionally dropped by :func:dataclasses.replace.

validate_certified

validate_certified(
    forward_context: ForwardContext,
) -> "FieldObservation"

Validate the strict native-observation contract used for claims.

Legacy and exploratory observations remain valid without these fields; production adapters opt into this fail-closed check explicitly.

FitResult dataclass

FitResult(
    model: Module,
    loss_history: tuple[float, ...],
    final_loss: float,
    diagnostics: dict[str, Any] | None = None,
    exposure_poses: tuple[ExposurePose | None, ...]
    | None = None,
    operator_bundle: ObservationOperatorBundle
    | None = None,
    observation_operator_bundles: tuple[
        ObservationOperatorBundle, ...
    ]
    | None = None,
    optimizer_state: dict[str, Any] | None = None,
    amp_scaler_state: dict[str, Any] | None = None,
    sampling_rng_state: Tensor | None = None,
    completed_steps: int = 0,
    _pre_train_loss: float | None = None,
)

_pre_train_loss class-attribute instance-attribute

_pre_train_loss: float | None = None

amp_scaler_state class-attribute instance-attribute

amp_scaler_state: dict[str, Any] | None = None

completed_steps class-attribute instance-attribute

completed_steps: int = 0

diagnostics class-attribute instance-attribute

diagnostics: dict[str, Any] | None = None

exposure_poses class-attribute instance-attribute

exposure_poses: tuple[ExposurePose | None, ...] | None = (
    None
)

final_loss instance-attribute

final_loss: float

initial_loss property

initial_loss: float

Loss on full observations before any training (when available), otherwise loss_history[0] (loss after the first optimizer step).

loss_history instance-attribute

loss_history: tuple[float, ...]

model instance-attribute

model: Module

observation_operator_bundles class-attribute instance-attribute

observation_operator_bundles: (
    tuple[ObservationOperatorBundle, ...] | None
) = None

operator_bundle class-attribute instance-attribute

operator_bundle: ObservationOperatorBundle | None = None

optimizer_state class-attribute instance-attribute

optimizer_state: dict[str, Any] | None = None

sampling_rng_state class-attribute instance-attribute

sampling_rng_state: Tensor | None = None

training_loss_after_step

training_loss_after_step(step: int) -> float

Training loss after optimizer step step (1-based, matches loss_history index + 1).

ForwardContext dataclass

ForwardContext(
    survey: str | None = None,
    instrument: str | None = None,
    detector: str | None = None,
    exposure_id: str | None = None,
    time_mjd: float | None = None,
    extra: dict[str, Any] = dict(),
    time_scale: TimeScale | None = None,
    calibration_id: str | None = None,
    provenance_id: str | None = None,
)

Exposure / instrument context c (see docs/models/fields.md).

Rendering passes this to nn.Module.forward or render_bandpass only when the model declares a forward_context keyword parameter. Otherwise it is ignored so existing field models stay valid without changes.

calibration_id class-attribute instance-attribute

calibration_id: str | None = None

detector class-attribute instance-attribute

detector: str | None = None

exposure_id class-attribute instance-attribute

exposure_id: str | None = None

extra class-attribute instance-attribute

extra: dict[str, Any] = field(default_factory=dict)

instrument class-attribute instance-attribute

instrument: str | None = None

provenance_id class-attribute instance-attribute

provenance_id: str | None = None

survey class-attribute instance-attribute

survey: str | None = None

time_mjd class-attribute instance-attribute

time_mjd: float | None = None

time_scale class-attribute instance-attribute

time_scale: TimeScale | None = None

__post_init__

__post_init__() -> None

validate_certified

validate_certified() -> 'ForwardContext'

Fail closed unless the context identifies one calibrated exposure.

PSFSpec dataclass

PSFSpec(
    mode: FieldPSFMode = "none",
    sigma_pix: float = 0.0,
    kernel2d: Tensor | None = None,
    model: Module | None = None,
    cell_u_pix: Tensor | None = None,
    cell_lam_norm: float = 0.5,
    frozen: bool = True,
    pixel_response_included: bool | None = None,
)

Public PSF description for field observations and target products.

kernel2d is an odd-sized, image-shaped unit-source response: its center indexes zero displacement and increasing columns/rows are positive detector x/y offsets. Rendering normalizes its sum and retains zero-padding losses at detector boundaries. It is not a PyTorch cross-correlation weight array.

Learned PSFs are calibration products and are frozen while rendering a sky scene. frozen=False is reserved for a future calibration-only execution path; the scene renderer rejects it instead of silently dropping gradients.

cell_lam_norm class-attribute instance-attribute

cell_lam_norm: float = 0.5

cell_u_pix class-attribute instance-attribute

cell_u_pix: Tensor | None = None

frozen class-attribute instance-attribute

frozen: bool = True

kernel2d class-attribute instance-attribute

kernel2d: Tensor | None = None

mode class-attribute instance-attribute

mode: FieldPSFMode = 'none'

model class-attribute instance-attribute

model: Module | None = None

pixel_response_included class-attribute instance-attribute

pixel_response_included: bool | None = None

Whether the calibrated kernel already includes detector pixel response.

True requires center sampling; another pixel quadrature would integrate twice. False records an optical-only kernel; None leaves legacy calibration unknown. This metadata does not certify the accuracy of discrete detector-grid convolution.

sigma_pix class-attribute instance-attribute

sigma_pix: float = 0.0

__post_init__

__post_init__() -> None

gaussian classmethod

gaussian(sigma_pix: float) -> 'PSFSpec'

kernel classmethod

kernel(kernel2d: Tensor) -> 'PSFSpec'

learned classmethod

learned(
    model: Module,
    *,
    base_sigma_pix: float,
    cell_u_pix: Tensor | None = None,
    cell_lam_norm: float = 0.5,
    frozen: bool = True,
) -> "PSFSpec"

to

to(
    device: device | str | None = None,
    dtype: dtype | None = None,
) -> "PSFSpec"

Return an independently moved PSF without mutating the source model.

SpectralDataBudget dataclass

SpectralDataBudget(
    observations: tuple[ObservationSpectralSummary, ...],
    wavelength_min: float,
    wavelength_max: float,
    wavelength_span: float,
    min_variation_scale: float,
    max_variation_scale: float,
    n_distinct_band_specs: int,
)

Aggregated spectral complexity from a list of :class:FieldObservation.

max_variation_scale instance-attribute

max_variation_scale: float

min_variation_scale instance-attribute

min_variation_scale: float

n_distinct_band_specs instance-attribute

n_distinct_band_specs: int

observations instance-attribute

observations: tuple[ObservationSpectralSummary, ...]

wavelength_max instance-attribute

wavelength_max: float

wavelength_min instance-attribute

wavelength_min: float

wavelength_span instance-attribute

wavelength_span: float

SpectralModelCapacityHints dataclass

SpectralModelCapacityHints(
    spectroscopicity: float,
    profile: SpectralCapacityProfile,
    spectral_frequencies: int,
    num_spectral_frequencies_basis: int,
    recommended_model_kinds: tuple[str, ...],
    rationale: str,
)

Suggested encoder / model capacity from a :class:SpectralDataBudget.

num_spectral_frequencies_basis instance-attribute

num_spectral_frequencies_basis: int

Suggested num_spatial_frequencies-style order for spectral-basis families.

profile instance-attribute

profile: SpectralCapacityProfile

rationale instance-attribute

rationale: str

recommended_model_kinds instance-attribute

recommended_model_kinds: tuple[str, ...]

spectral_frequencies instance-attribute

spectral_frequencies: int

Suggested spectral_frequencies for :class:SkyCoordinateEncoder / sky MLP fields.

spectroscopicity instance-attribute

spectroscopicity: float

wavelength_span / min_variation_scale (large ⇒ finer λ structure).

Typing

FieldModelKind

typing.Literal['sine_implicit', 'wire_implicit', 'sasnet', 'bacon', 'flair_bla', 'explicit_spectral_grid_field', 'gaussian_basis', 'fourier_mlp', 'spectral_power_foam', 'foam_initialized_inr', 'diffuse_foam_hybrid', 'spectral_basis_field', 'learned_spectral_basis_field', 'sky_encoder_bandpass_field', 'sky_lambda_planes_field', 'sky_lambda_kplanes_field', 'sky_lambda_feature_field', 'operator_constrained_feature_field', 'sky_lambda_feature_field_hetero', 'sky_lambda_feature_atlas_field', 'sky_lambda_atlas_field', 'bandpass_conditioned_field', 'conditioned_spectral_field', 'hashgrid_conditioned_field', 'sky_encoder_mlp', 'strainer_sky_encoder_mlp', 'sky_encoder_resmlp', 'hashgrid_mlp', 'hashgrid_resmlp', 'hashgrid_spectral_field']

PresetName

typing.Literal['fast', 'balanced', 'spectral', 'operator_constrained']

RenderTarget

typing.Literal['observation', 'samples', 'cube', 'healpix', 'cube_via_healpix']

SpectralCapacityProfile

typing.Literal['photometric_wide', 'multiband', 'narrowband_sweep', 'spectroscopic']