cena_energy_resolution_kazama

Analyze Kazama-san’s energy resolution data

The program input is the numeric data from Figs 16-18 in Kazama-2006, derived using “g3data”.

'''Analyze Kazama-san's energy resolution data

The program input is the numeric data from Figs 16-18 in Kazama-2006,
derived using "g3data".
'''

import os
import sys
import logging
logging.basicConfig()
import datetime
import math

import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import scipy.optimize

matrix_g3data_debug = True


def matrix_g3data(visualize=False):
    ''' Graced data matrix in Kazama-2006.

    :returns: Three branches, 25, 750, and 3300 eV, is returned, i.e.,
            (matrix_25, matrix_750, matrix_3300).
            matrix_`XX` is (N, 2) numpy array, where N is the data point.
            Data is retrieved using ``g3data`` manually.
            Then, the data are interpolated.
    '''

    # 25 eV three data
    dat25_1 = np.fromstring('''2.64150943396  0
7.54716981132  34.1523341523
12.641509434  122.604422604
17.5471698113  516.216216216
22.4528301887  947.42014742
27.5471698113  1004.91400491
32.641509434  929.72972973
37.5471698113  743.98034398
42.4528301887  617.936117936
47.5471698113  487.469287469
52.4528301887  339.312039312
57.5471698113  184.520884521
62.641509434  124.815724816
67.5471698113  80.5896805897
72.8301886792  47.4201474201
77.5471698113  38.5749385749
82.641509434  18.6732186732
87.5471698113  9.82800982801
92.4528301887  9.82800982801
97.5471698113  7.61670761671
102.641509434  3.1941031941
107.547169811  0.982800982801''', sep=' ').reshape((22, 2))

    dat25_2 = np.fromstring('''2.64150943396  0
7.54716981132  34.353993788
12.641509434  122.693430995
17.5471698113  513.985443419
22.4528301887  945.080895647
27.5471698113  1004.67340411
32.641509434  929.376477678
37.5471698113  743.518612953
42.4528301887  615.154605721
47.5471698113  484.575124009
52.4528301887  338.520699087
57.5471698113  183.616893051
62.4528301887  128.225858792
67.5471698113  79.4645588985
72.641509434  46.1823744843
77.5471698113  37.2286866626
82.641509434  15.0030133049
87.5471698113  8.26062769459
92.4528301887  8.15214871819
97.5471698113  5.82819526216
102.641509434  1.29293959483
107.547169811  1.18446061842
''', sep=' ').reshape((22, 2))

    dat25_3 = np.fromstring('''2.53343823761  0
7.44295830055  34.2485360337
12.4468922109  121.656248821
17.5452399685  522.347223472
22.4547600315  948.501194689
27.4586939418  1006.01960858
32.4626278521  932.910716204
37.4665617624  744.672672533
42.5649095201  613.998333532
47.474429583  486.646769694
52.4783634933  337.154113477
57.4822974036  185.44743512
62.4862313139  126.729686652
67.4901652242  81.2960710252
72.4940991345  46.9325660999
77.4036191975  38.0311867635
82.5019669552  18.0579547731
87.5059008655  8.04869339016
92.5098347758  8.00253163822
97.5137686861  7.95636988628
102.517702596  2.37515278379
107.521636507  1.22197996174''', sep=' ').reshape((22, 2))

    dat25 = np.zeros_like(dat25_1)
    dat25[:, 0] = np.linspace(2.5, 107.5, num=22)
    dat25[:, 1] = np.array([dat25_1[:, 1], dat25_2[:, 1], dat25_3[:, 1]]).mean(0) / 10000

    dat750_1 = np.fromstring('''250  0
354.722202882  201.10701107
449.63621806  322.878228782
549.51959897  498.15498155
649.751096568  747.232472325
759.407853512  994.464944649
858.377428114  976.014760148
951.811947365  784.132841328
1050.39859361  684.501845018
1148.83729026  553.505535055
1252.06259138  437.269372694
1350.62312887  332.103321033
1453.83972708  214.022140221
1552.72227251  177.121771218
1651.47427418  112.546125461
1750.37422544  79.3357933579
1849.29158254  49.815498155
1948.27856297  35.0553505535
2051.96511871  16.6051660517
2151.02172248  16.6051660517
2250.0522175  11.0701107011
2353.79969366  5.53505535055
2448.1393163  5.53505535055
2551.89549537  1.84501845018
2655.67778319  3.69003690037
''', sep=' ').reshape((25, 2))

    dat750_2 = np.fromstring('''250.196695515  0
349.331235248  198.339483395
450.826121164  328.413284133
549.960660897  500.922509225
649.095200629  760.147601476
750.590086546  993.542435424
849.724626279  976.014760148
951.219512195  778.597785978
1050.35405193  678.966789668
1149.48859166  547.04797048
1250.98347758  431.734317343
1350.11801731  330.258302583
1449.25255704  214.022140221
1550.74744296  177.121771218
1649.88198269  113.468634686
1749.01652242  78.4132841328
1850.51140834  49.815498155
1949.64594807  34.1328413284
2051.14083399  15.6826568266
2150.27537372  15.6826568266
2249.40991345  9.22509225092
2350.90479937  5.53505535055
2450.0393391  4.61254612546
2551.53422502  0
2653.02911094  1.84501845018
''', sep=' ').reshape((25, 2))

    dat750_3 = np.fromstring('''
250.505050505  0.2457002457
349.494949495  194.656019656
450.841750842  325.491400491
549.831649832  498.71007371
651.178451178  759.459459459
750.168350168  993.488943489
851.515151515  975.982800983
950.505050505  781.572481572
1049.49494949  682.063882064
1150.84175084  546.621621622
1249.83164983  435.135135135
1351.17845118  330.098280098
1450.16835017  214.926289926
1551.51515152  178.071253071
1650.50505051  113.574938575
1749.49494949  79.484029484
1850.84175084  50
1949.83164983  35.257985258
2051.17845118  16.8304668305
2150.16835017  16.8304668305
2251.51515152  10.3808353808
2352.86195286  6.69533169533
2444.78114478  5.77395577396
2553.1986532  1.16707616708
2649.83164983  3.9312039312
''', sep=' ').reshape((25, 2))

    dat750 = np.zeros_like(dat750_1)
    dat750[:, 0] = np.linspace(250, 2650, num=25)
    dat750[:, 1] = np.array([dat750_1[:, 1], dat750_2[:, 1], dat750_3[:, 1]]).mean(0) / 10000

    dat3300_1 = np.fromstring('''
1376.03305785  0
1623.96694215  18.8747731397
1871.90082645  178.584392015
2119.83471074  429.764065336
2380.16528926  543.012704174
2628.09917355  694.010889292
2876.03305785  707.078039927
3123.96694215  734.664246824
3371.90082645  650.453720508
3619.83471074  577.858439201
3867.76859504  463.157894737
4128.09917355  399.274047187
4376.03305785  294.736842105
4623.96694215  200.362976407
4871.90082645  162.613430127
5119.83471074  85.6624319419
5380.16528926  75.499092559
5628.09917355  34.8457350272
5876.03305785  26.1343012704
6123.96694215  18.8747731397
6371.90082645  5.80762250454
6619.83471074  8.71143375681
6880.16528926  10.1633393829
7128.09917355  4.3557168784
7376.03305785  7.25952813067
7623.96694215  2.90381125227
7871.90082645  4.3557168784
''', sep=' ').reshape((27, 2))

    dat3300_2 = np.fromstring('''
1376.03305785  0
1623.96694215  18.8747731397
1871.90082645  178.584392015
2119.83471074  429.764065336
2380.16528926  543.012704174
2628.09917355  694.010889292
2876.03305785  707.078039927
3123.96694215  734.664246824
3371.90082645  650.453720508
3619.83471074  577.858439201
3867.76859504  463.157894737
4128.09917355  399.274047187
4376.03305785  294.736842105
4623.96694215  200.362976407
4871.90082645  162.613430127
5119.83471074  85.6624319419
5380.16528926  75.499092559
5628.09917355  34.8457350272
5876.03305785  26.1343012704
6123.96694215  18.8747731397
6371.90082645  5.80762250454
6619.83471074  8.71143375681
6880.16528926  10.1633393829
7128.09917355  4.3557168784
7376.03305785  7.25952813067
7623.96694215  2.90381125227
7871.90082645  4.3557168784
''', sep=' ').reshape((27, 2))

    dat3300_3 = np.fromstring('''
1376.43932684  0.726744186047
1624.44641275  17.4418604651
1872.45349867  180.23255814
2126.66076174  431.686046512
2374.66784765  535.610465116
2622.67493357  694.76744186
2876.88219663  707.122093023
3124.88928255  735.465116279
3372.89636847  650.436046512
3627.10363153  575.581395349
3875.11071745  462.936046512
4123.11780337  401.162790698
4377.32506643  294.331395349
4625.33215235  199.854651163
4873.33923826  162.063953488
5127.54650133  84.3023255814
5375.55358725  75.5813953488
5623.56067316  34.8837209302
5877.76793623  25.4360465116
6125.77502214  18.8953488372
6373.78210806  5.08720930233
6627.98937112  7.99418604651
6875.99645704  9.4476744186
7124.00354296  3.63372093023
7378.21080602  6.54069767442
7626.21789194  2.90697674419
7874.22497786  3.63372093023
''', sep=' ').reshape((27, 2))

    dat3300 = np.zeros_like(dat3300_1)
    dat3300[:, 0] = np.linspace(1375, 7875, num=27)
    dat3300[:, 1] = np.array([dat3300_1[:, 1], dat3300_2[:, 1], dat3300_3[:, 1]]).mean(0) / 10000

    if visualize:
        fig = plt.figure()
        enestep = np.linspace(0, 3, 300)

        ax = fig.add_subplot(221)
        ax.plot(dat25_1[:, 0], dat25_1[:, 1])
        ax.plot(dat25_2[:, 0], dat25_2[:, 1])
        ax.plot(dat25_3[:, 0], dat25_3[:, 1])
        ax.plot(dat25[:, 0], dat25[:, 1] * 1e4, lw=3)
        lim0 = 12.5
        lim1 = 62.5
        ax.plot(enestep * 25., energy_response_triangle(enestep, limit0=lim0/25., limit1=lim1/25.) * dat25[:, 1].max() * 1.1e4)
        ax.text(80, 800, '%g--%g eV' % (lim0, lim1))
        ax.set_title('25 eV calibration')

        ax = fig.add_subplot(222)
        ax.plot(dat750_1[:, 0], dat750_1[:, 1])
        ax.plot(dat750_2[:, 0], dat750_2[:, 1])
        ax.plot(dat750_3[:, 0], dat750_3[:, 1])
        ax.plot(dat750[:, 0], dat750[:, 1] * 1e4, lw=3)
        lim0 = 325
        lim1 = 1650
        ax.plot(enestep * 750., energy_response_triangle(enestep, limit0=lim0/750., limit1=lim1/750.) * dat750[:, 1].max() * 1e4)
        ax.text(2000, 800, '%g--%g eV' % (lim0, lim1))
        ax.set_title('750 eV calibration')

        ax = fig.add_subplot(223)
        ax.plot(dat3300_1[:, 0], dat3300_1[:, 1])
        ax.plot(dat3300_2[:, 0], dat3300_2[:, 1])
        ax.plot(dat3300_3[:, 0], dat3300_3[:, 1])
        ax.plot(dat3300[:, 0], dat3300[:, 1] * 1e4, lw=3)
        peak = 3000.
        lim0 = 1650.
        lim1 = 4950.
    
        ax.plot(enestep * 3300., energy_response_triangle(enestep, peak=peak/3300., limit0=lim0/3300, limit1=lim1/3300.) * dat3300[:, 1].max() * 1.1e4)
        ax.text(4000, 800, '%g--(%g)--%g eV' % (lim0, peak, lim1))
        ax.set_title('3300 eV calibration')

        # Normalized

        ax = fig.add_subplot(224)
        ax.plot(dat25[:, 0] / 25.0, dat25[:, 1] / dat25[:, 1].max(), label='25eV')
        ax.plot(dat750[:, 0] / 750.0, dat750[:, 1] / dat750[:, 1].max(), label='750eV')
        ax.plot(dat3300[:, 0] / 3300.0, dat3300[:, 1] / dat3300[:, 1].max(), label='3300eV')

        ax.plot(enestep, energy_response_triangle(enestep), ':', lw=2, label='Calrep.')
        ax.grid(True)
        ax.legend()



    return dat25, dat750, dat3300
    

