AIA Response Functions: Comparing SunPy and SSW Results


In [5]:
import os
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import astropy.units as u
import ChiantiPy.tools.data as ch_data
import hissw

from sunpy.instr.aia.response import Response
from sunpy.instr.aia.response_utils import EmissTableInterface,aia_instr_properties_to_table,make_emiss_table

%matplotlib inline

SSW Response Functions

Calculate the wavelength and temperature response functions using SSW.


In [6]:
ssw_script="""
; wavelength response
wresp = aia_get_response(/area,/dn,/timedepend_date,/evenorm)
wavelength = wresp.wave
wresponse_94 = wresp.a94.ea
wresponse_131 = wresp.a131.ea
wresponse_171 = wresp.a171.ea
wresponse_193 = wresp.a193.ea
wresponse_211 = wresp.a211.ea
wresponse_304 = wresp.a304.ea
wresponse_335 = wresp.a335.ea

; temperature response
tresp = aia_get_response(/temp,/dn,/timedepend_date,/evenorm)
temperature = 10.^tresp.logte
tresponse_94 = tresp.a94.tresp
tresponse_131 = tresp.a131.tresp
tresponse_171 = tresp.a171.tresp
tresponse_193 = tresp.a193.tresp
tresponse_211 = tresp.a211.tresp
tresponse_304 = tresp.a304.tresp
tresponse_335 = tresp.a335.tresp
"""

In [7]:
save_vars = ['temperature',
             'wavelength',
             'tresponse_94', 
             'tresponse_131',
             'tresponse_171',
             'tresponse_193',
             'tresponse_211',
             'tresponse_304',
             'tresponse_335',
             'wresponse_94', 
             'wresponse_131',
             'wresponse_171',
             'wresponse_193',
             'wresponse_211',
             'wresponse_304',
             'wresponse_335']

In [8]:
ssw_runner = hissw.ScriptMaker(ssw_pkg_list=['sdo/aia'],ssw_path_list=['aia'])

In [9]:
ssw_response_functions = ssw_runner.run([(ssw_script,{})],save_vars=save_vars,cleanup=True,verbose=True)


SSW  setup will include: <gen sdo/aia>

Type <sswidl> to start SSW IDL
--------------------------------------------------------
Running SSW, Last Updated: Wed Nov 2 22:22:51 GMT 2005
 
PROBLEMS? - e-mail TO: freeland@penumbra.nascom.nasa.gov
--------------------------------------------------------
IDL Version 8.5 (linux x86_64 m64). (c) 2015, Exelis Visual Information Solutions, Inc., a subsidiary of Harris Corporation.
Installation number: 401801-1.
Licensed for use by: Rice University

Executing SSW IDL_STARTUP for: GEN
Invalid MIT-MAGIC-COOKIE-1 key% DEVICE: Unable to connect to X Windows display: :0.0
Executing SSW IDL_STARTUP for: SITE
% Compiled module: SSW_PATH.
% Compiled module: SSW_INSTRUMENTS.
% Compiled module: WC_WHERE.
% Compiled module: PATHFIX.
% Compiled module: UNIQO.
% Compiled module: STRJUSTIFY.
% Compiled module: UNIQ.
% Compiled module: DEFAULT.
% Compiled module: PRSTR.
Including Paths:
 ------------------------------
| $SSW/sdo/gen/idl/utilities   |
| $SSW/sdo/gen/idl/attitude    |
| $SSW/sdo/aia/idl/pubrel      |
| $SSW/sdo/aia/idl/response    |
| $SSW/sdo/aia/idl/psf/PRO     |
| $SSW/sdo/aia/idl/calibration |
| $SSW/sdo/aia/idl/util        |
 ------------------------------
