Reproducing Figure 1 of the 2023 SunPy Paper in 2025

solar physics
python
english
Author

Mingyu Jeon

Published

August 6, 2025

import warnings; warnings.simplefilter("ignore")
import logging, sunpy; logging.getLogger('sunpy').setLevel(logging.ERROR)

The SunPy (The SunPy Community et al. 2020) is a community-developed, free, and open source Python package for solar data analysis (Barnes et al. 2023; Mumford et al. 2020; The SunPy Community et al. 2015).

In this post, I will reproduce Figure 1 (see below) of the 2023 SunPy paper (Barnes et al. 2023) using the SunPy ecosystem. This is because one of the most important affiliated packages, pfsspy (Stansby et al. 2020), has been no longer developed since August 2023. The pfsspy package has been forked to sunkit-magex. Also, there are subtle differences in the aiapy package between 2023 and 2025. Therefore, I will basically follow the steps in this code provided by the authors of the 2023 SunPy paper, except that I will use the sunkit-magex package instead of the pfsspy package.

Figure 1 of the 2023 SunPy paper source

Version Information

import sys
print("Python version:", sys.version)

import numpy as np
print("NumPy version:", np.__version__)
import matplotlib
print("Matplotlib version:", matplotlib.__version__)
import astropy
print("Astropy version:", astropy.__version__)
import sunpy
print("SunPy version:", sunpy.__version__)
import aiapy
print("aiapy version:", aiapy.__version__)
import sunpy_soar
print("sunpy-soar version:", sunpy_soar.__version__)
import sunkit_magex
print("sunkit-magex version:", sunkit_magex.__version__)
Python version: 3.12.11 | packaged by conda-forge | (main, Jun  4 2025, 14:29:09) [MSC v.1943 64 bit (AMD64)]
NumPy version: 2.2.6
Matplotlib version: 3.10.5
Astropy version: 7.1.0
SunPy version: 7.0.1
aiapy version: 0.10.1
sunpy-soar version: 1.11.1
sunkit-magex version: 1.1.0

Download HMI Synoptic Magnetogram for CR 2255

The SunPy paper (2023) said that the left panel of Figure1A shows the SDO/HMI synoptic magnetogram for Carrington rotation (CR) 2255 which began on 2022-03-08.

Let’s start by checking that CR 2255 actually began on 2022-03-08.

from sunpy.coordinates.sun import carrington_rotation_time
carrington_rotation_time(2255)
<Time object: scale='utc' format='iso' value=2022-03-07 11:59:56.657>

CR 2255 actually began on 2022-03-07 12:00 UTC, which also can be found in this page.

We can download the HMI polar-filled full-CR synoptic magnetogram hmi.synoptic_mr_polfil_720s (Sun 2018) for CR 2255 from JSOC using sunpy.net.Fido.

from sunpy.net import Fido, attrs as a
# Please replace the following email with your own email address registered with the JSOC.
jsoc_email = ''
q = Fido.search(
    a.Time('2022-03-07T12:00', '2022-03-07T12:00'),
    a.jsoc.Series('hmi.synoptic_mr_polfil_720s'),
    a.jsoc.PrimeKey('CAR_ROT', 2255),
    a.jsoc.Notify(jsoc_email)
)
q
Results from 1 Provider:

1 Results from the JSOCClient:
JSOCResponse length=1
TELESCOPINSTRUMEWAVELNTHCAR_ROT
str7str9float64int64
SDO/HMIHMI_SIDE16173.02255

f = Fido.fetch(q, path='./data', overwrite=True)
print(f)
['data\\hmi.synoptic_mr_polfil_720s.2255.Mr_polfil.fits']

We can load the fits file using sunpy.map.Map.

from sunpy.map import Map
m_hmi = Map(f)
m_hmi.peek()

This is the same HMI synoptic magnetogram as shown in the left panel of Figure 1A in the SunPy paper (2023).

Select Location of AR

We identify the center of the active region (AR) of interest visually from the HMI synoptic magnetogram.

import astropy.units as u
from astropy.coordinates import SkyCoord
m_hmi.coordinate_frame
<HeliographicCarrington Frame (obstime=2022-03-21T03:51:33.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate (obstime=2022-03-21T03:51:33.000, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, m)
    (0., -7.02110576, 1.48994814e+11)>)>
ar_center = SkyCoord(lon=65*u.deg, lat=15*u.deg, frame=m_hmi.coordinate_frame)

We can plot this coordinate on the HMI map.

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(15, 5))
ax = fig.add_subplot(111, projection=m_hmi)
m_hmi.plot(axes=ax)
ax.plot_coord(ar_center, marker='X', color='k', markersize=10);

The default obstime of the carrington map is halfway through the carrington rotation.

