API Reference

Use Getting Started for examples. The reference below reads signatures, defaults, and docstrings directly from the code. Each definition links to its source.

The API is a short pipeline, and this page follows it in order:

Step

Use

You get

1. Get data

ObservationFetcher, load_observations(), or Observation.from_array

Observation

2. Load a model

FOXESModel(checkpoint)

FOXESModel

3. Predict

FOXESModel.predict / predict_time_series()

Prediction / PredictionTimeSeries

4. Look at the results

attributes and plot_* methods on the result

numbers and figures

The AIA, HARP, and SXR processors at the end are the building blocks the fetcher uses; most people won’t need them directly.

ObservationFetcher

Fetch everything you need for a prediction at a given time.

Observation

One processed AIA stack, with its metadata and any HARP or GOES data.

load_observations

Load a folder of saved observations, one at a time.

FOXESModel

A FOXES model loaded from a checkpoint, ready to make predictions.

Prediction

What the model predicts for one AIA stack.

PredictionTimeSeries

A set of predictions over time, for light curves.

AIAProcessor

Download AIA images and turn them into a stack the model can use.

HARPProcessor

Look up HMI active regions (HARPs) and place their boxes on an AIA crop.

SXRProcessor

Fetch measured GOES X-ray flux to compare against predictions.

parse_timestamp

Parse a date/time and return it as a timezone-aware UTC datetime.

1. Get Data

ObservationFetcher

class helio_foxes.ObservationFetcher(*, email=None, include_aia=True, include_harps=True, include_sxr=True, resolution=512, aia_tolerance=120, n_workers=4, harp_bbox_expansion=0.0, sxr_tolerance=120, sxr_satellite=None)[source]

Fetch everything you need for a prediction at a given time.

This is the usual starting point. fetch() returns an Observation and fetch_range() yields one per time; pass them to FOXESModel to predict. It wraps AIAProcessor, HARPProcessor, and SXRProcessor and bundles what they return into an Observation. All three sources are on by default. Any of them can be turned off here or for a single call.

The sources fail differently. If the AIA download or processing fails, you get an error, since there’s nothing to predict on. HARP and GOES are extras: if they fail you get an empty region list or sxr_data=None and the observation still comes back. A source you turned off shows up as None (aia_data=None for AIA, harp_regions=None for HARPs).

Set up the data sources.

Parameters:
  • email (str or None, optional) – Your registered JSOC email, used for both AIA and HARP queries. Defaults to the JSOC_EMAIL environment variable.

  • include_aia (bool, default True) – Which sources to fetch by default. Each fetch call can override these.

  • include_harps (bool, default True) – Which sources to fetch by default. Each fetch call can override these.

  • include_sxr (bool, default True) – Which sources to fetch by default. Each fetch call can override these.

  • resolution (int, default 512) – Processed AIA image size in pixels. Should match the checkpoint.

  • aia_tolerance (int, default 120) – How far, in seconds, each AIA frame can be from the requested time.

  • n_workers (int, default 4) – Number of parallel AIA download threads.

  • harp_bbox_expansion (float, default 0.0) – Fraction of each HARP box’s width and height added on every side (0.1 grows each side by 10%). HARP flux sums use the expanded boxes.

  • sxr_tolerance (float, default 120) – How far, in seconds, a GOES sample can be from the requested time.

  • sxr_satellite (int or None, optional) – Use only this GOES satellite (e.g. 18). None averages all satellites with good data.

fetch(time, *, include_aia=None, include_harps=None, include_sxr=None) → Observation[source]

Fetch one observation.

Raw downloads are deleted before the method returns. The HARP and GOES data stay attached to the observation, so they show up again when you predict and plot.

Parameters:
  • time (datetime.datetime, datetime.date, numpy.datetime64, or str) – Any format parse_timestamp() accepts.

  • include_aia (bool or None, optional) – Turn a source on or off for this call only. None keeps the fetcher’s default. You need AIA to make a prediction.

  • include_harps (bool or None, optional) – Turn a source on or off for this call only. None keeps the fetcher’s default. You need AIA to make a prediction.

  • include_sxr (bool or None, optional) – Turn a source on or off for this call only. None keeps the fetcher’s default. You need AIA to make a prediction.

Returns:

Observation

fetch_range(start, end, *, cadence='1h', on_error='raise', save_dir=None, overwrite=False, progress=False, include_aia=None, include_harps=None, include_sxr=None)[source]

Fetch observations over a time range, one at a time.

