Simulation
The instrument response, from electric field to ADC counts: grand.sim.
Master module for the detector unit simulation GRAND |
|
Processing effective length of antenna |
|
Simulation of Galactic radio noise. |
|
Master module for the ADC in GRAND |
|
Offline, DAQ-style first-level (T1) trigger on ADC traces. |
|
grand.sim.efield2voltage
Master module for the detector unit simulation GRAND
- grand.sim.efield2voltage.get_fastest_size_fft(sig_size, f_samp_mhz, padding_factor=1)[source]
Returns an FFT-friendly transform length and its frequency axis.
Real FFTs are fastest at lengths whose prime factorisation is small, so rather than transforming
sig_sizesamples directly this rounds up to the next such length viascipy.fft.next_fast_len()and returns the matching one-sided frequency axis forscipy.fft.rfft(). Padding to a longer length also improves the frequency resolution of the result, which is whatpadding_factoris for.- Parameters:
sig_size (int) – Length of the time traces, in samples.
f_samp_mhz (ndarray) – Sampling frequency in MHz, e.g. 2000 MHz for a 0.5 ns bin. An array is expected and only its first element is used: the routine assumes every trace in the event shares one time binning.
padding_factor (float, optional) – Factor by which to stretch the traces with zeros before transforming. Must be at least 1; the default of 1 pads only as far as the next fast length.
- Returns:
fast_size (int) – The transform length actually to use,
>= padding_factor*sig_size.freqs_mhz (ndarray) – Frequency axis in MHz, of length
fast_size//2 + 1, matching the output ofscipy.fft.rfft()at that length.
- Raises:
ValueError – If
padding_factoris less than 1.
Examples
import numpy as np from grand.sim.efield2voltage import get_fastest_size_fft n, freqs = get_fastest_size_fft(1000, np.array([2000.0])) print("padded length:", n) # 1000 is already 2^3 x 5^3 print("frequency bins:", freqs.size, "| Nyquist:", freqs[-1], "MHz")
padded length: 1000 frequency bins: 501 | Nyquist: 1000.0 MHz
Doubling the padding halves the bin spacing:
n2, freqs2 = get_fastest_size_fft(1000, np.array([2000.0]), padding_factor=2) print("padded length:", n2) print("bin spacing: %.3f -> %.3f MHz" % (freqs[1], freqs2[1]))
padded length: 2000 bin spacing: 2.000 -> 1.000 MHz
Notes
That only
f_samp_mhz[0]is read is a real limitation, not an oversight in this docstring: an event whose detection units record at different sampling rates would silently get the first unit’s frequency axis applied to all of them. TheToDoin the body marks the same point.
- grand.sim.efield2voltage.PARAM_DEFAULTS = {'add_jitter_ns': 0, 'add_noise': True, 'add_rf_chain': True, 'add_rf_chain_gaa': False, 'add_rf_chain_nut': False, 'calibration_smearing_sigma': 0, 'extend_to_us': 0, 'lst': 18.0, 'resample_to_mhz': 0}
The processing switches of Efield2Voltage.params, with their defaults.
- class grand.sim.efield2voltage.Efield2Voltage(d_input, f_output=None, output_directory=None, seed=None, padding_factor=1.0, du_type='GP300', efield_level=None)[source]
Bases:
objectComputes the voltage at each detection unit from simulated electric fields.
Reads the folder
sim2root.pywrote (efield, run and shower trees), computes the voltage of every event or of one, and writes a voltage file. Seecompute_voltage()for the steps.- params
The processing switches, with these keys and defaults:
add_noise(True)Add Galactic noise.
lst(18.0)Local sidereal time for the Galactic noise, in hours, 0 to 24.
add_rf_chain(True)Apply the GP300 RF chain.
add_rf_chain_nut(False),add_rf_chain_gaa(False)Apply the RF chain up to the LNA output, or the G@Auger chain.
resample_to_mhz(0)Resample to this rate; 0 keeps the input rate. Other rates stay in memory:
save_voltage()refuses them.extend_to_us(0)Extend the traces to this duration, in µs; 0 keeps their length.
calibration_smearing_sigma(0)Relative Gaussian smearing of each unit’s amplitude calibration.
add_jitter_ns(0)Gaussian jitter of the trigger times, in ns.
Unknown keys and invalid values are refused when the computation starts.
- Type:
See also
grand.sim.detector.adc.ADCDigitizes the voltages this class writes.
grand.sim.detector.rf_chain.RFChainThe RF chain applied to the open-circuit voltage.
grand.sim.noise.galaxy.galactic_noiseThe Galactic noise it adds.
grand.sim.detector.antenna_model.AntennaModelThe antenna response it uses.
Examples
Simulate the voltages of the shower that ships with the repository, with the Galactic noise of 6 h local sidereal time:
import tempfile from pathlib import Path import numpy as np import grand from grand import Efield2Voltage from grand.dataio import TVoltage sample = (Path(grand.__file__).parents[1] / "sim2root/Common/sim_Xiaodushan_20221026_000000_RUN1_CD_ZHAireS_0000") out = Path(tempfile.mkdtemp()) sim = Efield2Voltage(str(sample), "voltage.root", output_directory=str(out), seed=1, efield_level=0) sim.params["lst"] = 6.0 sim.compute_voltage() with TVoltage(str(out / "voltage.root")) as tvoltage: tvoltage.get_entry(0) traces = np.asarray(tvoltage.trace) print("%d units, traces of shape %s, in µV" % (traces.shape[0], traces.shape[1:]))
44 units, traces of shape (3, 8192), in µV
- get_event(event_idx=None, event_number=None, run_number=None)[source]
Loads the data of one event, selected by index or by number.
Call this for every new event: it replaces the traces, positions and shower parameters the other methods work from.
- Parameters:
- Raises:
Exception – If neither event_idx nor the
(event_number, run_number)pair identifies an event in the input.
Notes
Either event_idx, or both event_number and run_number, must be given.
- get_leff(du_idx)[source]
Builds the antenna response for one detection unit.
The effective length depends on the direction of the incoming signal in the antenna frame, so it is constructed per unit from that unit’s position and the shower direction.
- Parameters:
du_idx (int) – Index of the detection unit in the event arrays.
- Returns:
The response object for that unit’s three arms.
- Return type:
- get_time_samples()[source]
Define time sample in ns for the duration of the trace t_samples.shape = (nb_du, self.sig_size) t_start_ns.shape = (nb_du,)
- Returns:
Time axis of each unit, in nanoseconds.
- Return type:
ndarray, shape (n_du, n_samples)
- add(addend)[source]
Adds addend to the output voltage spectrum, in place.
Provided so that a caller can inject their own noise instead of, or in addition to, the built-in Galactic model.
- Parameters:
addend (ndarray) – A frequency-domain quantity of exactly the shape of
vout_f,(n_du, 3, n_freqs)– it is not broadcast; another shape raisesValueError. It must already be evaluated onself.freqs_mhz: nothing here interpolates it.
- multiply(multiplier)[source]
Multiplies the output voltage spectrum by multiplier, in place.
Provided so that a caller can apply their own transfer function instead of the built-in RF chain.
- Parameters:
multiplier (ndarray) – A frequency-domain quantity that broadcasts against
vout_f, whose shape is(n_du, 3, n_freqs), already evaluated onself.freqs_mhz.
- final_resample()[source]
Brings the voltage back to the time domain, resampled and cut as requested.
Called last: resamples to
params["resample_to_mhz"]if set (by Fourier interpolation), otherwise inverts the spectrum if noise or an RF chain changed it, then truncates the traces to the target length. The result is inself.vout.
- compute_voc_du(du_idx)[source]
Computes the open-circuit voltage for one detection unit.
This is the base of every voltage computation: the others call it, directly or through
compute_voc_event().- Parameters:
du_idx (int) – Index of the detection unit in the trace arrays.
Notes
Stores the result on the instance rather than returning it:
vocin the time domain andvoc_fin the frequency domain.
- compute_voc_event(event_idx=None, event_number=None, run_number=None)[source]
Computes the open-circuit voltage for every unit in one event.
Fills
vocwith shape(n_du, 3, n_samples)andvoc_fwith shape(n_du, 3, n_freqs).- Parameters:
- Raises:
Exception – If neither event_idx nor the
(event_number, run_number)pair identifies an event in the input.
Notes
Either event_idx, or both event_number and run_number, must be given.
- compute_voltage_du(du_idx)[source]
Computes the output voltage for one detection unit.
Applies, in order:
the open-circuit voltage from the antenna response,
Galactic noise, if
params["add_noise"],the GP300 RF chain, if
params["add_rf_chain"],the RF chain up to the LNA output, if
params["add_rf_chain_nut"],the G@Auger RF chain, if
params["add_rf_chain_gaa"].
- Parameters:
du_idx (int) – Index of the detection unit in the trace arrays.
Notes
Which stages run is taken from
self.params, not from arguments.
- compute_voltage_event(event_idx=None, event_number=None, run_number=None)[source]
Computes the output voltage for every unit in one event.
Equivalent to calling
compute_voltage_du()for each unit in turn, but vectorised over units and therefore much faster.- Parameters:
- Raises:
Exception – If neither event_idx nor the
(event_number, run_number)pair identifies an event in the input.
Notes
Either event_idx, or both event_number and run_number, must be given.
- compute_voltage(event_idx=None, du_idx=None, event_number=None, run_number=None, append_file=True)[source]
Computes voltages for any or all events, and saves them.
The primary entry point. With no arguments it processes every event in the input file.
- Parameters:
event_idx (int, list or ndarray, optional) – Index or indices of events to process.
Noneprocesses all.du_idx (int, list or ndarray, optional) – Detection units to process.
Noneprocesses all of them. May be used for a single event only.event_number (int, list or ndarray, optional) – Event number or numbers, given with run_number.
run_number (int, list or ndarray, optional) – Run number or numbers, given with event_number.
append_file (bool, optional) – Append to the output file rather than replacing it.
Notes
Give either event_idx, or both event_number and run_number, or none of the three. When lists are given, event_number and run_number must be the same length.
The result is written to
self.f_outputas a side effect; the method returns nothing.
grand.sim.detector.antenna_model
- class grand.sim.detector.antenna_model.DataTable(frequency: Number | ndarray, theta: Number | ndarray, phi: Number | ndarray, leff_theta: Number | ndarray = None, phase_theta: Number | ndarray = None, leff_phi: Number | ndarray = None, phase_phi: Number | ndarray = None, leff_phi_reim: Number | ndarray = None, leff_theta_reim: Number | ndarray = None)[source]
Bases:
objectThe tabulated antenna response, on a grid of frequency, azimuth and zenith.
- Parameters:
frequency (Union[Number, np.ndarray]) – Frequencies of the table, in Hz
theta (Union[Number, np.ndarray]) – Zenith angles of the table, in degrees, in the antenna frame
phi (Union[Number, np.ndarray]) – Azimuths of the table, in degrees, in the antenna frame
leff_theta (Union[Number, np.ndarray]) – Modulus of the effective length along e_theta, shape (n_freq, n_phi, n_theta), in m
phase_theta (Union[Number, np.ndarray]) – Phase of the effective length along e_theta, in degrees
leff_phi (Union[Number, np.ndarray]) – Modulus of the effective length along e_phi, shape (n_freq, n_phi, n_theta), in m
phase_phi (Union[Number, np.ndarray]) – Phase of the effective length along e_phi, in degrees
leff_phi_reim (Union[Number, np.ndarray]) – Complex effective length along e_phi (real and imaginary parts), in m
leff_theta_reim (Union[Number, np.ndarray]) – Complex effective length along e_theta (real and imaginary parts), in m
- leff_theta: Number | ndarray = None
Modulus of the effective length along e_theta, shape (n_freq, n_phi, n_theta), in m
- leff_phi: Number | ndarray = None
Modulus of the effective length along e_phi, shape (n_freq, n_phi, n_theta), in m
- grand.sim.detector.antenna_model.tabulated_antenna_model(filename)[source]
Returns the antenna response read from a tabulated file.
- class grand.sim.detector.antenna_model.AntennaModel(du_type='GP300')[source]
Bases:
objectThe effective length of a GRAND antenna, for the three arms.
Loads the tabulated response and provides it on whatever frequency and direction grid the caller needs.
See also
grand.sim.detector.process_ant.AntennaProcessingInterpolates this response for a direction.
grand.sim.efield2voltage.Efield2VoltageUses it to compute the open-circuit voltage.
Examples
The tables of one arm. The frequency axis is in Hz, unlike the rest of the simulation, which works in MHz:
import numpy as np from grand import AntennaModel model = AntennaModel() # 'GP300', the HFSS simulation sn = model.leff_sn # the south-north arm print("frequencies from %.0f to %.0f MHz" % (sn.frequency[0] / 1e6, sn.frequency[-1] / 1e6)) print("table shape (frequency, azimuth, zenith):", np.shape(sn.leff_theta_reim))
frequencies from 30 to 250 MHz table shape (frequency, azimuth, zenith): (221, 361, 91)
grand.sim.detector.process_ant
Processing effective length of antenna
- exception grand.sim.detector.process_ant.MissingFrameError[source]
Bases:
ValueErrorError class
- class grand.sim.detector.process_ant.PreComputeInterpol(idx_first: Any = None, idx_lastp1: Any = None, idx_itp: Any = None, c_inf: Any = None, c_sup: Any = None)[source]
Bases:
objectPrecompute linear interpolation of frequency of Leff
- Parameters:
idx_first (Any) – index of freq in first in band 30-250MHz
idx_lastp1 (Any) – index of freq in last plus one in band 30-250MHz
idx_itp (Any) – array of index where f_out are in f_in for interpolation between idx_itp[i], idx_itp[i]+1 with c_inf and c_sup coefficient
c_inf (Any) – array of coefficient inf
c_sup (Any) – array of coefficient sup c_inf + c_sup = 1
- init_linear_interpol(freq_in_mhz, freq_out_mhz)[source]
Precompute coefficient of linear interpolation for freq_out_mhz with reference defined at freq_in_mhz
- Parameters:
freq_in_mhz (ndarray) – Frequency axis the response is tabulated on, in MHz.
freq_out_mhz (ndarray) – Frequency axis wanted, in MHz.
- class grand.sim.detector.process_ant.AntennaProcessing(model_leff: Any, pos: LTP | GRANDCS)[source]
Bases:
objectProcessing effective length of antenna
- Parameters:
model_leff (Any) – The antenna response of one arm, as
tabulated_antenna_modelreturns itpre_cpt (ClassVar[PreComputeInterpol])
- pre_cpt: ClassVar[PreComputeInterpol]
- classmethod init_interpolation(freq_sampling_mhz, freq_out_mhz)[source]
Precomputes the interpolation from the tabulated grid onto an output axis.
Doing this once and reusing it is what keeps the per-unit voltage computation cheap.
- Parameters:
freq_sampling_mhz (ndarray) – Frequency axis the response is tabulated on, in MHz.
freq_out_mhz (ndarray) – Frequency axis wanted, in MHz.
- set_out_freq_mhz(a_freq)[source]
! typically the return of scipy.fft.rfftfreq/1e6
- Parameters:
a_freq (ndarray) – Output frequency axis, in MHz.
- effective_length(xmax: LTP, efield: ElectricField, frame: LTP | GRANDCS) CartesianRepresentation[source]
Calculation of FFT length effective of antenna for a given Xmax position. Linear interpolation of template of effective length in Fourier space by tripler (theta, phi, frequency) in direction of the efield source at antenna position
- Parameters:
xmax (Coordinates) – Position of shower maximum.
efield (ElectricField) – The incoming field.
- Returns:
Effective length per antenna arm, in the frequency domain.
- Return type:
ndarray, shape (3, n_freq)
- compute_voltage(xmax: LTP, efield: ElectricField, frame: LTP | GRANDCS | None = None) Voltage[source]
- Parameters:
xmax (Coordinates) – Position of shower maximum.
efield (ElectricField) – The incoming field.
frame (LTP or GRANDCS, optional) – Frame the field is expressed in.
- Returns:
Open-circuit voltage at the three arms. Qualified because
grand.aoi.timetrace.Voltageexists too and a bare name is ambiguous.- Return type:
- Raises:
MissingFrameError – If the antenna position or the shower frame is absent or not finite.
grand.sim.detector.rf_chain
- grand.sim.detector.rf_chain.read_config(xml_file)[source]
Reads the XML configuration file and returns component settings.
- grand.sim.detector.rf_chain.xml_file = PosixPath('/home/runner/work/grand/grand/grand/sim/detector/rf_chain_config.xml')
it was parsed at import, so a broken file broke
import grand.sim- Type:
The configuration file, read on first use (#255)
- grand.sim.detector.rf_chain.get_axis_filename(component_name, axis)[source]
Returns the correct filename for a given component and axis.
- Parameters:
- Returns:
Path to that component measurements for that arm.
- Return type:
- Raises:
KeyError – If the component is not in the configuration file.
ValueError – If the component is disabled there, or the axis is not one of the above.
FileNotFoundError – If the configuration gives no file for the component.
- grand.sim.detector.rf_chain.interp(x, y, z)[source]
Returns z interpolated onto x from samples at y.
A thin wrapper over
numpy.interp(), kept so that the interpolation used across this module can be changed in one place.- Parameters:
x (ndarray) – Positions to interpolate onto.
y (ndarray)
positions. (Sample)
z (ndarray)
values. (Sample)
- Returns:
ndarray
Interpolated values, of the shape of x.
- grand.sim.detector.rf_chain.interpol_at_new_x(a_x, a_y, new_x)[source]
Returns a_y interpolated onto new_x, zero outside the input range.
- Parameters:
a_x (ndarray, shape (n,)) – Sample positions, in increasing order.
a_y (ndarray, shape (n,)) – Values at a_x.
new_x (ndarray, shape (m,)) – Positions to interpolate onto.
- Returns:
Interpolated values. Cubic within the range of a_x, and zero outside it rather than extrapolated – the S-parameter tables are measured over 30-250 MHz and have no meaning beyond it.
- Return type:
ndarray, shape (m,)
- grand.sim.detector.rf_chain.db2reim(dB, phase)[source]
Returns the real and imaginary parts of a quantity given in decibels.
A magnitude in decibels and a phase describe a complex number in polar form. This converts that pair to Cartesian form, using the voltage convention \(|z| = 10^{dB/20}\) rather than the power convention \(10^{dB/10}\), because the S-parameters this module reads are measured as voltage ratios by a vector network analyzer.
- Parameters:
dB (array_like) – Magnitude in decibels.
phase (array_like) – Phase in radians. Note the unit: the tabulated S-parameter files store degrees, so callers convert with
numpy.deg2rad()before calling this.
- Returns:
re (ndarray) – Real part.
im (ndarray) – Imaginary part.
Examples
A 0 dB magnitude is unit modulus, so a quarter-turn of phase lands on the imaginary axis:
import numpy as np from grand.sim.detector.rf_chain import db2reim re, im = db2reim(np.array([0.0, -20.0]), np.array([0.0, np.pi/2])) print("re =", np.round(re, 6)) print("im =", np.round(im, 6))
re = [1. 0.] im = [0. 0.1]
The second entry shows the voltage convention: -20 dB is a factor of ten in amplitude, not a factor of a hundred.
- grand.sim.detector.rf_chain.s2abcd(s11, s21, s12, s22)[source]
Returns the normalized ABCD matrix of a two-port from its S-parameters.
Scattering parameters are what a vector network analyzer measures, but they do not cascade: the S-matrix of two networks in series is not the product of their S-matrices. The ABCD (transmission) representation does cascade, which is the whole reason for this conversion — it is what lets
RFChainobtain the response of the complete chain by multiplying the matrices of the LNA, the baluns, the cable and the VGA-plus-filter in order.The normalized form returned here assumes equal reference impedances at both ports, which holds for the 50 \(\Omega\) measurements this module reads.
- Parameters:
s11 (array_like) – The four complex scattering parameters, each of shape
(n_freq,). Obtain them from tabulated magnitude and phase withdb2reim().s21 (array_like) – The four complex scattering parameters, each of shape
(n_freq,). Obtain them from tabulated magnitude and phase withdb2reim().s12 (array_like) – The four complex scattering parameters, each of shape
(n_freq,). Obtain them from tabulated magnitude and phase withdb2reim().s22 (array_like) – The four complex scattering parameters, each of shape
(n_freq,). Obtain them from tabulated magnitude and phase withdb2reim().
- Returns:
The ABCD matrix, shape
(2, 2, n_freq), whose entries are\[ \begin{align}\begin{aligned}A = \frac{(1+S_{11})(1-S_{22}) + S_{12}S_{21}}{2S_{21}},\quad B = \frac{(1+S_{11})(1+S_{22}) - S_{12}S_{21}}{2S_{21}},\\C = \frac{(1-S_{11})(1-S_{22}) - S_{12}S_{21}}{2S_{21}},\quad D = \frac{(1-S_{11})(1+S_{22}) + S_{12}S_{21}}{2S_{21}}.\end{aligned}\end{align} \]- Return type:
ndarray
Notes
A matched, lossless through-line has \(S_{11}=S_{22}=0\) and \(S_{12}=S_{21}=1\), for which the ABCD matrix is the identity — the check in the example below, and a useful invariant for any test of this function.
Examples
import numpy as np from grand.sim.detector.rf_chain import s2abcd one = np.ones(3, dtype=complex) zero = np.zeros(3, dtype=complex) abcd = s2abcd(zero, one, one, zero) # ideal through-line print("shape:", abcd.shape) print("identity at every frequency:", np.allclose(abcd, np.eye(2)[:, :, None]))
shape: (2, 2, 3) identity at every frequency: True
See also
matmulcascades two ABCD matrices.
RFChain.compute_for_freqsbuilds the full chain from its stages.
References
Section 8.3 of arXiv:2408.10926, and the Xidian University RF-chain note cited in the module docstring.
- grand.sim.detector.rf_chain.matmul(A, B)[source]
Returns the product of two stacked 2x2 matrices.
Multiplies elementwise over the trailing axes, which is what cascades two stages of the RF chain: an ABCD matrix per port and per frequency.
- Parameters:
A (ndarray, shape (2, 2, n_ports, n_freqs)) – The matrices to multiply.
B (ndarray, shape (2, 2, n_ports, n_freqs)) – The matrices to multiply.
- Returns:
The product, computed as
\[\begin{split}AB = \begin{bmatrix} A_{11}B_{11} + A_{12}B_{21} & A_{11}B_{12} + A_{12}B_{22} \\ A_{21}B_{11} + A_{22}B_{21} & A_{21}B_{12} + A_{22}B_{22} \end{bmatrix}\end{split}\]- Return type:
ndarray, shape (2, 2, n_ports, n_freqs)
See also
s2abcdproduces the matrices this cascades.
Examples
Cascading a stage with the identity leaves it unchanged, which is the property that makes the ABCD representation worth converting to.
import numpy as np from grand.sim.detector.rf_chain import matmul n = 4 identity = np.zeros((2, 2, n), dtype=complex) identity[0, 0] = identity[1, 1] = 1.0 rng = np.random.default_rng(0) stage = rng.normal(size=(2, 2, n)) + 1j * rng.normal(size=(2, 2, n)) print("cascading with the identity is a no-op:", np.allclose(matmul(stage, identity), stage))
cascading with the identity is a no-op: True
- class grand.sim.detector.rf_chain.GenericProcessingDU[source]
Bases:
objectDefine common attribut for frequencies for all DU effects processing
- set_out_freq_mhz(freqs_mhz)[source]
Define frequencies
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Frequency axis the stage’s response is wanted on, in MHz.
- release_arrays()[source]
Frees the arrays computed for the current frequencies.
Every array whose last axis runs over the frequencies is dropped, in this stage and in the stages it holds; the measured input tables are kept. A chain over a million-sample trace holds about 1 GB of them, while a caller such as
grand.sim.efield2voltage.Efield2Voltageneeds only the transfer function. Callcompute_for_freqs()again before using the stage.
- class grand.sim.detector.rf_chain.MatchingNetwork[source]
Bases:
GenericProcessingDUThe impedance matching network between antenna and LNA.
- compute_for_freqs(freqs_mhz)[source]
Computes this stage’s response on the given frequency axis.
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz. The tabulated S-parameters are interpolated onto it, and are zero outside the 30-250 MHz band they were measured over.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.gaa_frontend0db[source]
Bases:
GenericProcessingDUThe GAA front end at 0 dB gain.
- compute_for_freqs(freqs_mhz)[source]
Computes this stage’s response on the given frequency axis.
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz. The tabulated S-parameters are interpolated onto it, and are zero outside the 30-250 MHz band they were measured over.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.LowNoiseAmplifier[source]
Bases:
GenericProcessingDU- Class goals:
Perform the LNA filter on signal for each antenna
read only once LNA data files
pre_compute interpolation
- class grand.sim.detector.rf_chain.BalunAfterLNA[source]
Bases:
GenericProcessingDU- Class goals:
deals with Balun after LNA (inside Nut).
Note that balun is placed after LNA in version 1. The same type of balun is placed before matching-network and LNA in version 2.
Balun is used in X and Y ports only. No Balun in Z port.
Balun without matching network is used in version 1.
- class grand.sim.detector.rf_chain.Cable[source]
Bases:
GenericProcessingDU- Class goals:
pre_compute interpolation
- class grand.sim.detector.rf_chain.VGAFilter(gain=0)[source]
Bases:
GenericProcessingDU- Class goals:
pre_compute interpolation
- class grand.sim.detector.rf_chain.BalunBeforeADC[source]
Bases:
GenericProcessingDUClass goals: * Pass signal through Balun before Analog to Digitial Converter (ADC) for each antenna * Balun is used in x, y, and z ports * Same data is used for all three ports * read data files only once * pre_compute interpolation * this Balun is referred to as Balun1 Balun2
- compute_for_freqs(freqs_mhz)[source]
compute s-parameters and ABCD matrix of Balun before AD chip for freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.Rfchain_elements_db(filename='test2.s2p')[source]
Bases:
GenericProcessingDUA chain element whose response is tabulated in decibels.
- compute_for_freqs(freqs_mhz)[source]
Computes this stage’s response on the given frequency axis.
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz. The tabulated S-parameters are interpolated onto it, and are zero outside the 30-250 MHz band they were measured over.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.Rfchain_elements_db_rad(filename='test2.s2p')[source]
Bases:
GenericProcessingDUA chain element tabulated in decibels, with phase in radians.
- compute_for_freqs(freqs_mhz)[source]
Computes this stage’s response on the given frequency axis.
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz. The tabulated S-parameters are interpolated onto it, and are zero outside the 30-250 MHz band they were measured over.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.Rfchain_elements(filename='test.s2p')[source]
Bases:
GenericProcessingDUA chain element whose response is tabulated in linear units.
- compute_for_freqs(freqs_mhz)[source]
compute s-parameters and ABCD matrix of Balun before AD chip for freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.Rfchain_elements_rad(filename='test.s2p')[source]
Bases:
GenericProcessingDUA chain element tabulated linearly, with phase in radians.
- compute_for_freqs(freqs_mhz)[source]
compute s-parameters and ABCD matrix of Balun before AD chip for freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.Zload_arb(filename='S_balun_AD.s1p')[source]
Bases:
GenericProcessingDUAn arbitrary load impedance read from a measurement file.
- compute_for_freqs(freqs_mhz)[source]
Computes this stage’s response on the given frequency axis.
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz. The tabulated S-parameters are interpolated onto it, and are zero outside the 30-250 MHz band they were measured over.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- class grand.sim.detector.rf_chain.Zload[source]
Bases:
GenericProcessingDUClass goals: * computes input impedance of load due to balun + 200ohm ADC.
- class grand.sim.detector.rf_chain.RFChain(vga_gain=20)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
See also
grand.sim.efield2voltage.Efield2VoltageApplies this chain to simulated voltages.
grand.sim.detector.rf_chain.s2abcdConverts measured S-parameters to the cascadable form.
Examples
The magnitude of the transfer function, from the open-circuit voltage to the ADC input, for the three arms, at frequencies in MHz:
import numpy as np from grand.sim.detector.rf_chain import RFChain chain = RFChain() chain.compute_for_freqs(np.array([50.0, 100.0, 150.0, 200.0])) print(np.round(np.abs(chain.get_tf()), 1)) # (arms, frequencies)
[[46.3 47. 28.5 17. ] [45.7 42.6 26.8 22.7] [17.9 35. 35.2 22.5]]
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
Examples
The chain is a net amplifier across the band at 20 dB; Fig. 8 of arXiv:2408.10926 shows this curve.
import numpy as np from grand.sim.detector.rf_chain import RFChain freqs = np.arange(30.0, 251.0) chain = RFChain(vga_gain=20) chain.compute_for_freqs(freqs) gain = np.abs(chain.get_tf()) print("peak |V_out/V_oc| per arm:", np.round(gain.max(axis=1), 1)) print("at 100 MHz :", np.round(gain[:, 70], 1))
peak |V_out/V_oc| per arm: [94.8 93.5 69. ] at 100 MHz : [47. 42.6 35. ]
- class grand.sim.detector.rf_chain.RFChainNut(vga_gain=20)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
- class grand.sim.detector.rf_chain.RFChain_gaa(vga_gain=0)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
- class grand.sim.detector.rf_chain.RFChain_Balun1(vga_gain=20)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
- class grand.sim.detector.rf_chain.RFChain_Match_net(vga_gain=20)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
- class grand.sim.detector.rf_chain.RFChain_Cable_Connectors(vga_gain=20)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
- class grand.sim.detector.rf_chain.RFChain_VGA(vga_gain=20)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
- class grand.sim.detector.rf_chain.RFChain_in_Balun1(vga_gain=20)[source]
Bases:
GenericProcessingDUFacade for all elements in RF chain
- compute_for_freqs(freqs_mhz)[source]
Compute transfer function for frequency freqs_mhz
- Parameters:
freqs_mhz (ndarray, shape (n_freq,)) – Output frequency axis, in MHz.
- Returns:
Results are stored on the instance; the chain reads them when it cascades the stages.
- Return type:
None
- vout_f(voc_f)[source]
Compute final voltage after propagating signal through RF chain. Input: Voc_f (in frequency domain) Output: Voltage after RF chain in frequency domain. Make sure to run self.compute_for_freqs() before calling this method. RK Note: name ‘vout_f’ is a placeholder. Change it with something better.
- Parameters:
voc_f (ndarray, shape (3, n_freq)) – Open-circuit voltage spectrum at the antenna feed point.
- Returns:
Voltage spectrum after the chain, for one unit’s three arms.
- Return type:
ndarray, shape (3, n_freq)
- get_tf()[source]
Return transfer function for all elements in RF chain total transfer function is the output voltage for input Voc of 1. It says by what factor the Voc will be multiplied by the RF chain. @return total TF (complex, (3,N)):
- Returns:
The complex transfer function per antenna arm, \(V_{\rm out}/V_{\rm oc}\).
- Return type:
ndarray, shape (3, n_freq)
grand.sim.noise.galaxy
Simulation of Galactic radio noise.
- grand.sim.noise.galaxy.interpol_at_new_x(a_x, a_y, new_x)[source]
Interpolate a discrete function and return zero outside its definition range.
- Parameters:
a_x (ndarray) – Sample positions.
a_y (ndarray) – Sample values.
new_x (ndarray) – Positions to interpolate onto.
- Returns:
Cubic interpolation, zero outside the range of a_x.
- Return type:
ndarray
- grand.sim.noise.galaxy.galactic_noise(f_lst, size_out, freqs_mhz, nb_ant, seed=None, du_type='GP300')[source]
Generate Galactic-noise voltage spectra for detector units.
The precomputed Galactic-noise tables contain available-power spectral density
P_Lin W/Hz for 30–250 MHz, 72 local-sidereal-time bins (20-minute spacing), and the three antenna ports X, Y, Z. The requested LST is mapped to the nearest available 20-minute bin.For each port, the open-circuit RMS voltage in a 1 MHz native table bin is reconstructed from
V_oc,RMS**2 = 4 * P_L * Re(Z_ant).The RMS voltage is then scaled to the requested uniform FFT-bin width, interpolated onto
freqs_mhz, and converted to complex rFFT coefficients using the normalization appropriate for SciPy/NumPy’s backward-normalizedirfft.- Parameters:
f_lst (float) – Local sidereal time in hours. Must satisfy
0 <= f_lst < 24.size_out (int) – Length, in samples, of the time trace the spectrum belongs to. It sets the normalization, so it must be the length the spectrum will be inverted to, whatever part of the frequency axis freqs_mhz covers.
freqs_mhz (ndarray, shape (nb_freq,)) – Uniformly spaced output-frequency grid, in MHz: the full rFFT axis of size_out samples (
size_out // 2 + 1points), or a band of it. It is not checked against size_out.nb_ant (int) – Number of detector units for which independent noise is generated.
seed (int or None, optional) – Random-number-generator seed. If None, a non-reproducible realization is generated; an integer seed gives reproducible output.
du_type (str, optional) – Antenna model used for the Galactic-noise table:
"GP300"(HFSS),"GP300_nec"(NEC), or"GP300_mat"(MATLAB).
- Returns:
Complex rFFT coefficients of the Galactic noise, for all detector units and antenna arms, in microvolts.
- Return type:
ndarray, complex, shape (nb_ant, 3, nb_freq)
- Raises:
ValueError – If f_lst is not finite or lies outside
[0, 24), if du_type is not one of the three tabulated models, or if freqs_mhz is not uniformly spaced and increasing.
See also
grand.sim.efield2voltage.Efield2VoltageAdds this noise to simulated voltages.
grand.sim.detector.rf_chain.RFChainThe chain the noise passes through.
Examples
One realization of the noise spectrum for two units at 18 h local sidereal time, on a 1 MHz grid from 30 to 250 MHz:
import numpy as np from grand.sim.noise.galaxy import galactic_noise freqs_mhz = np.arange(30.0, 251.0) spectrum = galactic_noise(18.0, 2048, freqs_mhz, nb_ant=2, seed=1) print(spectrum.shape) # (units, arms, frequencies) print("|V| at 30, 100 and 250 MHz on unit 0, arm X:", np.round(np.abs(spectrum[0, 0, [0, 70, 220]]), 1), "µV")
(2, 3, 221) |V| at 30, 100 and 250 MHz on unit 0, arm X: [ 793.4 3713.6 364.2] µV
grand.sim.detector.adc
Master module for the ADC in GRAND
- class grand.sim.detector.adc.ADC[source]
Bases:
objectClass that represents the analog-to-digital converter (ADC) of GRAND. The ADC digitizes an analog voltage that has been processed through the entire RF chain. For GRAND, the ADC has: - a sampling rate of 500 MHz - 14 bits centered around 0 V <-> 0 ADC counts, with 13 positive and 13 negative bits - a saturation at an input voltage of +/- 0.9 V
See also
grand.sim.efield2voltage.Efield2VoltageComputes the voltages the ADC digitizes.
grand.sim.detector.trigger.t1_du_triggersThe T1 trigger on the digitized traces.
- downsample(voltage_trace, input_sampling_rate_mhz)[source]
downsamples the voltage trace to the target sampling rate
- Parameters:
voltage_trace (np.ndarray[double]) – Array of voltage traces, with shape (N_du,3,N_samples), in µV.
input_sampling_rate_mhz (float) – Sampling rate of voltage_trace, in MHz.
- Returns:
downsampled_voltage_trace – Array of downsampled voltage traces, with shape (N_du,3,N_samples), in µV.
- Return type:
np.ndarray[double]
Examples
Simulated voltages are sampled at 2 GHz; the ADC samples at 500 MHz:
import numpy as np from grand import ADC voltage = np.zeros((1, 3, 8192)) # one unit, 4.096 µs at 2 GHz print(ADC().downsample(voltage, 2000.0).shape)
(1, 3, 2048)
- process(voltage_trace, noise_trace=None)[source]
Processes an analog voltage trace to a digital ADC trace, with an option to add measured noise
- Parameters:
- Returns:
adc_trace – Array of ADC traces with shape (N_du,3,N_samples), in ADC counts.
- Return type:
np.ndarray[int]
Examples
A signal below one count does not come out small, it comes out absent. A simulation whose voltages land under the step yields an all-zero trace, which reads as a quiet event rather than a scaling error.
import numpy as np from grand.sim.detector.adc import ADC adc = ADC() lsb = adc.max_voltage / adc.max_bit_value print("one count is %.1f uV" % lsb) t = np.arange(512) * 0.5 pulse = np.exp(-((t - 100.0) ** 2) / (2 * 5.0 ** 2)) for amplitude in (lsb / 10, lsb * 100): trace = np.stack([np.stack([pulse * amplitude] * 3)]) print("%9.1f uV -> %4d counts" % (amplitude, int(np.abs(np.asarray(adc.process(trace))).max())))
one count is 109.9 uV 11.0 uV -> 0 counts 10986.3 uV -> 100 counts
grand.sim.detector.trigger
Offline, DAQ-style first-level (T1) trigger on ADC traces.
The algorithm and default parameters are those of
scripts/T1_trigger_offline.py; scripts/convert_voltage2adc.py
--t1_trigger applies them.
Warning
The default parameters (DEFAULT_T1_CONFIG) are copied verbatim
from the offline script. They have not been confirmed by the trigger
group as the ones the DAQ uses. See extract_trigger_parameters()
for the points that need confirming.
The trigger, per channel
All times are in ns and converted to samples assuming 2 ns per sample (500 MHz, the GRAND ADC):
T1 crossing. The first sample strictly above
th1. It needs at leastt_quiet/2samples before it in the trace, all at or belowth1(the quiet time). A first crossing earlier than that is rejected.Window. The
t_period/2samples starting at the T1 crossing.T2 crossings. In the window, every upward crossing of
+th2(a sample aboveth2whose predecessor is not); the T1 crossing counts as the first one. Negative crossings of-th2are not counted.Separation. Each T2 crossing must follow the previous one by less than
t_sepmaxns; one that does not rejects the channel.Count. The number of crossings
NCmust be within[nc_min, nc_max].
Q (peak / NC) is computed and returned, but, as in the offline script,
q_min and q_max are not applied.
The trigger, per detector unit
A DU passes T1 if any of the channels in channels (by default 0, 1, 2,
the X, Y and Z ADC channels) passes. t1_du_triggers() evaluates every
DU of an event.
- grand.sim.detector.trigger.DEFAULT_T1_CONFIG = {'nc_max': 8, 'nc_min': 2, 'q_max': 255, 'q_min': 0, 'sepmax_ends_count': 0, 'sepmax_inclusive': 0, 't_overlap': 64, 't_period': 512, 't_posttrig': 1024, 't_pretrig': 960, 't_quiet': 512, 't_sepmax': 10, 'th1': 100, 'th2': 50}
The trigger parameters of
scripts/T1_trigger_offline.py, unchanged. Times are in ns, thresholds in ADC counts.t_pretrig,t_overlapandt_posttrigconfigure the DAQ readout window and are not used by the trigger itself. To be confirmed by the trigger group.
- grand.sim.detector.trigger.DEFAULT_T1_CHANNELS = (0, 1, 2)
The ADC channels the offline script evaluates, (0, 1, 2) = (X, Y, Z).
- grand.sim.detector.trigger.T1_TRIGGER_FLAG = 1
Value written in
trigger_flagfor a DU that passes T1; 0 otherwise.trigger_flagis documented as “same as event_type”, where 0x1000 is the 10 s trigger, 0x8000 the random trigger and anything else a shower trigger; the encoding of a T1 shower trigger is to be confirmed by the trigger group.
- grand.sim.detector.trigger.extract_trigger_parameters(trace, trigger_config=None, baseline=0)[source]
Extracts the T1 trigger information from one ADC channel.
The algorithm of
scripts/T1_trigger_offline.py, unchanged; see the module docstring for a description.- Parameters:
trace (array_like) – One channel’s trace, in ADC counts, sampled at 500 MHz (2 ns).
trigger_config (dict, optional) – Trigger parameters overriding
DEFAULT_T1_CONFIG.baseline (float, optional) – Subtracted from the peak when computing
Q. The thresholds are applied to the raw trace, so it must already be centered on 0.
- Returns:
index_T1_crossing(int),index_T2_crossing(array of int, indices in trace, the T1 crossing first),NC(int) andQ.- Return type:
- Raises:
ValueError – If the channel does not trigger: no T1 crossing, not enough samples before it for the quiet time, or two T2 crossings more than
t_sepmaxapart.
Notes
The default parameters are the offline script’s. The trigger group must confirm them, and these points of the algorithm, before the result is taken as the DAQ’s:
the thresholds
th1 = 100andth2 = 50ADC counts, and that they apply to the positive polarity only (-th2crossings are not counted, and a negative-only pulse never triggers);t_sepmax = 10ns with a strict<: at 2 ns per sample, T2 crossings must be at most 8 ns apart, so an oscillation of period 10 ns or longer (below 100 MHz) never gives NC >= 2;that a crossing too far apart rejects the channel, rather than ending the count;
the quiet time: only the first crossing is tried, and it is rejected if it lies within the first
t_quiet/2samples;q_min/q_max, which are not applied;the channels used (0, 1, 2) and the “any channel” DU logic.
- grand.sim.detector.trigger.t1_channel_trigger(trace, trigger_config=None, baseline=0)[source]
Whether one ADC channel passes T1.
It passes if
extract_trigger_parameters()finds a T1 crossing and the number of crossings is within[nc_min, nc_max]. The parameters are to be confirmed by the trigger group (see that function).- Return type:
- grand.sim.detector.trigger.t1_du_triggers(traces, trigger_config=None, channels=(0, 1, 2), baseline=0)[source]
Evaluates T1 on every detector unit of an event.
- Parameters:
traces (array_like) – ADC traces with shape (N_du, N_channels, N_samples), e.g.
TADC.trace_chor the output ofgrand.ADC.process().trigger_config (dict, optional) – Trigger parameters overriding
DEFAULT_T1_CONFIG. The defaults are the offline script’s and are to be confirmed by the trigger group.channels (sequence of int, optional) – The channels evaluated; a DU triggers if any of them does.
baseline (float, optional) – See
extract_trigger_parameters().
- Returns:
One value per DU.
- Return type:
See also
grand.sim.detector.trigger.t1_channel_triggerT1 on one channel.
grand.sim.detector.trigger.extract_trigger_parametersThe quantities T1 decides on.
grand.sim.detector.adc.ADC.processProduces the ADC traces T1 reads.
Examples
Which units of a digitized event pass T1 with the default parameters:
from pathlib import Path import numpy as np import grand from grand.dataio import TADC from grand.sim.detector.trigger import t1_du_triggers sample = (Path(grand.__file__).parents[1] / "sim2root/Common/sim_Xiaodushan_20221026_000000_RUN1_CD_ZHAireS_0000") with TADC(str(sample / "adc_1618-13790_L1_0000.root")) as tadc: tadc.get_entry(0) counts = np.asarray(tadc.trace_ch) du_id = np.asarray(tadc.du_id) passed = t1_du_triggers(counts) print("units passing T1:", du_id[passed])
units passing T1: [229 245]
- grand.sim.detector.trigger.t1_trigger_flags(traces, trigger_config=None, channels=(0, 1, 2), baseline=0)[source]
The
trigger_flagvalues for every DU of an event.T1_TRIGGER_FLAGfor a DU that passest1_du_triggers(), 0 otherwise. The encoding is to be confirmed by the trigger group.- Return type:
grand.sim.shower.gen_shower
- class grand.sim.shower.gen_shower.ShowerEvent(energy: float | None = None, zenith: float | None = None, azimuth: float | None = None, primary: ParticleCode | None = None, frame: GRANDCS | LTP | None = None, core: CartesianRepresentation | None = None, geomagnet: CartesianRepresentation | None = None, maximum: CartesianRepresentation | None = None, origin_geoid: list | np.ndarray | None = None, fields: OrderedDict | None = None)[source]
Bases:
CheckedFieldsThe parameters of one air shower.
Direction, energy, core position, depth of maximum and primary type, together with the frame they are expressed in.
- Parameters:
energy (Optional[float]) – Energy of the primary, in GeV (
TShower.energy_primary)zenith (Optional[float]) – Zenith angle of the direction the shower comes from, in degrees
azimuth (Optional[float]) – Azimuth of the direction the shower comes from, in degrees, from north toward west
primary (Optional[ParticleCode]) – Primary particle type
frame (Optional[Union[GRANDCS, LTP]]) – The shower frame: NWU, centered on the core, magnetic north
core (Optional[CartesianRepresentation]) – Shower core, in m, in GRANDCS
geomagnet (Optional[CartesianRepresentation]) – Geomagnetic field vector, if set;
load_rootdoes not set itmaximum (Optional[CartesianRepresentation]) – Position of Xmax, in m, in frame
origin_geoid (Optional[list, np.ndarray]) – Origin of GRANDCS as [latitude (deg), longitude (deg), height (m)]; set it before
load_rootfields (Optional[OrderedDict]) – Electric fields by antenna, if set;
load_rootdoes not set it
- azimuth: float | None = None
Azimuth of the direction the shower comes from, in degrees, from north toward west
- primary: ParticleCode | None = None
Primary particle type
- frame: GRANDCS | LTP | None = None
NWU, centered on the core, magnetic north
- Type:
The shower frame
- core: CartesianRepresentation | None = None
Shower core, in m, in GRANDCS
- geomagnet: CartesianRepresentation | None = None
Geomagnetic field vector, if set;
load_rootdoes not set it
- maximum: CartesianRepresentation | None = None
Position of Xmax, in m, in frame
- origin_geoid: list | np.ndarray | None = None
Origin of GRANDCS as [latitude (deg), longitude (deg), height (m)]; set it before
load_root
- load_root(d_shower)[source]
Returns a shower built from a ROOT shower tree.
- Parameters:
d_shower (TShower or TShowerSim) – The tree to read.
- Returns:
The shower it describes.
- Return type:
grand.sim.shower.pdg
- class grand.sim.shower.pdg.ParticleCode(*values)[source]
Bases:
IntEnumPDG Monte Carlo particle numbering scheme
Ref: http://pdg.lbl.gov/2007/reviews/montecarlorpp.pdf
- GAMMA = 22
- Z_0 = 23
- W_PLUS = 24
- W_MINUS = -24
- ELECTRON = 11
- ANTI_ELECTRON = -11
- NEUTRINO_E = 12
- ANTI_NEUTRINO_E = -12
- MUON = 13
- ANTI_MUON = -13
- NEUTRINO_MU = 14
- ANTI_NEUTRINO_MU = -14
- TAU = 15
- ANTI_TAU = -15
- NEUTRINO_TAU = 16
- ANTI_NEUTRINO_TAU = -16
- PION_0 = 111
- PION_PLUS = 211
- PION_MINUS = -211
- PROTON = 2212
- NEUTRON = 2112
- IRON = 1000260560