-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding xrt_teem #89
adding xrt_teem #89
Changes from 49 commits
c442075
045ae37
05be3f6
f2b4210
5d8fb21
21856d7
ca0c2f6
2749141
79727c0
16e1df8
fe0a769
6cc2d94
b07863d
d03c5ce
75f8c2f
6307e40
31362a8
40cfc5d
e80f8d9
c87c151
bbe1132
cb3da6d
be6df43
7b870d8
44f9e85
30761b8
bb81bfa
3579b9f
5d51116
39b0956
6269dfd
baab3e3
d9a7aaa
55a965f
31722b7
6393b23
62823d4
fb94fb0
f3b7e74
792f6d4
e415cf2
9bd8c1f
ef4e499
51b38f5
d8fee3a
6a08fdf
9ee5289
071a9ac
60cc5b2
9db5b49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,3 +141,6 @@ version.py | |
#ignore mac files | ||
.DS_store | ||
.DS_Store | ||
|
||
# local directory | ||
idl_routines/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
:orphan: | ||
|
||
`xrtpy.response.xrt_teem` | ||
========================= | ||
|
||
.. currentmodule:: xrtpy.response.xrt_teem | ||
|
||
.. automodapi:: xrtpy.response.xrt_teem | ||
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -34,6 +34,10 @@ The default model assumes coronal abundances :cite:t:`feldman:1992`. | |||||||||||||
.. note:: | ||||||||||||||
XRTpy has future plans to accept other plasma emission spectra models. | ||||||||||||||
|
||||||||||||||
Deriving Temperature and Emission Measure for a Pair of Images | ||||||||||||||
-------------------------------------------------------------- | ||||||||||||||
XRTpy provides a routine, xrt_teem, that employs the objects listed above to derive the temperature and emission measure in for a given pair of images using the filter ratio method. This uses the same methods as in the SolarSoft IDL routine of the same name. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The single backticks are for generating a link to the object in the documentation build, and the tilde indicates that the namespace prefixes should be hidden. There's also a convention to limit lines in documentation to 72 characters (from PEP 8, I think?). |
||||||||||||||
|
||||||||||||||
|
||||||||||||||
X-Ray Filter Channel | ||||||||||||||
********************* | ||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,191 @@ | ||||||
{ | ||||||
"cells": [ | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "642ac1b3", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"# Using xrt_teem to analyze XRT data" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "22b53fce", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"This example demonstrates how to use xrt_teem to calculate the temperature and emission measure in an image using the filter ratio method." | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
...so it shows up as literal text in Markdown. |
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "f5f04496", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"First we need to import xrt_teem." | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"id": "58758bf1", | ||||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"from xrtpy.response.xrt_teem import xrt_teem" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "8aee2bae", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"As an example we will use the test data included in XRTpy, though data with the right characteristics in the XRT archive could also be used. It's necessary to use two images that are the same size and use different filters. To get good results the images should have been taken close in time as well, ideally adjacent in time. Note that not all filter ratios produce good results. \n", | ||||||
"\n", | ||||||
"This data was generated using the IDL routine xrt_prep.pro from SolarSoft and is unnormalized. Data in the Level 1 archive are normalized, which is also okay to use, though the IDL routine xrt_teem.pro did not allow that. For normalized data the image data is multiplied by the exposure time before analysis. " | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The explanations in this notebook are very well written. |
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"id": "da83268f", | ||||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"import pkg_resources\n", | ||||||
"import sunpy.map\n", | ||||||
"\n", | ||||||
"from pathlib import Path\n", | ||||||
"\n", | ||||||
"directory = pkg_resources.resource_filename(\n", | ||||||
" \"xrtpy\", \"response/tests/data/xrt_teem_testing_files\"\n", | ||||||
")\n", | ||||||
"data_files = sorted(Path(directory).glob(\"L1_XRT20110128_*.*.fits\"))\n", | ||||||
"file1 = data_files[1]\n", | ||||||
"file2 = data_files[0]" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "35dd0c83", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"xrt_teem uses SunPy maps as input. " | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"id": "48ac4220", | ||||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"map1 = sunpy.map.Map(file1)\n", | ||||||
"map2 = sunpy.map.Map(file2)" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "905f4e11", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"xrt_teem has several options, mirroring the IDL routine in SolarSoft in most respects. A simple call with no extra parameters calculates the temperature and (volume) emission measure for the two images without any binning or masking of the data." | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"id": "ddc9862b", | ||||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"T_e, EM, Terr, EMerr = xrt_teem(map1, map2)" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "d0ef85f3", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"The outputs of xrt_teem are also SunPy maps. T_e is the electron temperature, EM is the volume emission measure, Terr is a measure of the uncertainties in the temperature determined for each pixel and EMerr is the same for the emission measure. Each map has data and associated metadata. To examine the results one can use matplotlib and sunpy:" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"id": "3ae1f53b", | ||||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"import matplotlib.pyplot as plt\n", | ||||||
"import numpy as np\n", | ||||||
"\n", | ||||||
"from sunpy.coordinates.sun import angular_radius, B0\n", | ||||||
"from sunpy.map import Map\n", | ||||||
"\n", | ||||||
"# To avoid error messages from sunpy we add metadata to the header:\n", | ||||||
"rsun_ref = 6.95700e08\n", | ||||||
"hdr1 = map1.meta\n", | ||||||
"rsun_obs = angular_radius(hdr1[\"DATE_OBS\"]).value\n", | ||||||
"dsun = rsun_ref / np.sin(rsun_obs * np.pi / 6.48e5)\n", | ||||||
"solarb0 = B0(hdr1[\"DATE_OBS\"]).value\n", | ||||||
"hdr1[\"DSUN_OBS\"] = dsun\n", | ||||||
"hdr1[\"RSUN_REF\"] = rsun_ref\n", | ||||||
"hdr1[\"RSUN_OBS\"] = rsun_obs\n", | ||||||
"hdr1[\"SOLAR_B0\"] = solarb0\n", | ||||||
"\n", | ||||||
"fig = plt.figure()\n", | ||||||
"# We could create a plot simply by doing T_e.plot(), but here we choose to make a linear plot of T_e\n", | ||||||
"m = Map((10.0**T_e.data, T_e.meta))\n", | ||||||
"m.plot(title=\"Derived Temperature\", vmin=2.0e6, vmax=1.2e7, cmap=\"turbo\")\n", | ||||||
"m.draw_limb()\n", | ||||||
"m.draw_grid(linewidth=2)\n", | ||||||
"cb = plt.colorbar(label=\"T (K)\")" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "4e069966", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"See the xrt_teem.py code for more information. Among the options are verbose output, binning the data by an integer factor (to increase the signal to noise), specifying a temperature range to examine, providing a mask for excluding regions of the images from the analysis, and setting error thresholds on the temperature and photon noise that differ from the default values." | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"id": "94df8fee", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"These data were analyzed by Guidoni et al. (2015, ApJ 800, 54). See also Narukage et al. (2014, Solar Phys. 289, 1029). The filters for these two images are Ti poly and Be thin. " | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think it might be worth putting in a DOI hyperlink to these articles? |
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"id": "2807a18e", | ||||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"source": [] | ||||||
} | ||||||
], | ||||||
"metadata": { | ||||||
"kernelspec": { | ||||||
"display_name": "Python 3 (ipykernel)", | ||||||
"language": "python", | ||||||
"name": "python3" | ||||||
}, | ||||||
"language_info": { | ||||||
"codemirror_mode": { | ||||||
"name": "ipython", | ||||||
"version": 3 | ||||||
}, | ||||||
"file_extension": ".py", | ||||||
"mimetype": "text/x-python", | ||||||
"name": "python", | ||||||
"nbconvert_exporter": "python", | ||||||
"pygments_lexer": "ipython3", | ||||||
"version": "3.11.0" | ||||||
} | ||||||
}, | ||||||
"nbformat": 4, | ||||||
"nbformat_minor": 5 | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -120,6 +120,7 @@ extend-ignore = | |
W605, | ||
RST210, | ||
RST213, | ||
RST305, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot to add this error code as one of the linter checks to ignore. This one checks for undefined substitutions, but isn't smart enough to check for our globally defined substitutions. |
||
RST306 | ||
|
||
exclude = | ||
|
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,141 @@ | ||||||||||||
import numpy as np | ||||||||||||
import pkg_resources | ||||||||||||
import pytest | ||||||||||||
import sunpy.map | ||||||||||||
|
||||||||||||
from astropy.io import fits | ||||||||||||
from pathlib import Path | ||||||||||||
from scipy.io import readsav | ||||||||||||
|
||||||||||||
from xrtpy.response.xrt_teem import xrt_teem | ||||||||||||
|
||||||||||||
|
||||||||||||
def get_observed_data(): | ||||||||||||
directory = pkg_resources.resource_filename( | ||||||||||||
"xrtpy", "response/tests/data/xrt_teem_testing_files" | ||||||||||||
) | ||||||||||||
data_files = sorted(Path(directory).glob("L1_XRT20110128_*.*.fits")) | ||||||||||||
|
||||||||||||
return data_files | ||||||||||||
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
...although sometimes an intermediate variable name can make code more readable. |
||||||||||||
|
||||||||||||
|
||||||||||||
def get_IDL_results_data(): | ||||||||||||
directory = pkg_resources.resource_filename( | ||||||||||||
"xrtpy", "response/tests/data/xrt_teem_testing_files" | ||||||||||||
) | ||||||||||||
results_files = sorted(Path(directory).glob("IDL_results_*.sav")) | ||||||||||||
|
||||||||||||
return results_files | ||||||||||||
Comment on lines
+26
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
|
||||||||||||
|
||||||||||||
def rebin_image(data, binfac=1): | ||||||||||||
""" | ||||||||||||
Given a data array and a binning factor return the data array rebinned by | ||||||||||||
the binning factor. | ||||||||||||
""" | ||||||||||||
|
||||||||||||
s = data.shape | ||||||||||||
ns = (s[0] // binfac, s[1] // binfac) | ||||||||||||
rbs = (ns[0], binfac, ns[1], binfac) | ||||||||||||
# sums the data in binfac x binfac sized regions | ||||||||||||
drbin = data.reshape(rbs).mean(-1).mean(1) | ||||||||||||
# for a boolean mask, this makes a pixel masked if any of the summed | ||||||||||||
# pixels is masked. If we want to mask only if all the pixels are masked | ||||||||||||
# then we could use prod in place of sum above | ||||||||||||
|
||||||||||||
if data.dtype == bool: | ||||||||||||
# need to convert back to bool after summing | ||||||||||||
drbin = drbin.astype(bool) | ||||||||||||
return drbin | ||||||||||||
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
[optional] |
||||||||||||
|
||||||||||||
|
||||||||||||
def test_standard_case(): | ||||||||||||
""" | ||||||||||||
Test case with all default values: | ||||||||||||
no binning, no masking, no temperature range, standard thresholds | ||||||||||||
""" | ||||||||||||
|
||||||||||||
data_files = get_observed_data() | ||||||||||||
# it turns out that the IDL test data was generated with the inverse order | ||||||||||||
# of the files | ||||||||||||
file1 = data_files[1] | ||||||||||||
file2 = data_files[0] | ||||||||||||
map1 = sunpy.map.Map(file1) | ||||||||||||
map2 = sunpy.map.Map(file2) | ||||||||||||
|
||||||||||||
T_e, EM, Terr, EMerr = xrt_teem(map1, map2) | ||||||||||||
|
||||||||||||
testdata = get_IDL_results_data() | ||||||||||||
|
||||||||||||
# This is needed because there are multiple test data sets, though so far | ||||||||||||
# only a test written for the standard case | ||||||||||||
fnames = [td.name for td in testdata] | ||||||||||||
idata1 = fnames.index("IDL_results_bin1.sav") | ||||||||||||
testdata1 = testdata[idata1] | ||||||||||||
|
||||||||||||
idldata = readsav(testdata1) | ||||||||||||
goodT = (T_e.data > 0.0) & (idldata.te > 0.0) | ||||||||||||
goodE = (EM.data > 0.0) & (idldata.em > 0.0) | ||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** T_e.data[goodT], 10.0 ** idldata.te[goodT], atol=2.0e5, rtol=0.02 | ||||||||||||
) | ||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** EM.data[goodE], 10.0 ** idldata.em[goodE], atol=4.0e44, rtol=0.03 | ||||||||||||
) | ||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** Terr.data[goodT], 10.0 ** idldata.et[goodT], atol=1.0e4, rtol=0.08 | ||||||||||||
) | ||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** EMerr.data[goodE], 10.0 ** idldata.ee[goodE], atol=4.0e43, rtol=0.02 | ||||||||||||
) | ||||||||||||
|
||||||||||||
|
||||||||||||
def test_binning_case(): | ||||||||||||
""" | ||||||||||||
Test case with following parameters: | ||||||||||||
binning by a factor of 2 | ||||||||||||
no masking | ||||||||||||
no temperature range | ||||||||||||
standard thresholds | ||||||||||||
""" | ||||||||||||
|
||||||||||||
data_files = get_observed_data() | ||||||||||||
# it turns out that the IDL test data was generated with the inverse order | ||||||||||||
# of the files | ||||||||||||
file1 = data_files[1] | ||||||||||||
file2 = data_files[0] | ||||||||||||
map1 = sunpy.map.Map(file1) | ||||||||||||
map2 = sunpy.map.Map(file2) | ||||||||||||
|
||||||||||||
T_e, EM, Terr, EMerr = xrt_teem(map1, map2, binfac=2) | ||||||||||||
|
||||||||||||
testdata = get_IDL_results_data() | ||||||||||||
|
||||||||||||
# This is needed because there are multiple test data sets | ||||||||||||
fnames = [td.name for td in testdata] | ||||||||||||
idata1 = fnames.index("IDL_results_bin2.sav") | ||||||||||||
testdata1 = testdata[idata1] | ||||||||||||
|
||||||||||||
idldata = readsav(testdata1) | ||||||||||||
idlTe = rebin_image(idldata.te, 2) | ||||||||||||
idlEM = rebin_image(idldata.em, 2) | ||||||||||||
idlTerr = rebin_image(idldata.et, 2) | ||||||||||||
idlEMerr = rebin_image(idldata.ee, 2) | ||||||||||||
goodT = (T_e.data > 0.0) & (idlTe > 0.0) | ||||||||||||
goodE = (EM.data > 0.0) & (idlEM > 0.0) | ||||||||||||
|
||||||||||||
delta = 10.0 ** T_e.data[goodT] - 10.0 ** idlTe[goodT] | ||||||||||||
x = 10.0 ** idlTe[goodT] | ||||||||||||
|
||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** T_e.data[goodT], 10.0 ** idlTe[goodT], atol=2.0e5, rtol=0.02 | ||||||||||||
) | ||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** EM.data[goodE], 10.0 ** idlEM[goodE], atol=1.0e44, rtol=0.05 | ||||||||||||
) | ||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** Terr.data[goodT], 10.0 ** idlTerr[goodT], atol=1.0e4, rtol=0.1 | ||||||||||||
) | ||||||||||||
assert np.allclose( | ||||||||||||
10.0 ** EMerr.data[goodE], 10.0 ** idlEMerr[goodE], atol=2.0e43, rtol=0.03 | ||||||||||||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we add a new module, we often need to add a stub documentation file so that the docstrings from the module show up in the online documentation.