This is a generator, so nothing gets downloaded until you start looping over it (or hand it to FOXESModel.predict_time_series()). Only one AIA stack is held in memory at a time, which keeps long ranges manageable.

Which times. The range starts at start and steps forward by cadence. end is included only if it falls exactly on one of those steps. There’s no interpolation, so two requested times close together can end up using the same AIA exposure.

How it downloads. AIA frames for the whole range are found with a single JSOC query, then downloaded. HARP and GOES are still queried separately for each time. Very long ranges can produce large JSOC responses, so it’s worth splitting them into chunks.

Saving and reusing. With save_dir set, each observation is saved with Observation.save() before it’s yielded. On a later run, any time that already has a file is loaded from disk, with no downloads or queries, and only the missing times are fetched. Saved files are reused exactly as they are, so if you change preprocessing or source settings, pass overwrite=True to fetch everything again. Reload a saved folder without the fetcher using load_observations().

Errors. By default an AIA failure stops the iterator. With on_error="skip", the failure is logged with its time and the iterator carries on. HARP and GOES failures never stop it (see fetch()). Problems with the initial JSOC query, or with reading or writing saved files, always raise.

Parameters:
  • start (datetime.datetime, datetime.date, numpy.datetime64, or str) – First and last times, in any format parse_timestamp() accepts. end can’t be before start.

  • end (datetime.datetime, datetime.date, numpy.datetime64, or str) – First and last times, in any format parse_timestamp() accepts. end can’t be before start.

  • cadence (str, datetime.timedelta, pandas.Timedelta, int, or float, default "1h") – Time between observations, as a whole number of seconds. Use a pandas-style string like "12min", a timedelta, or a plain number of seconds (so 720 is 12 minutes).

  • on_error ({"raise", "skip"}, default "raise") – What to do when a single time fails to download or process.

  • save_dir (str or pathlib.Path or None, optional) – Folder to save observations in and reuse them from.

  • overwrite (bool, default False) – Fetch every time again and replace any saved files.

  • progress (bool, default False) – Show a tqdm progress bar for the JSOC query and downloads, or for loading files if everything is already saved. If AIA is turned off, it tracks the HARP and GOES fetches instead.

  • include_aia (bool or None, optional) – Turn a source on or off for this range only, as in fetch(). None keeps the fetcher’s default.

  • include_harps (bool or None, optional) – Turn a source on or off for this range only, as in fetch(). None keeps the fetcher’s default.

  • include_sxr (bool or None, optional) – Turn a source on or off for this range only, as in fetch(). None keeps the fetcher’s default.

Yields:

Observation – One per successful time, in time order.

Examples

>>> observations = fetcher.fetch_range(
...     "2023-08-01 00:00", "2023-08-01 01:00", cadence="12min",
...     save_dir="saved_observations", progress=True,
... )
>>> series = model.predict_time_series(observations)

Observation

class helio_foxes.Observation(metadata: ObservationMetadata, aia_data: ndarray | None, sxr_data: dict | None = None, harp_regions: list[dict] | None = None)[source]

One processed AIA stack, with its metadata and any HARP or GOES data.

You’ll usually get these from ObservationFetcher, or build one from your own array with from_array(). Pass it to FOXESModel.predict() to get a Prediction.

Parameters:
  • metadata (ObservationMetadata) – Time, channel order, and crop geometry for aia_data.

  • aia_data (array-like or None) – Channel-first (C, H, W) array matching metadata.image_shape. None if AIA fetching was turned off. from_array() checks the values and converts them to float32; this constructor doesn’t.

  • sxr_data (dict or None, optional) – Measured GOES flux from SXRProcessor.fetch(), with xrsa_flux and xrsb_flux in W/m². It’s only there for comparison; the model never sees it. None if SXR was turned off or no data was available.

  • harp_regions (list of dict or None, optional) – Active-region boxes. Each dict needs a HARPNUM (or harpnum) and top_pct, left_pct, width_pct, and height_pct, given as percentages of this crop measured from its top-left corner. For example {"HARPNUM": 1234, "top_pct": 10, "left_pct": 20, "width_pct": 15, "height_pct": 12}. None means HARPs were never requested. An empty list means they were requested but nothing usable came back, which includes failed queries. Prediction won’t query HARPs for you.

classmethod from_array(aia_data, *, timestamp, wavelengths=(94, 131, 171, 193, 211, 304, 335), padding_factor=0.1, sxr_data=None, harp_regions=None)[source]