m_hmi.coordinate_frame.obstime
<Time object: scale='utc' format='isot' value=2022-03-21T03:51:33.000>
car_start = carrington_rotation_time(2255)
car_end = carrington_rotation_time(2256)
print('CR 2255 start   ', car_start)
print('CR 2255 end     ', car_end)
car_halfway = car_start + (car_end - car_start) / 2
print('CR 2255 halfway ', car_halfway)
CR 2255 start    2022-03-07 11:59:56.657
CR 2255 end      2022-04-03 19:19:29.900
CR 2255 halfway  2022-03-21 03:39:43.279

Since a synoptic map is comprised of observations from many different times, we look up the obstime associated with our selected longitude and use this to correct our original AR coordinate

ar_date = carrington_rotation_time(2255, ar_center.lon)
ar_date
<Time object: scale='utc' format='iso' value=2022-03-29 21:04:15.174>
change_obstime = lambda x,y: SkyCoord(x.replicate(observer=x.observer.replicate(obstime=y), obstime=y))
ar_center_corrected = change_obstime(ar_center, ar_date)
ar_center_corrected
<SkyCoord (HeliographicCarrington: obstime=2022-03-29 21:04:15.174, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate (obstime=2022-03-29 21:04:15.174, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, m)
    (0., -7.02110576, 1.48994814e+11)>): (lon, lat) in deg
    (65., 15.)>

Download EUV data for AR

We will download the EUV data from SDO/AIA, STEREO-A/SECCHI/EUVI, and Solar Orbiter/EUI/FSI.

First, we define the time range of interest.

tr = a.Time(ar_date-2*u.min, ar_date+2*u.min)
tr
<sunpy.net.attrs.Time(2022-03-29 21:02:15.174, 2022-03-29 21:06:15.174)>

We construct a search query for 171 Å EUV data from SDO/AIA and STEREO-A/SECCHI/EUVI. This will search the Virtual Solar Observatory (VSO).

aia_or_euvi = ((a.Instrument('AIA') | a.Instrument('EUVI'))
                & a.Wavelength(171*u.angstrom)
                & a.Sample(5*u.min))

We construct a search query for 174 Å EUV data from Solar Orbiter/EUI/FSI. This will search the Solar Oriter Archive (SOAR) using the sunpy-soar package.

eui = a.Level(2) & a.soar.Product('EUI-FSI174-IMAGE')
q = Fido.search(tr, aia_or_euvi | eui)
q
Results from 3 Providers:

1 Results from the VSOClient:
VSOQueryResponseTable length=1
Start TimeEnd TimeSourceInstrumentWavelengthProviderPhysobsWavetypeExtent WidthExtent LengthExtent TypeSize
AngstromMibyte
TimeTimestr3str3float64[2]str4str9str6str4str4str8float64
2022-03-29 21:02:21.0002022-03-29 21:02:22.000SDOAIA171.0 .. 171.0JSOCintensityNARROW40964096FULLDISK64.64844

1 Results from the VSOClient:
VSOQueryResponseTable length=1
Start TimeEnd TimeSourceInstrumentWavelengthProviderPhysobsWavetypeExtent TypeSize
AngstromMibyte
TimeTimestr8str6float64[2]str3str9str6str8float64
2022-03-29 21:03:00.0002022-03-29 21:03:02.000STEREO_ASECCHI171.0 .. 175.0SSCintensityNARROWFULLDISK-0.00098

1 Results from the SOARClient:
QueryResponseTable length=1
InstrumentData productLevelStart timeEnd timeData item IDFilenameFilesizeSOOP NameDetectorWavelength
Mbyte
str3str16str2str23str23str43str52float64str31str3float64
EUIeui-fsi174-imageL22022-03-29 21:04:45.2982022-03-29 21:04:55.298solo_L2_eui-fsi174-image_20220329T210445298solo_L2_eui-fsi174-image_20220329T210445298_V02.fits5.593L_FULL_HRES_HCAD_Eruption-WatchFSI174.0

Note that Fido returns three results (one for each instrument) and that two out of the three of these are from the VSO and one is from the SOAR.

files = Fido.fetch(q, path='./data', overwrite=True)
print(sorted(files))
['data\\20220329_210300_n5euA.fts', 'data\\aia.lev1.171A_2022_03_29T21_02_21.35Z.image_lev1.fits', 'data\\solo_L2_eui-fsi174-image_20220329T210445298_V02.fits']
m_secchi, m_aia, m_eui = Map(sorted(files))

Using the metadata provided in each file, we can plot the relative locations of the three spacecraft.

fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
# Plot the Sun
ax.plot(0, 0, marker='o', markersize=20, label='Sun', color='yellow')
# Plot the spacecraft locations
for m in [m_aia, m_eui, m_secchi]:
    sat = m.observatory
    coord = m.observer_coordinate
    ax.plot(coord.lon.to('rad'), coord.radius.to(u.AU), 'o', label=sat)
ax.set_theta_zero_location('S')
ax.legend();