% SSW_PATH: Number of paths changed from 87 to 94
% Compiled module: AIA_GET_RESPONSE.
% Compiled module: FILE2TIME.
% Compiled module: EXTRACT_FID.
% Compiled module: BREAK_FILE.
% Compiled module: EXTRACT_FIDS.
% Compiled module: DELVARX.
% Compiled module: DELVARX2.
% Compiled module: DESTROY.
% Compiled module: STRMIDS.
% Compiled module: STRSPECIAL.
% Compiled module: STRLASTCHAR.
% Compiled module: ANYTIM.
% Compiled module: IS_STRUCT.
% Compiled module: CHECKVAR.
% Compiled module: STR_LASTPOS.
% Compiled module: STR2UTC.
% Compiled module: VALID_NUM.
% Compiled module: BOOST_ARRAY.
% Compiled module: UTC2INT.
% Compiled module: TAG_EXIST.
% Compiled module: DATE2MJD.
% Compiled module: CHECK_INT_TIME.
% Compiled module: GET_LEAP_SEC.
% Compiled module: UTIME2STR.
% Compiled module: GETUTBASE.
% Compiled module: GETUT.
% Compiled module: INT2EX.
% Compiled module: DAYCNV.
% Compiled module: EX2INT.
% Compiled module: JDCNV.
% Compiled module: INT2UTC.
% Compiled module: MJD2DATE.
% Compiled module: UTC2STR.
% Compiled module: ANYTIM2TAI.
% Compiled module: ANYTIM2UTC.
% Compiled module: UTC2TAI.
% Compiled module: AIA_BP_READ_RESPONSE_TABLE.
% Compiled module: RD_TFILE.
% Compiled module: RELTIME.
% Compiled module: UT_TIME.
% Compiled module: UT_DIFF.
% Compiled module: SYSTIM.
% Compiled module: INT2SEC.
% Compiled module: ANYTIM2INTS.
% Compiled module: TIMSTR2EX.
% Compiled module: CHECK_TIME.
% Compiled module: FMT_TIM.
% Compiled module: GT_DAY.
% Compiled module: GT_TIME.
% Compiled module: ATIME.
% Compiled module: FCHECK.
% Compiled module: F_ATIME.
% Compiled module: TIMEGRID.
% Compiled module: CONCAT_STRUCT.
% Compiled module: INT2SECARR.
% Compiled module: RESTGEN.
% Compiled module: AIA_BP_BLEND_CHANNELS.
% Compiled module: AIA_BP_PARSE_EFFAREA.
% Compiled module: AIA_BP_UTC2DATE_STRING.
% Compiled module: AIA_BP_CORRECTIONS.
% Compiled module: INTERPOL.
% Compiled module: STR_SUBSET.
% Compiled module: IS_NUMBER.
% Compiled module: BOX_MESSAGE.
 ----------------------------------------------------------
| Generating temperature response function from            |
| /usr/local/ssw/sdo/aia/response/aia_V6_all_fullinst.genx |
| /usr/local/ssw/sdo/aia/response/aia_V6_fullemiss.genx    |
 ----------------------------------------------------------
% Compiled module: AIA_BP_AREA2TRESP.
% Compiled module: AIA_BP_DATE_STRING.
% Compiled module: BIN_DATE.
% Compiled module: AIA_BP_MAKE_TRESP.
% Compiled module: LIMITS.
% Compiled module: AIA_BP_PARSE_TRESP.

Detailed Channel Information

Note first that we can easily read detailed information about each of the AIA channels. Here, I've used instrument files from local SSW installation.


In [10]:
info_table = aia_instr_properties_to_table([94,131,171,193,211,335],
                        [os.path.join('/usr/local/ssw/','sdo/aia/response/aia_V6_all_fullinst.genx')])

In [11]:
info_table