Wrap an AIA stack you’ve already preprocessed.

The array is used as is. Nothing is downloaded, and there’s no calibration, cropping, resizing, or normalization, so the stack needs to match the training preprocessing already. The model checks shape and channel order when you run it.

Parameters:
  • aia_data (array-like) – Finite numeric (C, H, W) stack, usually (7, 512, 512). A NumPy array or nested lists both work. Channels-last arrays aren’t transposed for you. The observation stores a float32 copy.

  • timestamp (datetime.datetime, datetime.date, numpy.datetime64, or str) – Required. Any format parse_timestamp() accepts.

  • wavelengths (sequence of int, optional) – Wavelength of each channel in Ångströms. Prediction needs the default (94, 131, 171, 193, 211, 304, 335).

  • padding_factor (float, default 0.1) – Padding past the solar limb used when the stack was cropped.

  • sxr_data (dict or None, optional) – Measured GOES flux; see Observation.

  • harp_regions (list of dict or None, optional) – Active-region boxes in percent of this crop; see Observation.

Returns:

Observation

save(path, *, overwrite=False)[source]

Save the whole observation to a compressed .npz file.

The file holds the array (if there is one) and the metadata: time, crop geometry, channel order, HARP regions, and SXR data. Reload it with load() and it’s ready to predict on, with no need to preprocess or normalize it again. The data is saved exactly as is, and pickle isn’t used.

The file is written to a temporary name first and only moved into place once it’s complete, so an interrupted save won’t leave a half-written archive behind.

Parameters:
  • path (str or pathlib.Path) – Destination ending in .npz. Missing parent folders are created and ~ is expanded.

  • overwrite (bool, default False) – Replace an existing file. If False and the file already exists, raises FileExistsError.

Returns:

pathlib.Path – Where the archive was written.

classmethod load(path)[source]

Reload an observation written by save().

Existing saved observations load into aia_data and sxr_data without conversion.

Parameters:

path (str or pathlib.Path) – A .npz archive from save(). For a plain .npy array, use Observation.from_array(np.load(path), timestamp=...).

Returns:

Observation – The saved array and metadata, along with any HARP and SXR data.

ObservationMetadata

class helio_foxes.api.ObservationMetadata(timestamp: datetime | str, image_shape: tuple[int, int, int], wavelengths: tuple[int, ...] = (94, 131, 171, 193, 211, 304, 335), padding_factor: float = 0.1, harp_bbox_expansion: float = 0.0)[source]

Time, channel order, and crop geometry for one processed AIA stack.

The metadata travels with an observation through prediction and plotting, so HARP boxes can be drawn in the right place later on.

Parameters:
  • timestamp (datetime.datetime, datetime.date, numpy.datetime64, or str) – The time you asked for, in any format parse_timestamp() accepts. Each channel’s actual exposure time can differ slightly.

  • image_shape (sequence of three positive int) – Array shape in channel-first order, e.g. (7, 512, 512). If AIA fetching was turned off, this is the shape the crop would have had.

  • wavelengths (sequence of int, optional) – Wavelength of each channel in Ångströms, in array order. Prediction needs the default (94, 131, 171, 193, 211, 304, 335).

  • padding_factor (float, default 0.1) – How far the crop extends past the solar limb, as a fraction of the solar radius. Please keep it consistent with the crop used to train the model (0.1).

  • harp_bbox_expansion (float, default 0.0) – Fraction of each HARP box’s width and height added on every side, so 0.1 grows each side by 10%. Stored here so HARPs fetched later, at plotting time, get the same expansion.

load_observations

helio_foxes.load_observations(directory, *, progress=False)[source]

Load a folder of saved observations, one at a time.

Files are read in filename order. The names that ObservationFetcher.fetch_range() writes are timestamps, so that’s chronological order. Only one file is in memory at a time, so you can pass the result straight to FOXESModel.predict_time_series():

series = model.predict_time_series(load_observations("saved_observations"))

Raises FileNotFoundError if the folder is missing or has no .npz files. A broken archive raises an error rather than being skipped.

Parameters:
  • directory (str or pathlib.Path) – Folder of .npz files written by Observation.save(). ~ is expanded. Every .npz file in the folder is loaded, so keep normalization files and other archives somewhere else.

  • progress (bool, default False) – Show a tqdm progress bar.

Yields:

Observation

2. Load a Model and Predict

FOXESModel