Process EUV Data for AR

We prepare the SDO/AIA data from level 1 to level 1.5 using aiapy package by (1) correcting the pointing keywords and (2) also correcting the instrument degradation.

from aiapy.calibrate import update_pointing, correct_degradation
from aiapy.calibrate.util import get_pointing_table, get_correction_table
pointing_table = get_pointing_table('JSOC', time_range=(m_aia.date-12*u.h, m_aia.date+12*u.h))
pointing_table
QTable length=8
ORIGINTELESCOPDATET_STARTT_STOPT_HKVALSVERSIONSC_Y_INRT_BIASSC_Z_INRT_BIASH_CAM1_IMSCALEH_CAM1_X0H_CAM1_Y0H_CAM1_INSTROTH_CAM2_IMSCALEH_CAM2_X0H_CAM2_Y0H_CAM2_INSTROTA_094_IMSCALEA_094_X0A_094_Y0A_094_INSTROTA_131_IMSCALEA_131_X0A_131_Y0A_131_INSTROTA_171_IMSCALEA_171_X0A_171_Y0A_171_INSTROTA_193_IMSCALEA_193_X0A_193_Y0A_193_INSTROTA_211_IMSCALEA_211_X0A_211_Y0A_211_INSTROTA_304_IMSCALEA_304_X0A_304_Y0A_304_INSTROTA_335_IMSCALEA_335_X0A_335_Y0A_335_INSTROTA_1600_IMSCALEA_1600_X0A_1600_Y0A_1600_INSTROTA_1700_IMSCALEA_1700_X0A_1700_Y0A_1700_INSTROTA_4500_IMSCALEA_4500_X0A_4500_Y0A_4500_INSTROTHMI_FSW_AL1_POSITIONHAL1POSHMI_FSW_AL2_POSITIONHAL2POSHMI_AL1_STATUSHAL1STATHMI_AL2_STATUSHAL2STATHMI_ISS_ERRGAINYHIERRGNYHMI_ISS_ERRGAINZHIERRGNZHMI_ISS_ERROFFYHIERROFYHMI_ISS_ERROFFZHIERROFZHMI_ISS_PZTOFFAHIPZTOFAHMI_ISS_PZTOFFBHIPZTOFBHMI_ISS_PZTOFFCHIPZTOFCHMI_ISS_PKT_YCOEF_AHIYCOEFAHMI_ISS_PKT_YCOEF_BHIYCOEFBHMI_ISS_PKT_YCOEF_CHIYCOEFCHMI_ISS_PKT_ZCOEF_AHIZCOEFAHMI_ISS_PKT_ZCOEF_BHIZCOEFBHMI_ISS_PKT_ZCOEF_CHIZCOEFCAIA_IS1_ERRGAINYA1ERRGNYAIA_IS1_ERRGAINZA1ERRGNZAIA_IS1_ERROFFYA1ERROFYAIA_IS1_ERROFFZA1ERROFZAIA_IS1_PZTGAINAA1PZTGNAAIA_IS1_PZTGAINBA1PZTGNBAIA_IS1_PZTGAINCA1PZTGNCAIA_IS1_PZTOFFAA1PZTOFAAIA_IS1_PZTOFFBA1PZTOFBAIA_IS1_PZTOFFCA1PZTOFCAIA_GT1_PKT_YCOEF_AAGT1_YCAAIA_GT1_PKT_YCOEF_BAGT1_YCBAIA_GT1_PKT_YCOEF_CAGT1_YCCAIA_GT1_PKT_ZCOEF_AAGT1_ZCAAIA_GT1_PKT_ZCOEF_BAGT1_ZCBAIA_GT1_PKT_ZCOEF_CAGT1_ZCCAIA_IS2_ERRGAINYA2ERRGNYAIA_IS2_ERRGAINZA2ERRGNZAIA_IS2_ERROFFYA2ERROFYAIA_IS2_ERROFFZA2ERROFZAIA_IS2_PZTGAINAA2PZTGNAAIA_IS2_PZTGAINBA2PZTGNBAIA_IS2_PZTGAINCA2PZTGNCAIA_IS2_PZTOFFAA2PZTOFAAIA_IS2_PZTOFFBA2PZTOFBAIA_IS2_PZTOFFCA2PZTOFCAIA_GT2_PKT_YCOEF_AAGT2_YCAAIA_GT2_PKT_YCOEF_BAGT2_YCBAIA_GT2_PKT_YCOEF_CAGT2_YCCAIA_GT2_PKT_ZCOEF_AAGT2_ZCAAIA_GT2_PKT_ZCOEF_BAGT2_ZCBAIA_GT2_PKT_ZCOEF_CAGT2_ZCCAIA_IS3_ERRGAINYA3ERRGNYAIA_IS3_ERRGAINZA3ERRGNZAIA_IS3_ERROFFYA3ERROFYAIA_IS3_ERROFFZA3ERROFZAIA_IS3_PZTGAINAA3PZTGNAAIA_IS3_PZTGAINBA3PZTGNBAIA_IS3_PZTGAINCA3PZTGNCAIA_IS3_PZTOFFAA3PZTOFAAIA_IS3_PZTOFFBA3PZTOFBAIA_IS3_PZTOFFCA3PZTOFCAIA_GT3_PKT_YCOEF_AAGT3_YCAAIA_GT3_PKT_YCOEF_BAGT3_YCBAIA_GT3_PKT_YCOEF_CAGT3_YCCAIA_GT3_PKT_ZCOEF_AAGT3_ZCAAIA_GT3_PKT_ZCOEF_BAGT3_ZCBAIA_GT3_PKT_ZCOEF_CAGT3_ZCCAIA_IS4_ERRGAINYA4ERRGNYAIA_IS4_ERRGAINZA4ERRGNZAIA_IS4_ERROFFYA4ERROFYAIA_IS4_ERROFFZA4ERROFZAIA_IS4_PZTGAINAA4PZTGNAAIA_IS4_PZTGAINBA4PZTGNBAIA_IS4_PZTGAINCA4PZTGNCAIA_IS4_PZTOFFAA4PZTOFAAIA_IS4_PZTOFFBA4PZTOFBAIA_IS4_PZTOFFCA4PZTOFCAIA_GT4_PKT_YCOEF_AAGT4_YCAAIA_GT4_PKT_YCOEF_BAGT4_YCBAIA_GT4_PKT_YCOEF_CAGT4_YCCAIA_GT4_PKT_ZCOEF_AAGT4_ZCAAIA_GT4_PKT_ZCOEF_BAGT4_ZCBAIA_GT4_PKT_ZCOEF_CAGT4_ZCC
arcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdegarcsec / pixpixpixdeg
str12str3str20TimeTimestr20int64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64
SDO/JSOC-SDPSDO2022-03-30T16:51:58Z2022-03-29T12:00:00.0002022-03-29T18:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.8215332009.129517-0.137610.6006982041.7043462041.861694-0.1387950.5994892054.6105962047.3101810.0193270.6007142040.7598882041.9180910.0577890.6007582036.5909422040.4560550.0564330.6001652067.3798832006.143433-0.1316390.6007372040.5375982040.539795-0.1424430.6093732051.8544922046.5614010.0197920.6128982053.2221682044.9229740.0205250.5999452051.9367682048.1933590.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
SDO/JSOC-SDPSDO2022-03-30T19:47:27Z2022-03-29T15:00:00.0002022-03-29T21:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.9704592009.175171-0.137610.6006982041.6452642041.656616-0.1387950.5994892054.4926762047.2633060.0193270.6007142040.7543952041.9854740.0577890.6007582036.5816652040.4633790.0564330.6001652068.613772006.88269-0.1316390.6007372040.4611822040.399536-0.1424430.6093732051.7543952046.541260.0197920.6128982053.1225592044.9132080.0205250.5999452051.8100592048.1201170.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
SDO/JSOC-SDPSDO2022-03-30T22:07:31Z2022-03-29T18:00:00.0002022-03-30T00:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.9128422009.273682-0.137610.6006982041.5379642041.681763-0.1387950.5994892054.4951172047.2779540.0193270.6007142040.4499512042.011230.0577890.6007582036.2672122040.5332030.0564330.6001652067.9755862006.063599-0.1316390.6007372040.3422852040.449707-0.1424430.6093732051.7214362046.5028080.0197920.6128982053.0676272044.8846440.0205250.5999452054.7106932067.6726070.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
SDO/JSOC-SDPSDO2022-03-31T01:40:36Z2022-03-29T21:00:00.0002022-03-30T03:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.8085942009.097412-0.137610.6006982041.291872041.739624-0.1387950.5994892054.4836432047.2253420.0193270.6007142040.0173342041.9810790.0577890.6007582035.7963872040.4809570.0564330.6001652067.68752006.059692-0.1316390.6007372040.3735352040.357666-0.1424430.6093732051.7150882046.3878170.0197920.6128982053.0510252044.7968750.0205250.5999452051.0400392047.5899660.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
SDO/JSOC-SDPSDO2022-03-31T04:51:55Z2022-03-30T00:00:00.0002022-03-30T06:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.5275882009.48877-0.137610.6006982041.1519782041.793945-0.1387950.5994892054.1889652047.5488280.0193270.6007142039.2766112042.2567140.0577890.6007582033.2718512040.7475590.0564330.6001652068.6059572007.465942-0.1316390.6007372039.6025392040.08252-0.1424430.6093732051.7800292046.5600590.0197920.6128982052.8330082044.9959720.0205250.5999452051.760012048.250.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
SDO/JSOC-SDPSDO2022-03-31T07:53:59Z2022-03-30T03:00:00.0002022-03-30T09:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.4948732009.120605-0.137610.6006982041.2006842041.945679-0.1387950.5994892054.4343262047.5662840.0193270.6007142039.736452042.1677250.0577890.6007582035.6168212040.6866460.0564330.6001652067.877932006.530029-0.1316390.6007372040.0360112040.598633-0.1424430.6093732051.6467292046.7902830.0197920.6128982053.0126952045.1606450.0205250.5999452050.9199222047.8233640.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
SDO/JSOC-SDPSDO2022-03-31T10:52:02Z2022-03-30T06:00:00.0002022-03-30T12:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.3942872009.004272-0.137610.6006982041.2562262041.89917-0.1387950.5994892054.354982047.5407710.0193270.6007142039.7774662041.9514160.0577890.6007582035.645022040.4791260.0564330.6001652066.5490722005.698975-0.1316390.6007372040.1077882040.618042-0.1424430.6093732051.7014162046.8031010.0197920.6128982053.0854492045.1665040.0205250.5999452051.7033692048.4199220.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
SDO/JSOC-SDPSDO2022-03-31T13:41:38Z2022-03-30T09:00:00.0002022-03-30T15:00:00.0002022-03-27T00:00:00Z1-4.9839.3230.504892033.7900392051.580078180.0135040.504862039.6300052048.47998179.9297940.6001092069.4577642008.996338-0.137610.6006982041.4189452041.971558-0.1387950.5994892054.5051272047.4327390.0193270.6007142039.9602052041.8460690.0577890.6007582035.7480472040.3553470.0564330.6001652067.6516112006.154053-0.1316390.6007372040.2333982040.649048-0.1424430.6093732051.8615722046.7789310.0197920.6128982053.2275392045.1586910.0205250.5999452051.8432622048.4199220.02063876.076.044.044.081.081.081.081.0-43.0-43.0-44.0-44.0-37.0-37.066.066.0-16.0-16.0-16.0-16.0-13.0-13.0-20920.0-20920.0-10460.0-10460.0-10460.0-10460.00.00.0-15098.0-15098.0-15098.0-15098.0-85.0-85.0-85.0-85.0-17.0-17.053.053.0-20.0-20.0-19.0-19.0-19.0-19.0-15.0-15.0-17.0-17.032.032.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-85.0-85.0-85.0-85.01.01.038.038.0-18.0-18.0-30.0-30.0-30.0-30.011.011.05.05.0-15.0-15.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-79.0-79.0-78.0-78.06.06.013.013.0-15.0-15.0-22.0-22.0-22.0-22.0-37.0-37.09.09.029.029.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0-80.0-80.0-80.0-80.02.02.0-6.0-6.0-15.0-15.0-22.0-22.0-22.0-22.025.025.01.01.0-26.0-26.02048.02048.013900.013900.00.00.02048.02048.013900.013900.00.00.0
correction_table = get_correction_table('JSOC')
correction_table
QTable length=665
DATEVER_NUMWAVE_STRWAVELNTHT_STARTT_STOPEFFA_P1EFFA_P2EFFA_P3EFF_AREAEFF_WVLN
Angstromcm2Angstrom
str20int64str9float64TimeTimefloat64float64float64float64float64
2020-11-02T11:14:52Z10131_THICK131.02010-03-24T00:00:00.0002011-02-24T19:00:00.000-0.0003830.00.00.101769131.199997
2017-12-10T05:05:04Z8131_THICK131.02010-03-24T00:00:00.0002011-02-24T19:00:00.000-0.0003240.00.00.092281131.199997
2020-07-06T21:54:29Z9131_THICK131.02010-03-24T00:00:00.0002011-02-24T19:00:00.000-0.0003770.00.00.101531131.199997
2011-09-29T00:00:00Z1131_THICK131.02010-03-24T00:00:00.0002030-04-30T23:59:57.0000.00.00.00.097468131.199997
2012-01-05T00:00:00Z2131_THICK131.02010-03-24T00:00:00.0002011-02-24T19:00:00.0000.00.00.00.075027131.199997
2010-11-23T00:00:00Z1131_THICK131.02010-03-24T00:00:00.0002011-03-24T00:00:00.0000.00.00.00.097131.199997
2012-09-26T00:00:00Z3131_THICK131.02010-03-24T00:00:00.0002011-02-24T19:00:00.0000.00.00.00.075027131.199997
2011-04-29T00:00:00Z1131_THICK131.02010-03-24T00:00:00.0002012-01-01T00:00:00.0000.00.00.00.097131.199997
2020-11-02T11:14:52Z10131_THIN131.02010-03-24T00:00:00.0002011-02-24T19:00:00.000-0.0003830.00.01.223503131.199997
.................................
2020-11-02T11:14:52Z10335_THICK335.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.006929335.399994
2020-11-02T11:14:52Z10335_THIN335.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.009789335.399994
2020-07-06T21:54:47Z9335_THIN335.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.009279335.399994
2020-11-19T19:00:00Z1094_THICK94.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.19323793.900002
2020-11-02T11:14:52Z1094_THICK94.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.2026193.900002
2020-07-06T21:54:28Z994_THICK94.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.21349393.900002
2020-07-06T21:54:28Z994_THIN94.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.2974493.900002
2020-11-19T19:00:00Z1094_THIN94.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.2692293.900002
2020-11-02T11:14:52Z1094_THIN94.02020-06-01T12:00:00.0002030-05-01T00:00:00.0000.00.00.00.28227893.900002
m_aia.peek(vmin=0, vmax=8000)

