curryer.compute.geometry

Selective geometric data-field computation.

This module computes the geolocation/geometry ancillary data fields that geolocated products commonly need. It is organized in two layers, both mission-generic:

  • Math-only leaf functions (sc_radius, colatitude, subobserver_point, earth_sun_distance, satellite_altitude) – pure, vectorized, SPICE-free. They take high-level inputs (positions) as arguments and can be called directly to compose custom fields; the coordinate/frame primitives they build on (e.g. ecef_to_geodetic) stay in curryer.compute.spatial.

  • A selective-compute registry – a declarative map from each output field to the SPICE-derived inputs (“providers”) it needs. A caller requests an arbitrary subset of fields and only the inputs that subset needs are queried, each provider exactly once, never per-field. This is the pre-built convenience over the leaf tools.

Why a registry rather than one method per field: the field-to-input mapping is many-to-many and growing. The registry computes the minimal provider set for any requested subset automatically; the naive alternative (each field querying its own inputs) re-queries SPICE redundantly.

Two public accessors are exposed on GeometryData:

  • get_geometry(ugps_times, fields) -> pandas.DataFrame – the primary, tabular API. Vector fields expand to per-field-prefixed columns.

  • get_vectors(ugps_times, fields) -> {field: (N, k) ndarray} – the typed sibling, addressed by field name rather than by string-built column prefixes.

Mission genericity: the observing body is the only mission input (a constructor argument). Only universal identifiers (EARTH, SUN, ITRF93) appear in code – no spacecraft/instrument names are hardcoded.

Frame contract: the Earth-fixed (ECEF) fields all share a single reference frame – the earth_frame configured on GeometryData (ITRF93 by default) – so fields are never combined in mismatched reference frames.

Fill contract: SPICE coverage gaps (and off-Earth samples) surface as NaN. The providers query with allow_nans (True by default), so an uncovered time yields a NaN provider row; the math-only leaves are pure and propagate NaN elementwise, so every field is NaN on exactly the rows its inputs are missing and finite elsewhere – per time, per field. Downstream maps those NaNs onto the product _FillValue (e.g. -999); the rows are never dropped or back-filled.

Available fields: request any by name (GeometryData.available_fields() lists them). Each field expands to the columns below.

  • sc_radius -> spacecraft_radius – observer distance from Earth center.

  • subsatellite -> subsatellite_latitude, subsatellite_longitude, subsatellite_colatitude – ground point beneath the spacecraft.

  • subsolar -> subsolar_latitude, subsolar_longitude, subsolar_colatitude – ground point beneath the Sun.

  • earth_sun_distance -> earth_sun_distance – Earth-Sun distance (AU).

  • sc_position -> spacecraft_position_x, spacecraft_position_y, spacecraft_position_z – spacecraft position (ECEF).

  • sc_altitude -> spacecraft_altitude – observer geodetic height above the ellipsoid (km).

Attributes

Classes

_Field

Registry entry for a single output field.

GeometryData

Selective geometric data-field server.

Functions

sc_radius(→ numpy.ndarray)

Distance of the observer from the body center (vectorized).

colatitude(→ numpy.ndarray)

Convert geodetic latitude to colatitude (vectorized).

subobserver_point(→ numpy.ndarray)

Sub-observer geodetic latitude, longitude, and colatitude (vectorized).

earth_sun_distance(→ numpy.ndarray)

Distance between Earth and Sun (vectorized).

satellite_altitude(→ numpy.ndarray)

Geodetic altitude of the observer above the ellipsoid (vectorized).

_provider_sc_position(ugps_times, ctx)

Observer (spacecraft) position in the configured Earth-fixed frame

_provider_sun_position(ugps_times, ctx)

Earth-to-Sun position in the configured Earth-fixed frame

Module Contents

curryer.compute.geometry.logger
curryer.compute.geometry.sc_radius(observer_position: numpy.ndarray) numpy.ndarray