class helio_foxes.FOXESModel(checkpoint='uncertainty', *, device='auto', legacy_correction_enabled=None, legacy_cutoff='2016-11-19')[source]

A FOXES model loaded from a checkpoint, ready to make predictions.

Loading takes a while, so do it once and reuse the model:

model = FOXESModel()  # the current uncertainty model
result = model.predict(observation)

Give predict() an Observation to get a Prediction, or give predict_time_series() many of them to get a PredictionTimeSeries.

Parameters:
  • checkpoint (str or pathlib.Path, default "uncertainty") – A released checkpoint name or a path to a checkpoint file. The names are "uncertainty" (the current model) and the legacy "inverted-attention-mask", "localized-attention-mask", and "no-attention-mask". A named checkpoint is downloaded the first time you use it and cached in ~/.cache/helio_foxes (set the HELIO_FOXES_CACHE environment variable to use another folder). If a file with that name exists, the file wins. ~ is expanded in paths.

  • device ({"auto", "cpu", "cuda", "mps"}, default "auto") – Where to run the model. "auto" uses a GPU if one is available.

  • legacy_correction_enabled (bool or None, default None) – Turn on the approximate correction for the old deterministic checkpoints: flux (full-disk and per patch) is multiplied by 0.7 for times before legacy_cutoff. None turns it on for the legacy checkpoint names and off for everything else, including file paths, so pass True if you load a legacy checkpoint from a file. Keep it off for UncertaintyModel.ckpt and any model trained on the corrected targets. The weights and normalization themselves aren’t changed.

  • legacy_cutoff (datetime.datetime or str, default "2016-11-19") – Predictions before this UTC time get the 0.7 factor. It’s a rough stand-in for the change in GOES satellites, not a fitted calibration.

predict_time_series(observations, *, snapshot_time=None, snapshot_wavelength=193)[source]

Run the model over a sequence of observations.

Observations are processed one at a time as the iterable is consumed, so this works well with generators like ObservationFetcher.fetch_range() and load_observations(). The input stacks aren’t kept. If any prediction fails, the error is raised.

Those generators can only be looped over once. If you pass one that has already been used, you get an empty series and a warning. Call ObservationFetcher.fetch_range() again, or turn it into a list first if you need the observations more than once.

If you pass snapshot_time, one AIA channel from the observation closest to that time is kept (the earlier one wins a tie), so PredictionTimeSeries.plot_light_curves() can show it with show_snapshot=True.

Parameters:
  • observations (iterable of Observation) – A list, tuple, or generator of observations. Wrap plain arrays with Observation.from_array() first.

  • snapshot_time (datetime.datetime, datetime.date, numpy.datetime64, str, or None) – Time to keep a snapshot image for, in any format parse_timestamp() accepts. None keeps no image.

  • snapshot_wavelength (int, default 193) – Which AIA channel to keep, given as a wavelength in Ångströms (e.g. 171), not as an index.

Returns:

PredictionTimeSeries

predict(observation)[source]

Predict the full-disk flux and per-patch flux map for one observation.

Parameters:

observation (Observation) – From ObservationFetcher, load_observations(), or Observation.from_array() (for your own preprocessed arrays). Its AIA stack has to be (7, H, W) with channels in the order 94, 131, 171, 193, 211, 304, 335 Å, and H and W have to match the checkpoint (512 × 512 for the current one). Its metadata and HARPs are carried over to the result. Its measured GOES XRS-B flux becomes the result’s ground_truth.

Returns:

Prediction

Raises:
  • TypeError – If observation isn’t an Observation.

  • ValueError – If the observation has no AIA data, the shape or channel order doesn’t match the checkpoint, or the array has non-finite values.

3. Results and Plotting

Prediction

class helio_foxes.Prediction(total_flux: float, flux_map: ndarray, prediction_intervals: dict[str, tuple[float, float]] | None = None, flux_std_map: ndarray | None = None, harp_contributions: list[dict] | None = None, ground_truth: float | None = None, *, metadata: ObservationMetadata | None = None, legacy_correction_enabled: bool = False, flux_scale_factor: float = 1.0)[source]

What the model predicts for one AIA stack.

Returned by FOXESModel.predict(). Read the numbers from the attributes below, or draw them with plot_flux_map() and plot_uncertainty_map(). total_flux is the prediction and ground_truth is the matched measured GOES XRS-B flux. All fluxes are in W/m², for the 1–8 Å band.