m_aia = correct_degradation(update_pointing(m_aia, pointing_table=pointing_table), correction_table=correction_table)
m_aia.peek(vmin=0, vmax=8000)

The SDO/AIA and STEREO-A/SECCHI/EUVI data are not been normalized for exposure time, whereas the Solar Orbiter/EUI/FSI data (level 2) are normalized for exposure time.

m_secchi.peek()

m_eui.peek()

We normalize the SDO/AIA and STEREO-A/SECCHI/EUVI data for exposure time.

m_aia = m_aia / m_aia.exposure_time
m_secchi = m_secchi / m_secchi.exposure_time
m_aia.peek()

m_secchi.peek()

We can plot the position of the corrected center of the AR on the EUV maps.

fig = plt.figure(figsize=(15, 5))
for i, m in enumerate([m_aia, m_eui, m_secchi]):
    ax = fig.add_subplot(1, 3, i+1, projection=m)
    m.plot(axes=ax, clip_interval=(1, 99.99)*u.percent)
    ax.plot_coord(ar_center_corrected, marker='o', color='C3')

We can plot the region of interest on the EUV maps.

ar_width = 700*u.arcsec
ar_height = 700*u.arcsec
fig = plt.figure(figsize=(15, 5))
for i, m in enumerate([m_aia, m_eui, m_secchi]):
    ax = fig.add_subplot(1, 3, i+1, projection=m)
    m.plot(axes=ax, clip_interval=(1, 99.99)*u.percent)
    ar_center_corrected_transformed = ar_center_corrected.transform_to(m.coordinate_frame)
    blc = SkyCoord(Tx=ar_center_corrected_transformed.Tx-ar_width/2,
                   Ty=ar_center_corrected_transformed.Ty-ar_height/2,
                   frame=ar_center_corrected_transformed)
    m.draw_quadrangle(blc, width=ar_width, height=ar_height, edgecolor='C3', lw=1)