def step_function(x, n):
    ''' A step function

    :param x: Scalar or np.array.
    :param n: Step function parameter.
    :returns: 0 if x<n whereas 1 for x>=n.
    '''

    x2 = np.array(x)
    mask = (x2 >= n)
    x2 = np.ma.zeros(x2.shape)
    x2.mask = mask
    return x2.filled(1) * 1.


def energy_response_triangle(e, peak=1.0, limit0=0.2, limit1=2.2):
    ''' Energy response function with triangle shape

    According to Martin's calibration report, triangle approximation with
    half max in 0.6--1.6 E0 is implemented.
    This means that E<=0.2 and E>=2.2 is no response.

    :param e: Energy normalized by the incoming beam energy
    :type e: ``np.array``

    :returns: Energy response for the given energy, *e*, normalized by the
        maximum resonse at E=1. (energy_response_triangle(1) == 1)
    '''
    e2 = np.array(e)

    e2 = np.ma.masked_less(e2, limit0)
    e2 = np.ma.masked_greater(e2, limit1)

    ke = (((e2 - peak) / (peak - limit0) + 1) * (1. - step_function(e2, peak))
            + ((e2 - peak) / (peak - limit1) + 1) * step_function(e2, peak))

    return ke.filled(0) * 1.   # * 1. is for scalar input to return as scalar
    

if __name__ == '__main__':
    mx = matrix_g3data(visualize=True)