The Gaussian checkpoint assigns a variance contribution to each patch. flux_std_map contains their square roots; the squares add to the full-disk variance. Each HARP’s flux_std is the square root of the summed variance contributions inside its box. These are model-assigned uncertainty attributions; calibration against individual regional X-ray measurements hasn’t been established.

metadata

Metadata from the input observation, kept so HARP boxes can be drawn later. The input array itself isn’t kept.

Type:

ObservationMetadata or None

total_flux

Predicted full-disk soft X-ray flux.

Type:

float

flux_map

2D map of each patch’s contribution to total_flux. It’s on the model’s patch grid (64 × 64 for the current checkpoint), not at image resolution.

Type:

numpy.ndarray

prediction_intervals

{"68": (low, high), "95": (low, high)} nominal full-disk prediction intervals in W/m². They are asymmetric because the Gaussian is defined in normalized log-flux space. None for deterministic checkpoints.

Type:

dict or None

flux_std_map

2D map of the standard-deviation contributions described above, on the same patch grid as flux_map. None for deterministic checkpoints. Add variances (squared values), rather than these values.

Type:

numpy.ndarray or None

harp_contributions

One dict per active region. Each has harpnum, the box position (top_pct, left_pct, width_pct, height_pct), and total_flux, the total of the positive patch contributions whose centers fall inside the box. Boxes can overlap, so a patch can count toward more than one region. flux_std is sqrt(sum(flux_std_map**2)) over the same patches, or None for deterministic checkpoints. The original JSOC columns, including the heliographic longitude and latitude bounds, are kept too. None means HARPs weren’t fetched; an empty list means none were usable.

Type:

list of dict or None

ground_truth

Matched measured GOES XRS-B flux, for comparison with total_flux. The sample may be offset from the requested time within the fetcher’s tolerance, and may average multiple satellites. Its provenance is in the input observation’s sxr_data. None if no sample is available.

Type:

float or None

legacy_correction_enabled

Whether legacy correction is enabled (see FOXESModel). This can be True with a scale factor of 1.0 after the cutoff.

Type:

bool

flux_scale_factor

Factor that was applied to total_flux and flux_map: 0.7 for legacy predictions before the cutoff date, otherwise 1.0. The measured ground_truth values are never scaled.

Type:

float

plot_flux_map(*, ax=None, cmap='inferno', vmin=None, vmax=None, show_harp_boxes=True, harp_regions=None, min_harp_flux=1e-7, harp_label_fontsize=6, show_harp_uncertainty=True, harp_label_offsets=None, harp_label_max_shift=80, aia_image=None, harp_colors=None)[source]

Plot the per-patch flux map on a log color scale.

The title shows the predicted full-disk flux (with its uncertainty, for Gaussian checkpoints) and the measured GOES XRS-B flux if it’s available. A plot with aia_image set gets no title. Patches with zero or negative flux are left blank.

HARP boxes. If the prediction has HARP regions, each one is outlined and labeled with its HARP number and summed flux (and, if available, ± its uncertainty). Colors go by flux rank, brightest first. Labels start just outside a box corner, on the side facing the image center. If labels overlap, the other corners are tried and then small nudges, up to harp_label_max_shift. Any label that moves gets a connector line back to its box. Placement is worked out once when you plot, so replot if you resize the figure a lot. On a crowded map, raise min_harp_flux or set harp_label_offsets by hand.

Parameters:
  • ax (matplotlib.axes.Axes or None, optional) – Axes to draw on. A new figure is created if omitted.

  • cmap (str or matplotlib.colors.Colormap, default "inferno") – Colormap name or instance.

  • vmin (float or None, optional) – Color-scale limits in W/m² (must be positive). Default to the smallest and largest positive values in the map.

  • vmax (float or None, optional) – Color-scale limits in W/m² (must be positive). Default to the smallest and largest positive values in the map.

  • show_harp_boxes (bool, default True) – Draw and label the HARP boxes, as described above. Nothing is drawn if there are no regions; plotting never queries JSOC.

  • harp_regions (list of dict or None, optional) – Regions to draw instead of the prediction’s own, for example from HARPProcessor.fetch(). Hand-made boxes need HARPNUM (or harpnum) and top_pct, left_pct, width_pct, height_pct in percent of the crop, from its top-left corner.

  • min_harp_flux (float, default 1e-7) – Hide regions whose summed flux is below this, in W/m². The prediction itself is unchanged.

  • harp_label_fontsize (float, default 6) – Label font size in points.

  • show_harp_uncertainty (bool, default True) – Add the ± uncertainty line to labels when the prediction has patch uncertainties.

  • harp_label_offsets (dict or None, optional) – {harpnum: (dx, dy)} starting offsets in points from the chosen box corner; positive dx moves right and positive dy moves up. Labels placed this way always get a connector.

  • harp_label_max_shift (float, default 80) – How far, in points, a label can be nudged automatically. Set it to 0 to keep every label at its starting corner. Nudged labels stay outside their box and inside the map. A label that can’t find a clear spot within this distance stays where it started.

  • aia_image (array-like or None, optional) – A processed 2D AIA channel to show instead of the flux map, with the same HARP boxes and labels. It has to cover exactly the same crop the prediction used. The labels still show predicted flux, not AIA intensity. The image is shown with a linear 1–99 percentile stretch (or vmin/vmax), pixel axes, and no colorbar. cmap="gray" usually looks best.

  • harp_colors (dict or None, optional) – {harpnum: color} to set specific region colors, e.g. to match another plot. Any matplotlib color works: "tab:blue", "#56B4E9", or (0.2, 0.5, 0.8). Other regions keep their rank colors.