We can rotate the maps such that the y-axis of the images is aligned with the solar north using sunpy.map.Map.rotate.

fig = plt.figure(figsize=(15, 5))
for i, m in enumerate([m_aia, m_eui, m_secchi]):
    ax = fig.add_subplot(1, 3, i+1, projection=m)
    m_rot = m.rotate(missing=0.0)
    m_rot.plot(axes=ax, clip_interval=(1, 99.99)*u.percent)
    ar_center_corrected_transformed = ar_center_corrected.transform_to(m_rot.coordinate_frame)
    blc = SkyCoord(Tx=ar_center_corrected_transformed.Tx-ar_width/2,
                   Ty=ar_center_corrected_transformed.Ty-ar_height/2,
                   frame=ar_center_corrected_transformed)
    m_rot.draw_quadrangle(blc, width=ar_width, height=ar_height, edgecolor='C3', lw=1)

We can now crop each full disk image to the appropriate region.

m_cutouts = []
for m in [m_aia, m_eui, m_secchi]:
    ar_center_corrected_transformed = ar_center_corrected.transform_to(m.coordinate_frame)
    blc = SkyCoord(Tx=ar_center_corrected_transformed.Tx-ar_width/2,
                   Ty=ar_center_corrected_transformed.Ty-ar_height/2,
                   frame=ar_center_corrected_transformed)
    # Each map is rotated prior to submapping such that the selection is aligned with the coordinate grid
    m_rot = m.rotate(missing=0.0)
    m_cutout = m_rot.submap(blc, width=ar_width, height=ar_height)
    m_cutouts.append(m_cutout)