Distance of the observer from the body center (vectorized).

Implements the “radius of satellite from center of Earth” field.

Parameters:

observer_position (np.ndarray) – Observer (e.g., spacecraft) positions in rectangular coordinates, shape (…, 3). Units are arbitrary; the result is in the input’s units.

Returns:

Euclidean distance ||position|| per point, shape (…,).

Return type:

np.ndarray

curryer.compute.geometry.colatitude(latitude: numpy.ndarray, degrees: bool = True) numpy.ndarray

Convert geodetic latitude to colatitude (vectorized).

Colatitude is the complement of latitude (90 - lat), ranging from 0 at the north pole to 180 at the south pole. Implements the surface-point colatitude field; also reused by the sub-point fields.

Parameters:
  • latitude (np.ndarray) – Geodetic latitude.

  • degrees (bool, optional) – If True (default), inputs and outputs are in degrees, otherwise radians.

Returns:

Colatitude in the same units as the input.

Return type:

np.ndarray

curryer.compute.geometry.subobserver_point(observer_position: numpy.ndarray, degrees: bool = True) numpy.ndarray

Sub-observer geodetic latitude, longitude, and colatitude (vectorized).

The sub-observer point is the geodetic ground point directly beneath the observer; it shares the observer’s geodetic latitude and longitude. Generic over the observing body: pass the spacecraft position for the sub-satellite point or the Sun position for the sub-solar point.

Parameters:
  • observer_position (np.ndarray) – Observer positions in ECEF rectangular coordinates (km), shape (…, 3).

  • degrees (bool, optional) – If True (default), latitude/longitude/colatitude are in degrees, otherwise radians. Longitude follows ecef_to_geodetic (-180 to 180).

Returns:

Stacked [latitude, longitude, colatitude], shape (…, 3).

Return type:

np.ndarray

curryer.compute.geometry.earth_sun_distance(earth_sun_position: numpy.ndarray, au: bool = True) numpy.ndarray

Distance between Earth and Sun (vectorized).

Implements the Earth-Sun distance field. The input is the Earth-to-Sun position vector (any frame, since only the magnitude is used).

Parameters:
  • earth_sun_position (np.ndarray) – Earth-to-Sun position in rectangular coordinates (km), shape (…, 3).

  • au (bool, optional) – If True (default), return astronomical units, otherwise kilometers.

Returns:

Earth-Sun distance per point, shape (…,).

Return type:

np.ndarray

curryer.compute.geometry.satellite_altitude(observer_position: numpy.ndarray) numpy.ndarray

Geodetic altitude of the observer above the ellipsoid (vectorized).

The height-above-ellipsoid component of the observer’s geodetic position – the piece subobserver_point drops when it returns latitude/longitude/colatitude. Complements sc_radius (the geocentric distance from the body center).

Parameters:

observer_position (np.ndarray) – Observer (e.g., spacecraft) positions in ECEF rectangular coordinates (km), shape (…, 3).

Returns:

Geodetic altitude in km, shape (…,).

Return type:

np.ndarray

class curryer.compute.geometry._Field

Registry entry for a single output field.

Parameters:
  • providers (frozenset of str) – Keys of the providers (high-level inputs) this field requires.

  • columns (tuple of str) – Output column names. One column for scalar fields, three for vectors.

  • evaluate (Callable) – Maps the gathered provider dict to an (N, len(columns)) array.

providers: frozenset[str]
columns: tuple[str, Ellipsis]
evaluate: collections.abc.Callable[[dict], numpy.ndarray]
curryer.compute.geometry._provider_sc_position(ugps_times, ctx)

Observer (spacecraft) position in the configured Earth-fixed frame (ctx.earth_frame, ITRF93 by default), shape (N, 3), km.

curryer.compute.geometry._provider_sun_position(ugps_times, ctx)

