Code architecture

This page describes how the source is organized. See Coordinates and conventions and The data model for what the pieces mean physically.

What GRANDlib is

GRANDlib computes little of the physics itself. Air showers and their radio emission come from ZHAireS or CoREAS, tau propagation from DANTON, terrain from TURTLE, the geomagnetic field from GULL and sky brightness from LFMap. What GRANDlib owns is three things:

  1. A schema — what a GRAND event is and the format the collaboration stores it in (grand.dataio).

  2. A frame-reconciliation engine — where things are, in which frame, over what terrain, in what magnetic field (grand.geo).

  3. An instrument-response model — effective length, Galactic noise, RF chain, ADC (grand.sim).

Forward and back

The simulation runs forward, from shower to field to voltage to ADC counts. grand.analysis (Marion Guelfand) runs back: it reconstructs the arrival direction, the distance to the shower maximum and an electromagnetic-energy estimate from recorded times and amplitudes, with plane-wave, spherical-wave and ADF fits. Its tests show that the fits recover what their own forward models generate; comparison with full simulations and measured showers is still to come.

Layering

The intended layering is that grand.geo and grand.dataio sit at the bottom and know nothing above them, grand.sim composes both and grand.aoi and grand.basis sit on top providing the objects a user handles and the plots they look at.

The imports do not quite follow it:

dependency graph of the six grand subpackages, measured from their import statements, showing a three-edge cycle between geo, dataio and basis

Click the figure to open it full size.

The diagram is generated from the import statements under grand/ by python docs/dev/make_modules_diagram.py --measure. Re-run it after moving code between subpackages.

Warning

There is a module-level import cycle. It is not visible from any one file:

grand.geo.topography     imports  grand.dataio.protocol
grand.dataio.root_files  imports  grand.basis.traces_event
grand.basis.type_trace   imports  grand.geo.coordinates

Python tolerates the cycle, because none of these modules needs the others’ names at import time, but it makes the package sensitive to import order. The first edge is also why topography needs ROOT (Topography, the data layer and the simulation need ROOT).

grand/basis/pipeline.py imports Efield2Voltage inside a function rather than at module level, which avoids a second cycle. Follow that pattern where an upward import cannot be avoided.

grand.analysis sits on top: it imports geometry (through the package namespace, from grand import Geodetic) and nothing in grand imports it back.

Lazy imports

import grand loads nothing heavy: its public names (Geodetic, Efield2Voltage, RFChain, …) are imported on first use (PEP 562). import grand and grand.geo.coordinates therefore work without ROOT or the compiled libraries. The documentation build uses a typed stand-in for ROOT where it is absent.