fig = plt.figure(figsize=(15, 5))
for i, m in enumerate(m_cutouts):
    ax = fig.add_subplot(1, 3, i+1, projection=m)
    m.plot(axes=ax, clip_interval=(1, 99.99)*u.percent)

Calculate PFSS from HMI Synoptic Magnetogram

We can calculate a potential field source surface (PFSS) solution from the HMI synoptic magnetogram using sunkit-magex package.

We first resample the HMI synoptic map to speed up the calculation of our field extrapolation.

m_hmi.data.shape
(1440, 3600)
m_hmi_resample = m_hmi.resample((1080, 540)*u.pix)
m_hmi_resample.data.shape
(540, 1080)
from sunkit_magex import pfss
nr = 70    # number of cells in the radial direction
rss = 2.5  # radius of the source surface in R_sun
pfss_input = pfss.Input(m_hmi_resample, nr, rss)
pfss_output = pfss.pfss(pfss_input)

Let’s plot the radial component of the magnetic field at the source surface (2.5 Rs) and the polarity inversion lines (PILs).

fig = plt.figure()
ax = fig.add_subplot(111, projection=pfss_output.source_surface_br)
im = pfss_output.source_surface_br.plot(axes=ax)
ax.plot_coord(pfss_output.source_surface_pils[0], color='k')
fig.colorbar(im, ax=ax);