Earth-to-Sun position in the configured Earth-fixed frame (ctx.earth_frame, ITRF93 by default), shape (N, 3), km.

curryer.compute.geometry._PROVIDERS
curryer.compute.geometry._FIELDS
curryer.compute.geometry._EPHEMERIS_PROVIDERS
curryer.compute.geometry._DEFAULT_FIELDS
class curryer.compute.geometry.GeometryData(observer, microsecond_cadence=None, earth='EARTH', sun='SUN', earth_frame=None)

Bases: curryer.compute.abstract.AbstractMissionData

Selective geometric data-field server.

Construct with the observing body, then request any subset of registered fields via get_geometry() (DataFrame) or get_vectors() (typed {field: ndarray}). Only the SPICE inputs the requested subset needs are queried, each exactly once. Relevant kernels must already be loaded for the requested times, mirroring the other compute servers.

The requested times are the independent variable: get_geometry() and get_vectors() evaluate every field at exactly the ugps_times passed – an arbitrary, possibly non-uniform array – with no resampling or interpolation. microsecond_cadence / get_times() only offers an optional uniform grid for callers that want one; it never constrains which times are queried.

Parameters:
  • observer (str or int or spicierpy.obj.Body) – Observing body whose position is queried – typically the instrument or spacecraft.

  • microsecond_cadence (int, optional) – Default cadence for get_times(), in microseconds.

  • earth (str or int or spicierpy.obj.Body, optional) – Central body and solar body for the ephemeris queries. Default "EARTH" / "SUN"; overridable so no body name is fixed in code.

  • sun (str or int or spicierpy.obj.Body, optional) – Central body and solar body for the ephemeris queries. Default "EARTH" / "SUN"; overridable so no body name is fixed in code.

  • earth_frame (str or spicierpy.obj.Frame, optional) – Earth-fixed (ECEF) reference frame. Default spatial.EARTH_FRAME (ITRF93).

DEFAULT_CADENCE = 60000000
observer
earth = 'EARTH'
sun = 'SUN'
earth_frame = 'ITRF93'
classmethod available_fields()

Tuple of registered field names.

_resolve_fields(fields)

Validate the requested fields, defaulting to the ephemeris-only set.

_gather_providers(fields, ugps_times)

Query the minimal set of providers for fields, once each.

Providers are evaluated in a stable (sorted) order so runs are reproducible. A provider that returns all-NaN over the whole grid is the documented missing-kernel signal, so it is logged as a warning.

get_geometry(ugps_times: numpy.ndarray, fields: list[str] | None = None) pandas.DataFrame

Compute the requested fields as a table.

Parameters:
  • ugps_times (array_like of int) – One or more times in GPS microseconds. Arbitrary and need not be uniformly spaced; each time is evaluated exactly (no interpolation).

  • fields (list of str, optional) – Field names to compute. Default is the ephemeris-only set – the fields computable from position/ephemeris providers alone.

Returns:

One row per time (index ugps); vector fields expand to per-field-prefixed columns (e.g. spacecraft_position_x, spacecraft_position_y, spacecraft_position_z). Times outside SPICE coverage are NaN across that field’s columns (see the module Fill contract).

Return type:

pandas.DataFrame

get_vectors(ugps_times: numpy.ndarray, fields: list[str]) dict[str, numpy.ndarray]

Compute the requested fields as typed arrays.

The typed sibling of get_geometry(), addressed by field name rather than by string-built column prefixes.

Parameters:
  • ugps_times (array_like of int) – One or more times in GPS microseconds. Arbitrary and need not be uniformly spaced; each time is evaluated exactly (no interpolation).

  • fields (list of str) – Field names to compute.

Returns:

dict of {str – Maps each field name to its (N, k) array. Vector fields (e.g. sc_position) are (N, 3) in the configured Earth-fixed frame (ITRF93 by default). Rows outside SPICE coverage are NaN (see the module Fill contract).

Return type:

numpy.ndarray}