Out[11]:
<QTable length=6>
channelwavelength [8751]minimum_wavelengthwavelength_intervalnumber_wavelength_intervalseffective_area [8751]geometric_area_ccdplate_scaleelectron_per_dnelectron_per_evfocal_plane_filter_efficiency [8751]entrance_filter_efficiency [8751]primary_mirror_reflectance [8751]secondary_mirror_reflectance [8751]quantum_efficiency_ccd [8751]ccd_contamination [8751]
AngstromAngstromAngstromAngstromcm2cm2sr / pixelectron / ctelectron / eV
float64float32float64float64float64float32float64float64float64float64float32float32float32float32float32float32
94.025.0 .. 900.025.00.100000001490116128751.05.274892935641651e-12 .. 2.452600161807414e-1483.08.461580394691914e-1218.2999992370605470.27397298812866210.0517922006547451 .. 1.0000100019169622e-060.0517922006547451 .. 1.0000100019169622e-065.367856374505209e-06 .. 0.085245899856090555.367856374505209e-06 .. 0.085245899856090550.8513820767402649 .. 0.092677913606166840.9657851457595825 .. 0.43874961137771606
131.025.0 .. 900.025.00.100000001490116128751.01.898906171193815e-10 .. 1.9013550499671905e-1383.08.461580394691914e-1217.6000003814697270.27397298812866210.0517922006547451 .. 1.0000100019169622e-060.0517922006547451 .. 1.0000100019169622e-063.220666985725984e-05 .. 0.237351357936859133.220666985725984e-05 .. 0.237351357936859130.8513820767402649 .. 0.092677913606166840.9657851457595825 .. 0.43874961137771606
171.025.0 .. 900.025.00.100000001490116128751.04.4623433836932236e-08 .. 2.7117869061399347e-1083.08.461580394691914e-1217.7000007629394530.27397298812866210.5954740047454834 .. 3.058119909837842e-050.5954740047454834 .. 3.058119909837842e-054.294149039196782e-05 .. 0.29311478137969974.294149039196782e-05 .. 0.29311478137969970.8513820767402649 .. 0.092677913606166840.9657851457595825 .. 0.43874961137771606
193.025.0 .. 900.025.00.100000001490116128751.00.0 .. 3.265265002827533e-1083.08.461580394691914e-1218.2999992370605470.27397298812866210.5954740047454834 .. 3.058119909837842e-050.5954740047454834 .. 3.058119909837842e-050.0 .. 0.32163932919502260.0 .. 0.32163932919502260.8513820767402649 .. 0.092677913606166840.9657851457595825 .. 0.43874961137771606
211.025.0 .. 900.025.00.100000001490116128751.02.789005471015571e-09 .. 2.4803481490920376e-1083.08.461580394691914e-1218.2999992370605470.27397298812866210.5954740047454834 .. 3.058119909837842e-050.5954740047454834 .. 3.058119909837842e-051.0735451724031009e-05 .. 0.280327856540679931.0735451724031009e-05 .. 0.280327856540679930.8513820767402649 .. 0.092677913606166840.9657851457595825 .. 0.43874961137771606
335.025.0 .. 900.025.00.100000001490116128751.00.0 .. 1.1316664272342791e-1083.08.461580394691914e-1217.6000003814697270.27397298812866210.5954740047454834 .. 3.058119909837842e-050.5954740047454834 .. 3.058119909837842e-050.0 .. 0.189351677894592290.0 .. 0.189351677894592290.8513820767402649 .. 0.092677913606166840.9657851457595825 .. 0.43874961137771606

Wavelength Response Functions

Next, we'll actually instantiate the response object and calculate the wavelength response functions. We'll go ahead and calculate the response for all 7 EUV channels.


In [12]:
aia_response = Response()

When the Response object is instantiated, if no instrument files are supplied, it checks the the default SunPy downloads directory for the EUV and FUV instrument files. If they are not there, they are downloaded from the Goddard SSW mirror. This way, a local installation of SSW is not needed. However, to calculate the temperature response functions, you'll need CHIANTI and ChiantiPy installed.


In [13]:
wavelength_response = aia_response.calculate_wavelength_response()

In [14]:
fig,axes = plt.subplots(3,3,figsize=(12,12))
for channel,ax in zip(sorted(wavelength_response.keys()),axes.flatten()):
    # sunpy
    ax.plot(wavelength_response[channel].wavelength,wavelength_response[channel].response,
            linestyle=':',lw=3,color=sns.color_palette('deep')[0],label='SunPy')
    # SSW
    ax.plot(ssw_response_functions['wavelength'],ssw_response_functions['wresponse_{}'.format(channel)],
            linestyle='-',color=sns.color_palette('deep')[2],label='SSW')
    ax.set_xlim([channel-20,channel+20])
    ax.set_xlabel(r'$\lambda$ [{:latex}]'.format(wavelength_response[channel].wavelength.unit))
    ax.set_ylabel(r'$R_{{{}}}$ [{:latex}]'.format(channel,wavelength_response[channel].response.unit))
axes[0,0].legend(loc='best')
plt.tight_layout()


Note that this shows the SSW AIA wavelength response functions with the time-dependent corrections so I guess we need to account for this somehow...

Temperature Response Functions

Now, calculate the temperature response functions. The first time you do this, it will build an emissivity table which may take around 30 minutes, but this only needs to be done once. Alternatively, you can also build your own emissivity table using the built-in function. This is necessary if, for example, you want to change the abundances or ionization equilibrium file, or calculate over a different temperature/density range.


In [15]:
temperature_response = aia_response.calculate_temperature_response()