Trace Magnetic Field Lines

Let’s trace some field lines through the extrapolated field.

To do this, we need to choose a set of seed points from which to trace our field lines. We want to select only the seed points that are within a certain area around the active region. We need to make the following transformations because we want to express our boundaries in terms of the active region boundary as identified by our SDO/AIA cutout.

change_obstime_frame = lambda x,y: x.replicate_without_data(observer=x.observer.replicate(obstime=y), obstime=y)
new_frame = change_obstime_frame(m_hmi.coordinate_frame, m_cutouts[0].date)
new_frame
<HeliographicCarrington Frame (obstime=2022-03-29T21:02:21.350, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate (obstime=2022-03-29T21:02:21.350, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, m)
    (0., -7.02110576, 1.48994814e+11)>)>
blc_ar_synop = change_obstime(m_cutouts[0].bottom_left_coord.transform_to(new_frame),
                              m_hmi.date)
trc_ar_synop = change_obstime(m_cutouts[0].top_right_coord.transform_to(new_frame),
                              m_hmi.date)

We mask all those points that are above -10 G and fall outside of the bounding box defined above.

masked_pix_y, masked_pix_x = np.where(m_hmi_resample.data < -1e1)
seeds = m_hmi_resample.pixel_to_world(masked_pix_x*u.pix, masked_pix_y*u.pix).make_3d()
in_lon = np.logical_and(seeds.lon > blc_ar_synop.lon, seeds.lon < trc_ar_synop.lon)
in_lat = np.logical_and(seeds.lat > blc_ar_synop.lat, seeds.lat < trc_ar_synop.lat)
seeds = seeds[np.where(np.logical_and(in_lon, in_lat))]
seeds
<HeliographicCarrington Coordinate (obstime=2022-03-21T03:51:33.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate (obstime=2022-03-21T03:51:33.000, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, m)
    (0., -7.02110576, 1.48994814e+11)>): (lon, lat, radius) in (deg, deg, km)
    [(48.88333333, -5.63254256, 695700.),
     (69.55      , -5.63254256, 695700.),
     (74.88333333, -5.63254256, 695700.), ...,
     (62.55      , 41.38466759, 695700.),
     (63.88333333, 41.38466759, 695700.),
     (71.55      , 41.38466759, 695700.)]>

We can trace field lines from seeds specified above.

ds = 0.01
max_steps = int(np.ceil(10 * nr / ds))
tracer = pfss.tracing.PerformanceTracer(step_size=ds, max_steps=max_steps)
fieldlines = tracer.trace(SkyCoord(seeds), pfss_output)

We also want to adjust obstime of all coordinates to coincide with AR at disk center. By default, each field line will have the obstime of the map that the field extrapolation was computed from. Additionally, we will choose only the close field lines.

fieldlines.closed_field_lines[0].coords.observer
<HeliographicStonyhurst Coordinate (obstime=2022-03-21T03:51:33.000, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, m)
    (0., -7.02110576, 1.48994814e+11)>
fline_coords = [change_obstime(f.coords, m_aia.date) for f in fieldlines.closed_field_lines if f.coords.shape[0]>500]
len(fline_coords)
1762
fline_coords[0].observer
<HeliographicStonyhurst Coordinate (obstime=2022-03-29T21:02:21.350, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, m)
    (0., -7.02110576, 1.48994814e+11)>

Finally, let’s plot these field lines on top of our EUV images.

fig = plt.figure(figsize=(18, 5))
for i, m in enumerate(m_cutouts):
    ax = fig.add_subplot(1, 3, i+1, projection=m)
    m.plot(axes=ax, title=f'{m.observatory} {m.detector} {m.wavelength.to_string(format="latex")}')
    bounds = ax.axis()
    for c in fline_coords[::5]:
        ax.plot_coord(c, lw=1, color='C2', alpha=0.75)
    ax.axis(bounds)

Figure 1 from the SunPy Paper (2023)

We can plot Figure 1 from the SunPy paper (2023).