Returns:

matplotlib.axes.Axes – The map axes. Flux maps also get a colorbar.

Raises:

ValueError – If the map isn’t a finite 2D array, has no positive values, or the color limits are invalid.

plot_uncertainty_map(*, ax=None, cmap='magma', vmin=None, vmax=None)[source]

Plot flux_std_map contributions with a logarithmic color scale.

Parameters:
  • ax (matplotlib.axes.Axes or None, optional) – Axes to draw on. A new figure and axes are created if omitted.

  • cmap (str or matplotlib.colors.Colormap, default "magma") – Colormap name such as “magma”, or a Colormap instance.

  • vmin (float or None, optional) – Positive limits for the logarithmic color scale in W/m². By default, use the smallest and largest positive values in the map.

  • vmax (float or None, optional) – Positive limits for the logarithmic color scale in W/m². By default, use the smallest and largest positive values in the map.

Returns:

matplotlib.axes.Axes – Axes containing the map. Its figure also has an uncertainty colorbar.

Raises:

ValueError – If the prediction has no uncertainty map (deterministic checkpoints), the map has no positive values, or the color limits are invalid. Nonpositive cells are masked.

PredictionTimeSeries

class helio_foxes.PredictionTimeSeries(predictions: tuple[Prediction, ...], *, snapshot: Observation | None = None)[source]

A set of predictions over time, for light curves.

You’ll normally get one from FOXESModel.predict_time_series(). Times are the requested UTC times. Any time that was skipped during download just won’t appear. When a HARP isn’t present at some time, its light curve has a NaN there rather than a zero. Regional fluxes are computed as in Prediction.

Parameters:
  • predictions (iterable of Prediction) – Predictions with metadata and no two sharing a timestamp. They’re sorted by time and stored as a tuple.

  • snapshot (Observation or None, optional) – A single AIA channel, shape (1, H, W), at one of the prediction times. It’s only used for the image panel in plot_light_curves().

full_disk_light_curve()[source]

Return the full-disk light curve as a DataFrame indexed by UTC time.

Columns are total_flux (predicted) and ground_truth (measured GOES XRS-B, if available), both in W/m². Legacy series also get legacy_correction_enabled and flux_scale_factor columns.

harp_light_curves()[source]

Return one light curve per active region, as {harpnum: DataFrame}.

Each DataFrame is indexed by UTC time and covers every prediction in the series, with total_flux and flux_std columns in W/m². If a region isn’t present at a time, or there’s no uncertainty, that value is NaN. Nothing is interpolated or filled with zeros.

plot_light_curves(*, harpnums=None, ax=None, show_full_disk=False, show_observed=False, show_snapshot=False, snapshot_ax=None, aia_ax=None, show_uncertainty=True, min_peak_flux=0.0, legend='auto')[source]

Plot HARP light curves, optionally with the full-disk total and GOES.

Every region in the series is plotted by default. The flux axis is logarithmic, so missing, zero, or negative values leave a gap in the line rather than being clipped to the bottom of the plot. Each HARP keeps the same color even when you filter regions out.

Uncertainty. For Gaussian checkpoints, each region gets a shaded band of ± one flux_std. A band whose lower edge would fall at or below zero is left out. Deterministic checkpoints get no bands. As in Prediction, these are the model’s uncertainty split across regions, not tested regional error bars.