In [16]:
fig,axes = plt.subplots(3,3,figsize=(12,12),sharex=True,sharey=True)
for channel,ax in zip(sorted(wavelength_response.keys()),axes.flatten()):
    # sunpy
    ax.plot(temperature_response[channel].temperature,temperature_response[channel].response,
            linestyle=':',lw=3,color=sns.color_palette('deep')[0],label='SunPy')
    # SSW
    ax.plot(ssw_response_functions['temperature'],ssw_response_functions['tresponse_{}'.format(channel)],
            linestyle='-',color=sns.color_palette('deep')[2],label='SSW')
    ax.set_xlabel(r'$T$ [{:latex}]'.format(temperature_response[channel].temperature.unit))
    ax.set_ylabel(r'$K_{{{}}}$ [{:latex}]'.format(channel,temperature_response[channel].response.unit))
    ax.set_xscale('log')
    ax.set_yscale('log')
    ax.set_ylim([1e-30,2e-24])
    ax.set_xlim([1e5,1e8])
axes[0,0].legend(loc='best')
plt.tight_layout()


Note that there is an issue with the 304 angstrom channel (even when the time-dependent correction to the wavelength response is not included). Perhaps there is some correction made in the 304 angstrom case, or ions that contribute in this wavlength range, that I've not accounted for here.

Oddly, the lack of a time-dependent correction in the SunPy case actually gives better agreement between the two in the 304 angstrom case.

You can also choose the number of ions that you'd like to include in the temperature response function calculation. So for example if wanted to see how only including Fe ions affected the response functions,


In [17]:
fe_ions = [ion for ion in ch_data.MasterList if ion.split('_')[0]=='fe']

Or no Fe at all.


In [18]:
no_fe_ions = [ion for ion in ch_data.MasterList if ion.split('_')[0]!='fe']

In [19]:
temperature_response_fe_only = aia_response.calculate_temperature_response(ion_list=fe_ions)

In [20]:
temperature_response_no_fe = aia_response.calculate_temperature_response(ion_list=no_fe_ions)

In [21]:
fig,axes = plt.subplots(3,3,figsize=(12,12),sharex=True,sharey=True)
for channel,ax in zip(sorted(wavelength_response.keys()),axes.flatten()):
    # all_ions
    ax.plot(temperature_response[channel].temperature,temperature_response[channel].response,
            linestyle='-',color=sns.color_palette('deep')[0],label='All ions')
    # Fe only
    ax.plot(temperature_response_fe_only[channel].temperature,
            temperature_response_fe_only[channel].response,
            linestyle='-',color=sns.color_palette('deep')[2],label='Fe only')
    # No Fe
    ax.plot(temperature_response_no_fe[channel].temperature,
            temperature_response_no_fe[channel].response,
            linestyle='-',color=sns.color_palette('deep')[1],label='No Fe')
    ax.set_xlabel(r'$T$ [{:latex}]'.format(temperature_response[channel].temperature.unit))
    ax.set_ylabel(r'$K_{{{}}}$ [{:latex}]'.format(channel,temperature_response[channel].response.unit))
    ax.set_xscale('log')
    ax.set_yscale('log')
    ax.set_ylim([1e-30,2e-24])
    ax.set_xlim([1e5,1e8])
axes[0,0].legend(loc='best')
plt.tight_layout()


Calculating a Custom Emission Table

If you want to change the temperature/density/wavelength over which the emissivities are calculated or change things like the abundance, you can calculate your own emissivity table and then pass that file name in when calculating the temperature response functions.

In this short example, we'll calculate a new table using photospheric abundances, with constant density, and using only handful of ions with very high ionization states. To show the progress bar in the notebook, we need to pass in the notebook=True kwarg. Default is false and nothing will be displayed in notebook. For this to actually work though, you seem to need to have ipywidgets installed.


In [22]:
temperature = np.logspace(5.5,8,100)*u.K
density = 1e9*np.ones(temperature.shape)*u.cm**(-3)
abundance_file = 'sun_photospheric_1998_grevesse'
high_ionization_state_ions = [ion for ion in ch_data.MasterList if ion[-1] != 'd' and int(ion.split('_')[1]) >= 20]
emiss_table_filename = '/home/wtb2/Desktop/test_emiss_table.h5'

In [23]:
make_emiss_table(emiss_table_filename,ion_list=high_ionization_state_ions,temperature=temperature,density=density,
                 notebook=True, abundance_file=abundance_file)


Widget Javascript not detected.  It may not be installed or enabled properly.