from matplotlib.patches import ConnectionPatch
from matplotlib.gridspec import GridSpec
def add_connectors(ax1, ax2, p1, p2, color='k', lw=1):
    con1 = ConnectionPatch(
        (0, 1), ax1.wcs.world_to_pixel(p1), 'axes fraction', 'data', axesA=ax2, axesB=ax1,
        arrowstyle='-', color=color, lw=lw
    )
    con2 = ConnectionPatch(
        (1, 1), ax1.wcs.world_to_pixel(p2), 'axes fraction', 'data', axesA=ax2, axesB=ax1,
        arrowstyle='-', color=color, lw=lw
    )
    ax2.add_artist(con1)
    ax2.add_artist(con2)

h_w_ratio = 21 / 18
width = 12
frame_color = 'C3'
fig = plt.figure(figsize=(width, width*h_w_ratio))
gs = GridSpec(3, 3, figure=fig)
# Plot HMI synoptic map
ax = fig.add_subplot(gs[0, :2], projection=m_hmi)
m_hmi.plot(axes=ax, title='HMI Synoptic Magnetogram (CR 2255)')
m_hmi.draw_quadrangle(blc_ar_synop, top_right=trc_ar_synop, color=frame_color)
ax.coords[0].grid(color='k')
ax.coords[1].grid(color='k')
# Plot spacecraft locations
ax = fig.add_subplot(gs[0, 2], projection='polar')
ax.plot(0, 0, marker='o', markersize=15, label='Sun', color='yellow')
for m in m_cutouts:
    sat = m.observatory
    coord = m.observer_coordinate
    ax.plot(coord.lon.to('rad'), coord.radius.to(u.AU), 'o', label=sat)
    ax.text(coord.lon.to_value('rad')*1.15, coord.radius.to_value(u.AU)*0.95, sat)
ax.set_theta_zero_location('S')
ax.set_rlabel_position(225)
ax.set_rlim(0, 1.1)
# Plot full-disk EUV images
full_disk_axes = []
for i, m in enumerate([m_aia, m_eui, m_secchi]):
    ax = fig.add_subplot(gs[1, i], projection=m)
    m.plot(axes=ax, clip_interval=(1, 99.99)*u.percent, title=f'{m.observatory} {m.detector} {m.wavelength.to_string(format="latex")}')
    m.draw_quadrangle(m_cutouts[i].bottom_left_coord, top_right=m_cutouts[i].top_right_coord, color=frame_color, lw=1)
    if i:
        ax.coords[1].set_axislabel(' ')
    ax.coords[0].set_axislabel(' ')
    full_disk_axes.append(ax)
    ax.coords[1].set_ticklabel(rotation=90)
# Plot EUV cutouts with field lines
for i, m in enumerate(m_cutouts):
    ax = fig.add_subplot(gs[2, i], projection=m)
    m.plot(
        axes=ax,
        title=False,
        clip_interval=(1, 99.99)*u.percent,
    )
    bounds = ax.axis()
    for c in fline_coords[::8]:
        ax.plot_coord(c, lw=1, color='C2', alpha=0.75)
    ax.axis(bounds)
    if i:
        ax.coords[1].set_axislabel(' ')
    bottom_right = SkyCoord(Tx=m_cutouts[i].top_right_coord.Tx, Ty=m_cutouts[i].bottom_left_coord.Ty, frame=m_cutouts[i].coordinate_frame)
    add_connectors(full_disk_axes[i], ax, m_cutouts[i].bottom_left_coord, bottom_right, color=frame_color, lw=1)
    ax.grid(alpha=0)
    ax.coords[0].set_ticks(direction='in', color=frame_color)
    ax.coords[1].set_ticks(direction='in', color=frame_color)
    ax.coords[0].frame.set_color(frame_color)
    ax.coords[0].frame.set_linewidth(1)
    ax.coords[1].frame.set_color(frame_color)
    ax.coords[1].frame.set_linewidth(1)
    ax.coords[1].set_ticklabel(rotation=90)

plt.subplots_adjust(hspace=0.0)
plt.show()

References

Barnes, W. T., Christe, S., Freij, N., et al. 2023, Frontiers in Astronomy and Space Sciences, 10, 1076726, https://arxiv.org/abs/2304.09794
Mumford, S., Freij, N., Christe, S., et al. 2020, The Journal of Open Source Software, 5, 1832
Stansby, D., Yeates, A., & Badman, S. 2020, The Journal of Open Source Software, 5, 2732
Sun, X. 2018, https://arxiv.org/abs/1801.04265
The SunPy Community, Barnes, W. T., Bobra, M. G., et al. 2020, The Astrophysical Journal, 890 (American Astronomical Society), 68, https://iopscience.iop.org/article/10.3847/1538-4357/ab4f7a
The SunPy Community, Mumford, S. J., Christe, S., et al. 2015, Computational Science and Discovery, 8, 014009, https://arxiv.org/abs/1505.02563