The simulation chain
This page describes how GRANDlib turns the electric field computed by an air-shower code into the digitized voltage a detection unit records: the physics of each stage and how it is implemented. To run the chain, see the Quick start guide and Command-line tools.
Click the figure to open it full size.
The stages
ZHAireS / CoREAS external: shower and radio emission
|
v
sim2root simulator output -> GRANDROOT trees
|
v
E-field --> V_oc antenna effective length
--> + noise Galactic background
--> RF chain LNA, baluns, cable, VGA, filter
--> ADC digitization
|
v
TVoltage / TADC trees
Open-circuit voltage
The response of the antenna to an incoming field is its effective length \(\boldsymbol{\ell}\), a vector that depends on direction and frequency. The open-circuit voltage at one arm is its projection onto the field:
with \(p\) running over the three arms. The computation is done in the
frequency domain over 30–250 MHz. grand.sim.detector.process_ant
interpolates the tabulated response in frequency, azimuth and zenith.
The traces are transformed with a length rounded up to the next size with
small prime factors (get_fastest_size_fft()),
which keeps the transforms fast. The frequency axis is taken from the
sampling rate of the first detection unit, so all units of an event must be
sampled at the same rate.
The dot product is basis-independent, but the table is not: what
AntennaModel stores is
\(\ell_\theta\) and \(\ell_\phi\) in the spherical basis of the
arrival direction,
so \(\hat\theta\) and \(\hat\phi\) are set by where the shower came
from. process_ant rotates the field into that basis before contracting.
Warning
The index \(p\) is the antenna arm, not a component of the
field. trace[:, 2] is the Z arm; it is not \(E_z\). Because the
basis follows the arrival direction, which arm sees a given field depends
on the geometry. The ratio between arms is not the ratio between field
components.
Click the figure to open it full size.
In notebook 06, a field with components in the ratio 1.0 : 0.6 : 0.2 produces arm amplitudes of about 600 : 400 : 1, although the Z arm has the largest \(|\ell_\theta|\) of the three at that zenith angle. An analysis that treats the arms as Cartesian components is wrong by an amount that depends on the direction.
Galactic noise
The noise model starts from the sky brightness temperature of LFMap, folded through the antenna response and tabulated over frequency and local sidereal time as an available power spectral density (Data files). Each detection unit receives an independent realization, with random amplitudes and phases. Spatial coherence between units is not modeled; it is expected to be small, given the spacing of the array.
import numpy as np
from grand.sim.noise.galaxy import galactic_noise
freqs = np.arange(30.0, 251.0)
v = galactic_noise(18.0, 1024, freqs, nb_ant=4, seed=0)
print("shape (units, arms, frequencies):", v.shape)
shape (units, arms, frequencies): (4, 3, 221)
The simulated level agrees with the level rebuilt independently from the
tables and the antenna impedance, within the sampling spread of about 1%
(tests/sim/test_galactic_noise_normalisation.py). Notebook 05 follows the
calculation step by step.
The RF chain
The RF chain is a cascade of two-port networks: matching network, low-noise
amplifier, balun, cable and connector, filter board, a second balun and the
ADC input. Each is described by measured scattering parameters. S-matrices
do not cascade (the S-matrix of two networks in series is not the product of
theirs), so s2abcd() converts each to the
transmission (ABCD) representation, in which the cascade is a matrix product.
A matched, lossless line gives the identity matrix.
Click the figure to open it full size.
The numbered circles give the order of the matrix product, which differs
from the signal-flow order of the boxes: the first factor is the class named
BalunAfterLNA, applied before the LNA. The vgaf stage loads
feb+amfitler+biast.s2p, a front-end board, not a variable-gain amplifier
(The VGA gain setting has no effect, Data files).
import numpy as np
from grand.sim.detector.rf_chain import RFChain
chain = RFChain(vga_gain=20)
chain.compute_for_freqs(np.arange(30.0, 251.0))
tf = np.abs(chain.get_tf())
print("transfer function shape (arms, frequencies):", tf.shape)
print("peak |V_out/V_oc| per arm:", np.round(tf.max(axis=1), 1))
transfer function shape (arms, frequencies): (3, 221)
peak |V_out/V_oc| per arm: [94.8 93.5 69. ]
S-parameters are shipped for VGA gains of 20 dB (the GRANDProto300
default), 5 dB and 0 dB, but the vga_gain argument currently has no
effect: every chain uses the same filter board
(The VGA gain setting has no effect).
Digitization
ADC.downsample resamples
the voltage to the 500 MHz of the ADC and
ADC.process quantizes it to 14
bits (one count is 109.9 µV) and saturates it at ±0.9 V, producing the counts
a TADC tree holds. Measured noise, in ADC counts,
can be added before saturation. An offline version of the T1 trigger,
grand.sim.detector.trigger.t1_du_triggers(), then flags the units that
would have triggered; its parameters are still to be confirmed by the trigger
group (The offline T1 trigger passes few units on clean simulations).
Performance
A shower across the full GRANDProto300 array takes about 13 s on one core, measured over 300 ZHAireS showers for the GRANDlib paper. The 44-antenna shower of the Quick start guide takes about 10 s, including loading the antenna and RF-chain models.
What is not modeled
Anthropogenic and other radio-frequency interference. Only the Galactic background is simulated; measured noise traces can be added at the ADC step where a realistic background is needed.
Spatial coherence of the Galactic noise between detection units.
The air shower and its radio emission, which come from ZHAireS or CoREAS (Converting simulations: sim2root).
Reconstruction, which goes from recorded times and amplitudes back to the
shower, is in grand.analysis; notebook 11 works through it.