libera_utils.footprint_matching.readers.flashflux.FLASHFluxReader#

class libera_utils.footprint_matching.readers.flashflux.FLASHFluxReader(file_paths: Sequence[Path])#

Bases: PassReader

Read CERES FLASHFlux footprints as time-stamped native points.

Parses the per-footprint geolocation, observation time and the base and FMATCH-IMAGER-only FLASHFlux fields from every staged granule.

Class Attributes#

READER_KEYstr

Registry key "FLASHFlux".

RESOLUTION_KMfloat

~20 km (CERES footprint scale at nadir), the fallback when a granule’s own footprint spacing cannot be measured.

VARIABLEStuple[VariableSpec, …]

The base and FMATCH-IMAGER-only FLASHFlux fields (see module docstring).

param file_paths:

The CERES FLASHFlux NetCDF4 granule(s) covering the processing period.

type file_paths:

Sequence[Path]

__init__(file_paths: Sequence[Path]) → None#

Methods

granules_in_window(time)

The staged granules whose span lies within MAX_TIME_OFFSET of time.

preload()

Load whatever this reader holds for the whole run, ahead of the first query.

product_stacks()

Return every stacked product variable this reader contributes.

product_variable_specs()

Return every variable this reader contributes to the product definition.

samples_in(bbox, time)

Return the native samples inside bbox that match time.

Attributes

ADDITIONAL_PRODUCT_VARIABLES

INSTRUMENT

MAX_TIME_OFFSET

PASS_WINDOW

PRODUCT_STACKS

READER_KEY

RESOLUTION_KM

TIMESTAMP_SOURCE

TIME_MATCH

VARIABLES

file_paths

The staged granule paths passed at construction, in the order given.

n_variables

Number of read variables (the first axis of every values array).

single_file_path

The one granule of a reader that reads exactly one per period.