Snapshot panels. With show_snapshot=True, two panels are added to the right of the curves: the flux map at the snapshot time (from Prediction.plot_flux_map(), with HARP boxes in the same colors as the curves) and the plain AIA image. A dashed line marks the snapshot time on the curves. You need to have called predict_time_series(..., snapshot_time=...) first. The snapshot is the observation closest to that time, with the earlier one winning a tie. Only regions that are selected and present at that time get boxes.

Parameters:
  • harpnums (iterable of int or None, optional) – Which HARPs to plot, e.g. [1234, 5678]. None plots all of them, and [] plots none (useful if you only want the totals). Wrap a single number in a list. Unknown numbers raise ValueError.

  • ax (matplotlib.axes.Axes or None, optional) – Axes for the light curves. If you’re also showing the snapshot and want to supply your own axes, pass all three (ax, snapshot_ax, aia_ax), all different and in the same figure. Otherwise leave them out and the layout is made for you.

  • show_full_disk (bool, default False) – Add the predicted full-disk flux and the measured GOES XRS-B flux for comparison. min_peak_flux doesn’t hide these.

  • show_observed (bool, default False) – Add the predicted full-disk flux and the measured GOES XRS-B flux for comparison. min_peak_flux doesn’t hide these.

  • show_snapshot (bool, default False) – Add the flux map and AIA image panels described above.

  • snapshot_ax (matplotlib.axes.Axes or None, optional) – Axes for the flux map and AIA image. See ax.

  • aia_ax (matplotlib.axes.Axes or None, optional) – Axes for the flux map and AIA image. See ax.

  • show_uncertainty (bool, default True) – Draw the uncertainty bands, and the ± lines on the snapshot’s HARP labels, where the checkpoint provides them.

  • min_peak_flux (float, default 0.0) – Hide HARPs whose highest flux over the whole series (not just at the snapshot) is below this value in W/m², e.g. 1e-7. Regions that never have a positive flux are always hidden. This applies to the snapshot labels as well, but it only affects the plot; the data in the series is unchanged.

  • legend ({"auto", "full", "compact", "none"}, default "auto") – Legend placed below the curves. "full" lists every region. "compact" shows a region count plus the comparison-line and marker keys, which is handy when the snapshot already labels the HARPs. "auto" picks compact when the snapshot is shown and full otherwise.

Returns:

matplotlib.axes.Axes – The light-curve axes. The snapshot panels, if any, are in the same figure.

Examples

>>> ax = series.plot_light_curves(show_snapshot=True,
...                  min_peak_flux=1e-7, legend="compact")

Lower-Level Building Blocks

AIAProcessor

class helio_foxes.AIAProcessor(*, email=None, resolution=512, tolerance=120, n_workers=4)[source]

Download AIA images and turn them into a stack the model can use.

Each stack has seven channels in the order 94, 131, 171, 193, 211, 304, and 335 Å. Processing uses the same ITI pipeline as training and gives you a channel-first float32 array. Most of the time you’ll want ObservationFetcher, which wraps this class and also fetches HARP and GOES data.

Parameters:
  • email (str or None, optional) – Your registered JSOC email address. Defaults to the JSOC_EMAIL environment variable.

  • resolution (int, default 512) – Height and width of each processed channel, in pixels.

  • tolerance (int, default 120) – How far, in seconds, each AIA frame can be from the requested time.

  • n_workers (int, default 4) – Number of parallel download threads.

WAVELENGTHS = (94, 131, 171, 193, 211, 304, 335)

AIA channels in Å, in the order the model expects them.

fetch(time)[source]

Download and process all seven channels for one time.

The raw FITS files go into a temporary folder that’s deleted when the method returns, even if something fails along the way.

Parameters:

time (datetime.datetime, datetime.date, numpy.datetime64, or str) – Any format parse_timestamp() accepts.

Returns:

numpy.ndarray – float32 array of shape (7, resolution, resolution) in WAVELENGTHS order.

Raises:
  • TypeError – If time isn’t a type parse_timestamp() understands.

  • FileNotFoundError – If any channel has no frame within tolerance seconds.

  • ValueError – If processing returns the wrong shape or non-finite values.

process_files(paths)[source]

Process AIA FITS files you already have on disk.

This runs the same pipeline as fetch(), just without the download, and your files are left untouched. Wrap the result with Observation.from_array() to predict on it:

paths = [Path("fits") / f"{w}.fits" for w in AIAProcessor.WAVELENGTHS]
stack = processor.process_files(paths)
observation = Observation.from_array(stack, timestamp="2023-08-01 12:00")
Parameters:

