""" Energy table for MEX/IMA
Energy table for VEX/IMA can be obtained by a function
- :func:`get_default_table_for` : When you have raw data
(:class:`CntMatrix2D <irfpy.imacommon.imascipac.CntMatrix2D>`
or :class:`CntMatrix <irfpy.imacommon.imascipac.CntMatrix>`)
See the description in :func:`get_default_table_for` to read the data and the energy table.
- :func:`get_default_table` : When you know EEPROM/PROM of the data.
**Energy table, patch, and version.**
- Italic is for the EEPROM section where patch was applied.
- Plus (+) is for the EEPROM section that is used for nominal operation.
- T0 is version 4: :func:`get_default_table_v4`
- T1 is version 5: :func:`get_default_table_v5`
- T2 is also version 5 but different from T1. ``irfpy`` calls as ``v5_late``: :func:`get_default_table_v5_late`
- T3 is version 6: :func:`get_default_table_v6` or :func:`get_default_table_v6_squashed`
========== == == ====== ====== ====== ====== ====== ==============================
Patch ver p0 p1 p2 p3 p4
Patch date 020412 060520 090625 130220 130909
---------- -- -- ------ ------ ------ ------ ------ ------------------------------
Section HK HK
---------- -- -- ------ ------ ------ ------ ------ ------------------------------
PROM 0 0 +T0 T0 T0 T0 T0
EEPROM0 1 1 T0 +*T1* T1 T1 T1
EEPROM1 2 2 T0 *T1* T1 T1 T1
EEPROM2 3 3 T0 *T1* T1 T1 T1
EEPROM3 4 4 T0 *T1* T1 T1 T1
EEPROM4 5 5 T0 *T1* T1 T1 T1
EEPROM5 6 6 T0 *T1* T1 T1 T1
EEPROM6 7 7 T0 *T1* T1 T1 T1
EEPROM7 8 8 T0 *T1* T1 T1 T1
EEPROM8 9 9 T0 *T1* T1 T1 T1
EEPROM9 10 a T0 *T1* T1 T1 T1
EEPROM10 11 b T0 *T1* T1 T1 T1
EEPROM11 12 c T0 *T1* T1 T1 T1
EEPROM12 13 d T0 *T1* T1 T1 T1
EEPROM13 14 e T0 *T1* T1 T1 *T3* For fast-mode
EEPROM14 15 f T0 *T1* T1 *T2* T2 For auto-reduction
EEPROM15 16 10 T0 *T1* +*T2* +T2 +T2
========== == == ====== ====== ====== ====== ====== ==============================
For IMAextra, ``ETableN`` entry of ``data`` dictionary gives the information of energy table.
Users can use the function :func:`get_default_table_extra` to choose the energy table.
======== =========== ===========================================
IMAEXTRA PROM/EEPROM Version
-------- ----------- -------------------------------------------
0 0 :func:`v4 <get_default_table_v4>`
1 1 :func:`v5 <get_default_table_v5>`
2 15, 16 :func:`v5late <get_default_table_v5_late>`
3? (TBC) 14 :func:`v6 <get_default_table_v6>`
======== =========== ===========================================
See https://sspt-wiki.irf.se/doku.php?id=sspt:imaenergytable for details.
**Negative energy**
Some of the default table contains negative energy, which should be invalid.
For normal data analysis, those values shall be disregarded.
In this case, you can use, for example,
>>> tbl = get_default_table_v5_late()
>>> print(tbl[95])
-20.0
>>> tbl[tbl < 0] = np.nan
>>> print(tbl[95])
nan
For some specific purposes, ``irfpy`` support to change this feature,
namely, the negative values are converted to arbitrary positive values.
>>> tbl = get_default_table_v5_late(keep_negative=False)
>>> tbl[95] # This is an arbitrary positive value.
1e-10
"""
import numpy as np
[docs]def get_default_table_for(imadata, keep_negative=True):
""" Get the energy table for MEX/IMA
:param imadata: Data of MEX/IMA. :class:`irfpy.imacommon.imascipac.CntMatrix` object is expected.
:param keep_negative: If *True* (default), negative energy is kept as is. Thus, the
returned table will contain negative energy. If *False*, the negative values
are proxied by a very small positive energy, in order to confirm the returned array
always positive.
:return: The energy table in eV/q. (96) shape.
Example of getting the energy table:
First, to get the IMA data, a peraparation is eneded.
>>> import datetime
>>> from irfpy.mima.rawdata import DataCenterCount3d
>>> dc = DataCenterCount3d()
Then, you get the data.
>>> t0 = datetime.datetime(2005, 10, 5, 7, 30)
>>> t, ima3d = dc.nextof(t0) # ima3d is an object of irfpy.imacommon.imascipac.CntMatrix
>>> print(t)
2005-10-05 07:31:47.529660
>>> print(ima3d)
<<class 'irfpy.imacommon.imascipac.CntMatrix'>(MEX/IMA)@2005-10-05T07:31:47.529660:MOD=24 >>24<<:CNTmax=108>
Now, you can get the IMA energy table.
>>> etbl = get_default_table_for(ima3d) # Getting the energy table
>>> print(etbl[0])
32288.7
Different time uses different energy table.
>>> t0 = datetime.datetime(2008, 10, 5, 4, 0, 0)
>>> t, ima3d = dc.nextof(t0)
>>> etbl = get_default_table_for(ima3d)
>>> print(etbl[0])
25001.5
>>> t0 = datetime.datetime(2012, 10, 5, 4, 0, 0)
>>> t, ima3d = dc.nextof(t0)
>>> etbl = get_default_table_for(ima3d)
>>> print(etbl[0])
15005.6
"""
promsection = imadata.hk.promsection
return get_default_table(promsection, keep_negative=keep_negative)
[docs]def get_default_table(promsection, keep_negative=True):
""" Get the default energy table for MEX/IMA.
:param promsection: PROM/EEPROM section. This defines the energy / elevation table.
:param keep_negative: If *True* (default), negative energy is kept as is. Thus, the
returned table will contain negative energy. If *False*, the negative values
are proxied by a very small positive energy, in order to confirm the returned array
always positive.
:return: The energy table in eV/q. (96) shape.
>>> etbl0 = get_default_table(0)
>>> print(etbl0[0])
32288.7
>>> etbl1 = get_default_table(1)
>>> print(etbl1[0])
25001.5
>>> etbl14 = get_default_table(14)
>>> print(etbl14[0])
4000.0
>>> etbl15 = get_default_table(15)
>>> print(etbl15[0])
15005.6
>>> etbl16 = get_default_table(16)
>>> print(etbl16[0])
15005.6
"""
if promsection == 0: # In practice, 0 (EEPROM) was used for the initial phase,
etable = get_default_table_v4(negative=keep_negative)
elif promsection == 1:
etable = get_default_table_v5(negative=keep_negative)
elif promsection == 14:
etable = get_default_table_v6(negative=keep_negative)
elif promsection in (15, 16): # and 15(RAW) and 16(RAW) was used for the later phase.
etable = get_default_table_v5_late(negative=keep_negative)
else:
raise ValueError('No EEPROM {} used. It must be either of (0, 1, 14, 15, 16)'.format(promsection))
return etable
def __handle_negative(negative, keep_negative):
""" A helper function to handle the negative energy
:param negative: negative keyword, bool or None
:param keep_negative: keep_negative keyword, bool or None
:return: bool value if or not we use negative energy
>>> __handle_negative(None, None) # +doctest: +IGNORE_EXCEPTION_DETAILS
Traceback (most recent call last):
ValueError: Either negative (=None) or keep_negative (=None) should be bool.
>>> __handle_negative(True, False)
True
>>> __handle_negative(True, True)
True
>>> __handle_negative(None, True)
True
>>> __handle_negative(None, False)
False
>>> __handle_negative(False, True)
False
>>> __handle_negative(False, False)
False
"""
# If negative is None, use keep_negative value
if negative is None:
if keep_negative is None:
raise ValueError('Either negative (={}) or keep_negative (={}) should be bool.'.format(negative, keep_negative))
else:
return keep_negative
else: # Otherwise, use negative
return negative
[docs]def get_default_table_v4(negative=None, keep_negative=True):
''' Return the default table (v4).
:keyword negative: (OBSOLETE) Alias to ``keep_negative``.
By historical reason, ``negative`` is prioritized than ``keep_negative``
but should use ``keep_negative`` for future.
:keyword keep_negative: If *True*, negative energy is considered as valid (default).
If *False*, negative energy is proxied by a very small positive energy,
in order to confirm the returned array always positive.
:returns: Energy table in eV/q. 96 elements.
'''
etbl = np.array([32288.7, 29659.4, 27234.1, 25001.5, 22950.2,
21068.8, 19334.8, 17748.2, 16286.2, 14948.9, 13713.6,
12568.9, 11526.2, 10574.3, 9690.3, 8874.3, 8137.6,
7446.3, 6823.0, 6245.0, 5712.3, 5225.0, 4771.6,
4352.3, 3978.3, 3627.0, 3309.7, 3015.0, 2743.0,
2493.7, 2267.0, 2051.7, 1859.0, 1677.7, 1519.0,
1371.7, 1235.7, 1099.7, 986.3, 884.3, 782.3, 691.7,
612.3, 533.0, 465.0, 397.0, 340.3, 283.7, 227.0,
181.7, 147.7, 102.3, 68.3, 34.3, 0.3, -1.0, -1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, ])
negative = __handle_negative(negative, keep_negative)
if not negative:
etbl_negative = np.linspace(100e-10, 1e-10, 96)
etbl = np.where(etbl > 0, etbl, etbl_negative)
return etbl
[docs]def get_default_table_v5(negative=None, keep_negative=True):
''' Return the default table (v5).
:keyword negative: (OBSOLETE) Alias to ``keep_negative``.
By historical reason, ``negative`` is prioritized than ``keep_negative``
but should use ``keep_negative`` for future.
:keyword keep_negative: If *True*, negative energy is considered as valid (default).
If *False*, negative energy is proxied by a very small positive energy,
in order to confirm the returned array always positive.
:returns: Energy table in eV/q. 96 elements.
'''
etbl = np.array([25001.5,23018.1,21204.8,19527.5,17986.2,
16558.2,15254.9,14042.2,12931.6,11911.6,10970.9,
10109.6,9304.9,8568.3,7888.3,7265.0,6698.3,6165.6,
5678.3,5225.0,4817.0,4431.6,4080.3,3763.0,3468.3,
3185.0,2935.7,2709.0,2493.7,2289.7,2108.3,1949.7,
1791.0,1655.0,1519.0,1394.3,1292.3,1190.3,1088.3,
1009.0,929.7,850.3,782.3,725.7,669.0,612.3,567.0,
521.7,476.3,442.3,408.3,374.3,340.3,317.7,295.0,
272.3,249.7,227.0,215.7,193.0,181.7,159.0,147.7,
136.3,125.0,113.7,113.7,102.3,92.4,85.1,78.4,72.2,
66.5,61.2,56.4,51.9,47.8,44.0,40.6,37.4,34.4,31.7,
29.2,26.9,24.7,22.8,21.0,19.3,17.8,16.4,15.1,13.9,
12.8,11.8,10.9,10.0])
return etbl
[docs]def get_default_table_v5_late(negative=None, keep_negative=True):
''' Return the default table (yet another v5).
:keyword negative: (OBSOLETE) Alias to ``keep_negative``.
By historical reason, ``negative`` is prioritized than ``keep_negative``
but should use ``keep_negative`` for future.
:keyword keep_negative: If *True*, negative energy is considered as valid (default).
If *False*, negative energy is proxied by a very small positive energy,
in order to confirm the returned array always positive.
:returns: Energy table in eV/q. 96 elements.
>>> tbl = get_default_table_v5_late()
>>> print(tbl.shape)
(96,)
>>> print(tbl[-1])
-20.0
>>> tbl2 = get_default_table_v5_late(negative=False)
>>> print(tbl2.shape)
(96,)
>>> print(tbl2[-1])
1e-10
'''
etbl = np.array([15005.6, 13747.6, 12602.9, 11548.9,
10585.6, 9701.6, 8896.9, 8148.9, 7469,
6845.6, 6279, 5757.6, 5270.3, 4839.6, 4431.6,
4057.6, 3717.6, 3411.7, 3128.3, 2867.7,
2629.7, 2403, 2210.3, 2017.7, 1859, 1700.3, 1553,
1428.3, 1303.7, 1201.7, 1099.7, 1009, 918.3, 850.3,
771, 714.3, 646.3, 601, 544.3, 499, 465, 419.7,
385.7, 351.7, 329, 295, 272.3, 249.7, 227, 215.7,
193, 181.7, 159, 147.7, 136.3, 125, 113.7, 102.3,
95.9, 87.9, 80.6, 73.9, 67.7, 62.1, 56.9, 52.1, 47.8,
43.8, 40.1, 36.8, 33.7, 30.9, 28.3, 26, 23.8, 21.8,
20, 17.9, 15.8, 13.7, 11.6, 9.5, 7.4, 5.3, 3.2, 1,
-1, -3.2, -5.3, -7.4, -9.5, -11.6, -13.7, -15.8, -17.9, -20,])
negative = __handle_negative(negative, keep_negative)
if not negative:
etbl_negative = np.linspace(100e-10, 1e-10, 96)
etbl = np.where(etbl > 0, etbl, etbl_negative)
return etbl
[docs]def get_default_table_v6(negative=None, keep_negative=True):
''' Return the default table for the v6, the 24s fast scan.
:keyword negative: (OBSOLETE) Alias to ``keep_negative``.
By historical reason, ``negative`` is prioritized than ``keep_negative``
but should use ``keep_negative`` for future.
:keyword keep_negative: If *True*, negative energy is considered as valid (default).
If *False*, negative energy is proxied by a very small positive energy,
in order to confirm the returned array always positive.
:returns: Energy table in eV/q. 96 elements.
>>> etbl = get_default_table_v6()
>>> print(etbl.shape)
(96,)
>>> print(etbl[0])
4000.0
'''
e32 = get_default_table_v6_squashed()
return np.hstack([e32, e32, e32])
[docs]def get_default_table_v6_squashed(negative=None, keep_negative=True):
''' Get the default table for v6, the 24s fast scan, with 32 energy steps.
:keyword negative: (OBSOLETE) Alias to ``keep_negative``.
By historical reason, ``negative`` is prioritized than ``keep_negative``
but should use ``keep_negative`` for future.
:keyword keep_negative: If *True*, negative energy is considered as valid (default).
If *False*, negative energy is proxied by a very small positive energy,
in order to confirm the returned array always positive.
:keyword invalidstep: Either ``keep``, ``replace``, or ``nan``.
:returns: Energy table in eV/q. 32 elements.
'''
e32 = np.array([4000, 3407.3, 2902.4, 2472.3, 2105.9, 1793.9, 1528.0, 1301.6,
1108.7, 944.4, 804.5, 685.3, 583.7, 497.2, 423.5, 360.8,
307.3, 261.8, 223.0, 189.9, 161.8, 137.8, 117.4, 100.0,
88.8, 77.5, 66.2, 55.0, 43.8, 32.5, 21.2, 10.0])
return e32