ADDITIONAL_PRODUCT_VARIABLES: tuple[VariableSpec, ...] = ()#
INSTRUMENT: str = 'NOAA20'#
MAX_TIME_OFFSET: timedelta64 | None = np.timedelta64(3,'h')#
PASS_WINDOW: timedelta64 | None = np.timedelta64(15,'m')#
PRODUCT_STACKS: tuple[ProductStack, ...] = ()#
READER_KEY: str = 'FLASHFlux'#
RESOLUTION_KM: float = 20.0#
TIMESTAMP_SOURCE: str | None = None#
TIME_MATCH: TimeMatch = 'nearest_pass'#
VARIABLES: tuple[VariableSpec, ...] = (VariableSpec(name='Aerosol_Optical_Depth', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Clear_Coverage', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Cloud_Optical_Depth_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Cloud_Water_Particle_Radius_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Cloud_Ice_Particle_Radius_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Cloud_Classification', dtype='int16', aggregation='weighted_mode', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Shortwave_ADM_Type', dtype='int16', aggregation='weighted_mode', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Longwave_ADM_Type', dtype='int16', aggregation='weighted_mode', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>, <OperationalMode.IMAGER_CAMTIME: 'FMATCH-IMAGER-CAMTIME'>}), n_categories=None), VariableSpec(name='Layer_Coverage_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Layer_Coverage_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Coverage_Multilayer_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Coverage_Multilayer_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Top_Pressure_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Top_Pressure_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Base_Pressure_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Base_Pressure_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Particle_Phase_37um_Lower', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Particle_Phase_37um_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Optical_Depth_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Water_Particle_Radius_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Cloud_Ice_Particle_Radius_Upper', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Match_AOT', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Aerosol_Type_Percentage_Type0', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Aerosol_Type_Percentage_Type1', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Aerosol_Type_Percentage_Type2', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Aerosol_Type_Percentage_Type3', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Aerosol_Type_Percentage_Type4', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Aerosol_Type_Percentage_Type5', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Aerosol_Type_Percentage_Type6', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Imager_Dark_Target_Land_055um_Corrected_Aerosol_Optical_Depth', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Imager_Deep_Blue_Ocean_055um_Aerosol_Optical_Depth', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='Surface_Albedo', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None), VariableSpec(name='TOA_Incoming_Solar_Radiation', dtype='float32', aggregation='weighted_mean', modes=frozenset({<OperationalMode.IMAGER: 'FMATCH-IMAGER'>}), n_categories=None))#
_box_samples(native: GridSamples | _PointIndex | _BoxNative, bbox: BoundingBox) → tuple[ndarray, ndarray, ndarray, ndarray | None]#

Return (lats, lons, values, times) of the native samples inside one plain box.

Raises:

TypeError – If native is none of the three native forms.

classmethod _check_product_stacks() → None#

Reject a PRODUCT_STACKS declaration the product could not be built from.

Raises:

TypeError – If a stack names a spec the reader does not produce, a spec is stacked twice, a stack takes the name of a product spec, a stack’s members have standard-deviation companions for some members but not others, or a stack’s members do not all have the same modes.

_granule_span(path: Path) → tuple[datetime64, datetime64]#

The granule’s time_coverage_start / time_coverage_end global attributes.

Raises:

ValueError – If either attribute is missing or is not an ISO 8601 instant.

_index_points(native: PointSamples) → _PointIndex#

Validate a loaded point cloud against the reader’s contract and index it.

_load_granule(path: Path) → PointSamples#

Parse one granule into a time-stamped point cloud.

Returns:

lats/lons are (n_footprints,) (longitude normalized to -180..180), values is (n_var, n_footprints) float32 with fill / out-of-range entries as NaN, times is datetime64[ns] per footprint (NaT where the file holds fill), and spacing_km the footprint spacing measured on this granule (the larger of along-scan and along-track).

Return type:

PointSamples

Raises:

ValueError – If the granule’s time variable cannot be decoded (see decode_cf_times), if its footprint latitude is not in degrees_north, if a group or variable read is missing, if a field’s dimensions differ from the ones the reader expects, or if a field read (geolocation and time included) has no _FillValue or valid_range attribute.

_native_for(time: datetime64) → tuple[_PointIndex, ...]#

Return the native data a query at time draws from; empty for no match.

StaticReader returns its one indexed body of data regardless of time; SteppedGridReader the step nearest time, or nothing when that step is farther than MAX_TIME_OFFSET.

_nearest_pass_mask(times: ndarray, time: datetime64, inside: ndarray) → ndarray#

Boolean mask of the samples that belong to the pass nearest time.

The nearest pass is every sample, padding included, within PASS_WINDOW of the observation time of the sample nearest time among the samples inside the footprint’s own (unpadded) box, so a pass that only clips the padding, where the PSF weight is zero, cannot displace one that covers the footprint. The window is centred on that sample’s own time rather than on its offset, so two passes on either side of the footprint time are never served together. When no sample lies inside the box (a footprint smaller than one native cell), the nearest sample is taken from every served sample; on a tie, the first in array order. When its offset exceeds MAX_TIME_OFFSET no sample is kept.

_padded(bbox: BoundingBox, resolution_km: float) → BoundingBox#

Widen bbox by half a native cell (resolution_km) on every side.

A sample whose cell overlaps the footprint’s box belongs to the footprint even when its center lies just outside: without the padding a coarse grid (ERA5 at 0.25 degree) never serves the points whose cells cover the rim of the contour, and the weigher scores the rim as missing. The PSF weight of a sample outside the contour is zero, so padding changes no aggregated value, only the coverage.

_require_valid_time(time: datetime64) → None#

Raise when a footprint time a time-selecting query needs is NaT.

Raises:

ValueError – If time is NaT.

_resolution_km(natives: tuple[GridSamples | _PointIndex | _BoxNative, ...]) → float#

The native cell size the served samples stand for, km.

The largest spacing a loaded point cloud measured on itself, else the reader’s declared RESOLUTION_KM. A measured spacing wins because a swath subsampled on delivery (e.g. every eighth pixel) must be credited with the cell it really stands for, not the instrument’s nominal one.

property _spans: list[tuple[datetime64, datetime64, Path]]#

(start, end, path) per staged granule, ascending by start.

_unknown_native_message(native: object) → str#
property file_paths: tuple[Path, ...]#

The staged granule paths passed at construction, in the order given.

granules_in_window(time: datetime64) → list[Path]#

The staged granules whose span lies within MAX_TIME_OFFSET of time.

Parameters:

time (np.datetime64) – A footprint time.

Returns:

The granules whose (start, end) span overlaps time +/- MAX_TIME_OFFSET, ascending by start; empty when none does.

Return type:

list[Path]

Raises:

ValueError – If a staged granule’s span is invalid: a start or end that is NaT, or an end before its start.

property n_variables: int#

Number of read variables (the first axis of every values array).

preload() → None#

Load whatever this reader holds for the whole run, ahead of the first query.

A process pool calls this before forking so the workers share one copy of the loaded data. This base implementation loads nothing: a reader that loads by time step or pass window loads on the query that needs it. StaticReader overrides it to load its whole source.

classmethod product_stacks() → tuple[ProductStack, ...]#

Return every stacked product variable this reader contributes.

Each stack in PRODUCT_STACKS, followed by a <name>_Standard_Deviation stack of its members’ companions when its members are continuous. The stacked members leave the product as separate variables; every other spec in product_variable_specs stays a 1-D product variable.

classmethod product_variable_specs() → tuple[VariableSpec, ...]#

Return every variable this reader contributes to the product definition.

This is the full set of output variables – what appears, prefixed with <READER_KEY>_, in the FMATCH product definition YAMLs and what the product/test cross-check is built against. It is distinct from VARIABLES (the smaller set actually read from the source file) and is composed of, in order:

  1. each read variable in VARIABLES;

  2. a <name>_Standard_Deviation companion for every continuous (mean-aggregated) read variable, inserted right after its parent by with_standard_deviation_companions();

  3. any reader-specific derived outputs declared in ADDITIONAL_PRODUCT_VARIABLES (e.g. IGBP ranked scenes).

samples_in(bbox: BoundingBox, time: datetime64) → SampleSet#

Return the native samples inside bbox that match time.

Parameters:
  • bbox (BoundingBox) – The footprint’s geographic box, widened by half a native cell (RESOLUTION_KM) on every side so a sample whose cell overlaps the box is served. A box that wraps the antimeridian (given in the [0, 360) form) is split and both halves served.

  • time (np.datetime64) – The footprint’s observation time (radiometer or camera time). Ignored by a DAILY reader.

Returns:

The selected samples; zero samples when the box holds none, or when no sample lies within MAX_TIME_OFFSET of time.

Return type:

SampleSet

Raises:
  • ValueError – If time is NaT and this reader selects in time; if more than one granule is staged for a reader that reads one per period; if the loaded data carries a different number of variables than VARIABLES; if a stepped reader’s step times are empty, hold a NaT or are not strictly ascending; or if a NEAREST_PASS reader serves samples without per-sample times.

  • TypeError – If the reader’s native data is none of GridSamples, PointSamples or a box-queried native.

property single_file_path: Path#

The one granule of a reader that reads exactly one per period.

Raises:

ValueError – If more than one granule was staged for this reader.