paths (iterable of str or pathlib.Path) – A list of exactly seven FITS files, one per channel, in WAVELENGTHS order. A folder name won’t work here.

Returns:

numpy.ndarray – float32 array of shape (7, resolution, resolution).

HARPProcessor

class helio_foxes.HARPProcessor(*, email=None, resolution=512, padding_factor=0.1, bbox_expansion=0.0)[source]

Look up HMI active regions (HARPs) and place their boxes on an AIA crop.

Boxes come from the hmi.sharp_720s series on JSOC and are converted to percentages of the processed AIA image, so they line up with the flux map.

Configure JSOC access and the AIA crop geometry.

Parameters:
  • email (str or None, optional) – Your registered JSOC email address. Defaults to the JSOC_EMAIL environment variable.

  • resolution (int, default 512) – Side length of the processed AIA image in pixels.

  • padding_factor (float, default 0.1) – How far the AIA crop extends past the solar limb, as a fraction of the solar radius.

  • bbox_expansion (float, default 0.0) – Fraction of each box’s width and height added on every side (0.1 grows each side by 10%). Expanded boxes are clipped to the image.

fetch(time: datetime | str) → list[dict][source]

Get the HARP regions closest in time to time.

Uses the nearest HARP record with good-quality regions. Each row keeps the original JSOC columns, including HARPNUM and the raw NOAA_ARS string. Regions visible in the crop also get top_pct, left_pct, width_pct, and height_pct.

HARPs are a nice-to-have, so if the query fails or nothing usable turns up, you get an empty list instead of an error. An invalid timestamp still raises.

Parameters:

time (datetime.datetime, datetime.date, numpy.datetime64, or str) – Any format parse_timestamp() accepts. JSOC’s own TAI record times aren’t supported.

Returns:

list of dict

SXRProcessor

class helio_foxes.SXRProcessor(*, tolerance=120, satellite=None)[source]

Fetch measured GOES X-ray flux to compare against predictions.

By default the fluxes from every GOES satellite with good data are averaged, the same way the training targets were built (see data.align_aia_sxr). The average is a plain mean of the physical fluxes, taken before any log scaling.

Set up the GOES lookup.

Parameters:
  • tolerance (float, default 120) – How far, in seconds, a GOES sample can be from the requested time.

  • satellite (int or None, optional) – Use only this GOES satellite, given as a number (18, not "GOES-18"). None averages all satellites with good data.

fetch(time) → dict | None[source]

Get the measured XRS-A (0.5–4 Å) and XRS-B (1–8 Å) flux near a time.

Finds the GOES sample closest to time that has good XRS-B data (quality flag zero and a positive flux), the same selection used for the training targets. If two samples are equally close, the earlier one wins. Only satellites reporting at that exact sample time are averaged. Values are used as the science product reports them, with no interpolation or extra scaling.

Like HARPs, GOES data is optional, so a missing sample or failed query returns None instead of raising. An invalid timestamp still raises.

Parameters:

time (datetime.datetime, datetime.date, numpy.datetime64, or str) – Any format parse_timestamp() accepts.

Returns:

dict or None – xrsb_flux and xrsa_flux in W/m² (xrsa_flux is None if no satellite had good XRS-A data), timestamp (the actual UTC sample time), time_offset_seconds (sample time minus requested time), satellites (which ones were averaged), and measurements (each satellite’s own values).

parse_timestamp

helio_foxes.parse_timestamp(value)[source]

Parse a date/time and return it as a timezone-aware UTC datetime.

Every function in FOXES that takes a time runs it through this parser, so the rules here apply everywhere:

  • A date with no time means midnight.

  • A time with no timezone is treated as UTC.

  • For other timezones, give an explicit offset such as -04:00. Abbreviations other than UTC and GMT (EST, for example) aren’t recognized.

Year-first formats work, and so do written-out month names. All-numeric dates like 01/08/2023 are rejected because the day/month order is ambiguous. Unix timestamps, partial dates (“August 2023”), relative dates, and NaT are rejected too.

Parameters:

value (datetime.datetime, datetime.date, numpy.datetime64, or str) – For example "2023-08-01", "2023-08-01T12:00:00Z", "2023/08/01 12:00", "2023.08.01_12:00:00_UTC", "August 1, 2023 12:00 UTC", or "2023-08-01T08:00:00-04:00".

Returns:

datetime.datetime – Timezone-aware UTC time.