name: timeseries-and-solar-physics
description: "Build, load, inspect, transform, plot, and round-trip SunPy
TimeSeries data locally, and apply SunPy solar constants, models, and
differential-rotation helpers without conflating local analysis with remote
acquisition."
metadata:
disco-role: operating
disable-model-invocation: true
license: BSD 2-Clause
SunPy time series and solar physics
Use this route when the task is about one-dimensional, time-indexed solar or
space-weather measurements, a deterministic local time-series analysis, solar
constants/models, or differential rotation. The verified public API surface is
SunPy 0.1.dev1+gd2ae0740e and Python >=3.12; treat other releases as
potentially version-sensitive.
Route the request first
- Create or analyze a local series: start with
workflows and units and data.
- Load a local mission file: read the source-loader section in
the API reference, then use a local path and
an explicit
source= when detection is ambiguous.
- Search or fetch GOES/HEK/CDAWeb data: stop here and hand acquisition to
data-access-and-io. This route consumes a
local file or already-created object; it does not perform an unbounded search
or download.
- Construct/transform coordinate frames: hand frame construction and
transformations to
coordinates-and-time.
solar_rotate_coordinate() is included here only as the time-dependent
differential-rotation operation after a valid coordinate exists.
- Rotate or plot mission images/maps: hand map construction and image
rendering to
maps-and-visualization.
differential_rotate() is map-oriented and should normally be used there.
Read troubleshooting when an import, optional
reader, unit, time-index, metadata, or headless plotting check fails.
Core operating contract
A TimeSeries has three coupled pieces:
- A pandas
DataFrame whose index is the observation time and whose columns
are channels.
meta, represented by TimeSeriesMetaData, containing time ranges,
applicable column names, and metadata dictionaries.
units, a mapping from every column name to an Astropy unit.
Keep the index sorted, time-like, and the same length as each value column.
Keep units synchronized after pandas operations. Validate with
ts.columns, ts.shape, ts.time_range, ts.units, ts.to_dataframe(), and
ts.quantity(column); these are stronger signals than a printed repr.
Choose the constructor correctly
sunpy.timeseries.TimeSeries(*args, allow_errors=False, **kwargs) is a
factory. It dispatches DataFrames, Astropy Tables, accepted data/header
pairs, local paths/globs/directories, URLs/URIs, existing TimeSeries objects,
and registered source classes. Multiple inputs return a list unless
concatenate=True is passed.
sunpy.timeseries.GenericTimeSeries(data, meta=None, units=None, **kwargs) is
the direct generic constructor. Prefer it when the data is already a local
DataFrame and no instrument parser is needed.
- A bare Python
dict or arbitrary list is not a generic data container for
the factory. It can be expanded as a list of accepted inputs, but a dict
or list of numeric values normally reaches dispatch and raises
NoMatchError. Build a DataFrame first, then call GenericTimeSeries(df, meta, units) or TimeSeries(df, meta, units).
- A valid source parser may return a source subclass; a generic local DataFrame
returns
GenericTimeSeries. Do not assume that every TimeSeries result has
a mission-specific source.
See the API table for exact signatures and
return-shape details.
Deterministic local workflow
- Normalize timestamps into a pandas datetime-like index and make a small
DataFrame. Use explicit units such as
u.W / u.m**2, u.ct, or
u.dimensionless_unscaled.
- Construct with
GenericTimeSeries(df, meta, units) (or the factory with the
same three positional groups).
- Inspect
ts.to_dataframe(), ts.to_table() (date column first, units
attached), ts.to_array(), ts.time, ts.time_range, and
ts.meta.to_string(depth=...).
- Select with
extract(column_name) or quantity(column_name). Add a new
channel with add_column(name, Quantity); remove one with
remove_column(name). These return new objects rather than modifying the
original data object.
- Restrict with
truncate(TimeRange(...)), two parseable time strings, or
integer slice arguments. Re-sort with sort_index() if inputs were not
chronological.
- For resampling, use pandas on a copy of
ts.to_dataframe(), for example
df.resample("10min").mean(), then reconstruct with the existing metadata
and units. SunPy does not provide a separate TimeSeries resample() method.
- Combine compatible intervals with
ts.concatenate(other, same_source=True)
or create from multiple accepted inputs with concatenate=True. The
implementation concatenates and sorts; do not assume overlapping timestamps
are deduplicated. Check the resulting index, shape, and units and apply an
explicit duplicate policy to a copied DataFrame when needed.
- Use
plot(axes=ax, columns=[...]) for controlled Matplotlib output. Use
peek() for a quick diagnostic figure, not as the primary plotting API.
Set MPLBACKEND=Agg or create/close figures explicitly in headless jobs.
quicklook() opens a browser and is therefore interactive/optional.
Run the bundled TimeSeries round-trip helper
when a compact executable smoke is useful. It uses synthetic local data and a
temporary ECSV file:
python scripts/timeseries_roundtrip.py --help
MPLBACKEND=Agg python scripts/timeseries_roundtrip.py
Local source-specific loading
For a local mission file, call the factory with a pathlib.Path or path string.
Use an explicit source when metadata cannot uniquely identify the parser:
- GOES XRS:
TimeSeries(path, source="XRS"); the registered
XRSTimeSeries reader supports relevant FITS and GOES netCDF/HDF5 forms.
- SDO EVE:
source="EVE" for the EVE L0CS text form; EVE ESP FITS uses the
registered ESP source (source="ESP").
- Fermi/GBM summary:
source="GBMSummary" and the registered
GBMSummaryTimeSeries parser.
- PROBA2/LYRA:
source="LYRA" and the registered LYRATimeSeries parser.
- CDF: with the CDF optional dependency available,
TimeSeries(path) can
read Space Physics Guidelines-style CDF data. A file may produce multiple
generic series (for example, separate data groups); inspect whether the
result is a list before indexing or concatenating.
For all loaders, validate the resulting class, columns, time span, units, and
finite/missing-value policy before scientific use. Source examples that fetch
sample data or query GOES/HEK remain documentation-only. Acquire files through
the data-access route, then pass the local result here.
Solar constants, models, and rotation
Use sunpy.sun.constants for Astropy constants and solar time parameters:
constants.get(key), constants.find(substring), and constants.print_all()
are the stable discovery operations. Common aliases include mass, radius,
luminosity, sfu, sidereal_rotation_rate, first_carrington_rotation, and
mean_synodic_period. Convert constants explicitly with Astropy units rather
than stripping .value prematurely.
Use sunpy.sun.models.interior and .evolution as unit-bearing Astropy
QTable model data, and sunpy.sun.models.differential_rotation(duration, latitude, model="howard", frame_time="sidereal") for a longitude change.
Supported models are howard, snodgrass, allen, and rigid; supported
frame times are sidereal and synodic. The result is an Astropy
Longitude. Use u.day/u.s and u.deg quantities, including arrays when
needed.
For an existing valid solar SkyCoord,
sunpy.physics.differential_rotation.solar_rotate_coordinate(coordinate, observer=...) or time=... returns the rotated coordinate. Supply exactly one
of observer and time; time= assumes an Earth observer and emits a warning.
Do not supply both or neither. Keep frame construction, observer selection, and
coordinate transforms in the coordinates route. For maps, use
differential_rotate(smap, observer=... or time=...) only after reviewing the
map route: it requires image/WCS inputs, may require scikit-image, and is more
expensive than the scalar model calculation.
Run the bundled differential-rotation helper
for a deterministic physics smoke without network or map data:
python scripts/differential_rotation_smoke.py --help
python scripts/differential_rotation_smoke.py --duration-days 2 --latitude 30
Validation and recovery signals
A successful local run has a non-empty DataFrame, a time range matching the
index extrema, one unit entry per column, stable column names after round-trip,
and a returned rotation with angular units. If a loader returns a list, inspect
each element instead of treating the list as a series. If a plot fails, first
validate non-empty data and switch to MPLBACKEND=Agg; if a unit or source is
unknown, preserve the warning and resolve it before analysis. Use the detailed
failure matrix in troubleshooting.