In [24]:
custom_temperature_response = aia_response.calculate_temperature_response(ion_list=high_ionization_state_ions,
                                                    emiss_table_file='/home/wtb2/Desktop/test_emiss_table.h5')

In [25]:
fig,axes = plt.subplots(3,3,figsize=(12,12),sharex=True,sharey=True)
for channel,ax in zip(sorted(wavelength_response.keys()),axes.flatten()):
    # all_ions
    ax.plot(temperature_response[channel].temperature,temperature_response[channel].response,
            linestyle='-',color=sns.color_palette('deep')[0],label='Original')
    # Fe only
    ax.plot(custom_temperature_response[channel].temperature,
            custom_temperature_response[channel].response,
            linestyle='-',color=sns.color_palette('deep')[2],label='Custom')
    ax.set_xlabel(r'$T$ [{:latex}]'.format(temperature_response[channel].temperature.unit))
    ax.set_ylabel(r'$K_{{{}}}$ [{:latex}]'.format(channel,temperature_response[channel].response.unit))
    ax.set_xscale('log')
    ax.set_yscale('log')
    ax.set_ylim([1e-30,2e-24])
    ax.set_xlim([1e5,1e8])
axes[0,0].legend(loc='best')
plt.tight_layout()


Detailed Information about Emissivity Tables

The EmissTableInterface object provides a convenient way to inspect the different parts of the atomic data used to calculate the response functions, including the temperature, density, and line and continuum contributions.


In [26]:
emiss_table = EmissTableInterface('/home/wtb2/sunpy/data/aia_emiss_table.h5')

In [27]:
emiss_table.temperature


Out[27]:
$[100000,~115139.54,~132571.14,~152641.8,~175751.06,~202358.96,~232995.18,~268269.58,~308884.36,~355648.03,~409491.51,~471486.64,~542867.54,~625055.19,~719685.67,~828642.77,~954095.48,~1098541.1,~1264855.2,~1456348.5,~1676832.9,~1930697.7,~2222996.5,~2559547.9,~2947051.7,~3393221.8,~3906939.9,~4498432.7,~5179474.7,~5963623.3,~6866488.5,~7906043.2,~9102981.8,~10481131,~12067926,~13894955,~15998587,~18420700,~21209509,~24420531,~28117687,~32374575,~37275937,~42919343,~49417134,~56898660,~65512856,~75431201,~86851137,~1 \times 10^{8}] \; \mathrm{K}$

In [28]:
emiss_table.density


Out[28]:
$[1 \times 10^{10},~8.6851137 \times 10^{9},~7.5431201 \times 10^{9},~6.5512856 \times 10^{9},~5.689866 \times 10^{9},~4.9417134 \times 10^{9},~4.2919343 \times 10^{9},~3.7275937 \times 10^{9},~3.2374575 \times 10^{9},~2.8117687 \times 10^{9},~2.4420531 \times 10^{9},~2.1209509 \times 10^{9},~1.84207 \times 10^{9},~1.5998587 \times 10^{9},~1.3894955 \times 10^{9},~1.2067926 \times 10^{9},~1.0481131 \times 10^{9},~9.1029818 \times 10^{8},~7.9060432 \times 10^{8},~6.8664885 \times 10^{8},~5.9636233 \times 10^{8},~5.1794747 \times 10^{8},~4.4984327 \times 10^{8},~3.9069399 \times 10^{8},~3.3932218 \times 10^{8},~2.9470517 \times 10^{8},~2.5595479 \times 10^{8},~2.2229965 \times 10^{8},~1.9306977 \times 10^{8},~1.6768329 \times 10^{8},~1.4563485 \times 10^{8},~1.2648552 \times 10^{8},~1.0985411 \times 10^{8},~95409548,~82864277,~71968567,~62505519,~54286754,~47148664,~40949151,~35564803,~30888436,~26826958,~23299518,~20235896,~17575106,~15264180,~13257114,~11513954,~10000000] \; \mathrm{\frac{1}{cm^{3}}}$

In [29]:
for i in range(emiss_table['fe_12'].contribution_function.shape[0]):
    plt.plot(emiss_table.temperature,emiss_table['fe_12'].contribution_function[:,i])
plt.xlabel(r'$T$ [{:latex}]'.format(emiss_table.temperature.unit))
plt.ylabel(r'$G(T)$ [{:latex}], Fe XII'.format(emiss_table['fe_12'].contribution_function.unit))
plt.ylim([1e-30,1e-18])
plt.yscale('log')
plt.xscale('log')



In [ ]: