Use case 0: Playing around with ShowerModel

[1]:
import showermodel as sm
import numpy as np
import matplotlib.pyplot as plt

Shower generation

Let’s generate a 1 TeV gamma-induced shower with 20 degrees zenith angle impacting 0.1 km east and 0.2 km north in the local coordinate system

[2]:
shower = sm.Shower(1.e6, theta=20., az=45., x0=0.1, y0=0.2)

The Shower object contains information on the shower track geometry, energy deposit profile and both Cherenkov and fluorescence light production

[3]:
#shower.track
shower.profile
#shower.cherenkov
#shower.fluorescence
[3]:
X s dX E_dep N_ch
0 833.809518 1.639942e+00 20.886275 1745.333353 31.730952
1 813.133111 1.621252e+00 20.467949 2162.692432 40.159662
2 792.870826 1.602432e+00 20.058002 2655.502880 50.365790
3 773.014369 1.583483e+00 19.656266 3231.401931 62.600293
4 753.555612 1.564409e+00 19.262576 3897.468541 77.120066
... ... ... ... ... ...
545 0.000096 4.178872e-07 0.000021 0.000009 0.090310
546 0.000075 3.250234e-07 0.000021 0.000009 0.090309
547 0.000054 2.321596e-07 0.000021 0.000009 0.090308
548 0.000032 1.392957e-07 0.000021 0.000009 0.090307
549 0.000011 4.643192e-08 0.000021 0.000009 0.090305

550 rows × 5 columns

This object also has some atributes and methods to make calculations and plot data

[4]:
X_max = shower.X_max
x, y, z = shower.track.X_to_xyz(X_max)
print("Depth of maximum (g/cm^2):", X_max)
print("Height of shower maximum (km):", z)
shower.show_light_production();
Depth of maximum (g/cm^2): 345.7529535008104
Height of shower maximum (km): 6.609931587598665
../_images/tutorials_UC0_Playing_around_with_ShowerModel_8_1.png

For example, one may be interested in evaluating the photon density on horizontal planes at different heights

[5]:
for z in [0., 5., 10.]:
    shower.show_distribution(x_c=shower.x0, y_c=shower.y0, z_c=z, size_x=4., size_y=4., N_x=10, N_y=10);
../_images/tutorials_UC0_Playing_around_with_ShowerModel_10_0.png
../_images/tutorials_UC0_Playing_around_with_ShowerModel_10_1.png
../_images/tutorials_UC0_Playing_around_with_ShowerModel_10_2.png

Observatory events

Now, we can generate an observatory consisting in a 500 m radius circular array of 25 IACTs pointing at the shower arrival direction. There exists a function to do that with default telescope parameters

[6]:
observatory = sm.Array25(R=0.5, theta=20., az=45.)
observatory.show();
../_images/tutorials_UC0_Playing_around_with_ShowerModel_13_0.png

Some characteristics of the default telescope

[7]:
print("Angular aperture (deg):", observatory[0].apert)
print("Number of pixels:", observatory[0].N_pix)
print("Collection area (m^2):", observatory[0].area)
print("Position of telescope #8 (km):", (observatory[8].x, observatory[8].y))
Angular aperture (deg): 8.0
Number of pixels: 1800
Collection area (m^2): 113.097
Position of telescope #8 (km): (0.23570226039551584, -0.2357022603955158)

An Event object can be generated

[8]:
event = sm.Event(shower, observatory)

It contains the shower track projection and signal at each telescope

[9]:
event.projections[8]
#event.signals[8]
[9]:
distance alt az theta phi beta time FoV
0 0.485012 11.967679 346.617250 68.268420 16.959319 68.268420 1.015912 False
1 0.598276 30.285485 353.548306 48.856659 16.959319 48.856659 0.679726 False
2 0.756485 41.661785 359.362985 36.553427 16.959319 36.553427 0.493454 False
3 0.937146 48.696291 4.216889 28.734994 16.959319 28.734994 0.382073 False
4 1.129536 53.258711 8.275287 23.507737 16.959319 23.507737 0.309816 False
... ... ... ... ... ... ... ... ...
545 116.839319 69.901311 44.423409 0.220937 16.959319 0.220937 0.000024 True
546 117.053371 69.901493 44.424458 0.220533 16.959319 0.220533 0.000018 True
547 117.267422 69.901675 44.425504 0.220131 16.959319 0.220131 0.000013 True
548 117.481474 69.901856 44.426545 0.219730 16.959319 0.219730 0.000008 True
549 117.695525 69.902036 44.427583 0.219330 16.959319 0.219330 0.000003 True

550 rows × 8 columns

There are methods to visualize the event geometry and time evolution of signals

[10]:
event.show_geometry3D(x_min=-1., x_max=2., y_min=-1., y_max=2.);
event.signals[8].show();
../_images/tutorials_UC0_Playing_around_with_ShowerModel_21_0.png
../_images/tutorials_UC0_Playing_around_with_ShowerModel_21_1.png

Example: Number of telescopes having an integrated signal greater than 100 photoelectrons as a function shower energy

[11]:
energy = np.linspace(50.e3, 50.e5, 10)
trig_tel = np.zeros_like(energy)
for (i, E) in enumerate(energy):
    event = sm.Event(shower.copy(E=E), observatory)
    Npe_sum = np.array([event.signals[tel].Npe_total_sum for tel in range(25)])
    trig_tel[i] = len(Npe_sum[Npe_sum>100])
plt.plot(energy, trig_tel);
plt.xlabel("Energy (MeV)");
plt.ylabel("Number of triggered telescopes");
plt.ylim(0, 25);
../_images/tutorials_UC0_Playing_around_with_ShowerModel_23_0.png

Camera images

Camera images (or videos) are generated assuming a Nishimura-Kamata-Greisen lateral distribution of electrons in the shower. A night sky background of 40 MHz/m\(^2\)/deg:math:^2 is assumed by default, but this parameter can be changed. The integration time (in us) per camera frame can be set too.

[12]:
image = sm.Image(event.signals[12], int_time=0.002)
image.show();
../_images/tutorials_UC0_Playing_around_with_ShowerModel_26_0.png
[13]:
image.animate()
[13]:
../_images/tutorials_UC0_Playing_around_with_ShowerModel_27_1.png

Event object includes a method to show the images of all the telescopes in the same figure

[14]:
event.make_images(NSB=50.)
event.show_images();
../_images/tutorials_UC0_Playing_around_with_ShowerModel_29_0.png
[ ]: