docu
This commit is contained in:
+234
-16
@@ -1,3 +1,153 @@
|
|||||||
|
"""
|
||||||
|
============================================================
|
||||||
|
G4Calo Public Interface Functions
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
This module provides two main functions intended for student use:
|
||||||
|
- run_batch(...)
|
||||||
|
- display_event(...)
|
||||||
|
|
||||||
|
They allow running Geant4 calorimeter simulations and visualising
|
||||||
|
individual events using a pandas-like, lightweight data structure
|
||||||
|
(MiniFrame) built for teaching and analysis.
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
Function: run_batch(...)
|
||||||
|
------------------------------------------------------------
|
||||||
|
Purpose
|
||||||
|
-------
|
||||||
|
Run a full Geant4 simulation batch with automatic parallelisation
|
||||||
|
and return the results as a lightweight, pandas-like table (MiniFrame).
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
`run_batch` distributes a requested number of Geant4 events across
|
||||||
|
available CPU cores, runs all mini-batches in parallel, collects their
|
||||||
|
temporary ROOT output files, converts them to numpy arrays, and
|
||||||
|
finally assembles everything into a single MiniFrame for analysis
|
||||||
|
or visualisation.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
gd : GeometryDescriptor
|
||||||
|
The calorimeter geometry to simulate.
|
||||||
|
|
||||||
|
nEvents : int
|
||||||
|
Total number of events to simulate. Must be > 0.
|
||||||
|
|
||||||
|
particleSpec : str or list[str]
|
||||||
|
Particle type(s) to generate, e.g. "gamma" or ["e-", "pi+"].
|
||||||
|
|
||||||
|
minEnergy_GeV : float
|
||||||
|
Minimum kinetic energy in GeV for the primary particles.
|
||||||
|
|
||||||
|
maxEnergy_GeV : float, optional
|
||||||
|
Maximum kinetic energy in GeV. If negative (default), identical
|
||||||
|
to minEnergy_GeV.
|
||||||
|
|
||||||
|
filename : str, optional
|
||||||
|
Optional output file name for storing the resulting MiniFrame
|
||||||
|
using pickle format. If empty, no file is written.
|
||||||
|
|
||||||
|
no_mp : bool, optional
|
||||||
|
If True, disables multiprocessing and runs all events sequentially.
|
||||||
|
Useful for debugging.
|
||||||
|
|
||||||
|
manual_seed : int, optional
|
||||||
|
Optional random-seed override. If < 0 (default), an internal per-core
|
||||||
|
seed sequence is generated and persisted between runs.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
MiniFrame
|
||||||
|
A table-like object containing one row per simulated event.
|
||||||
|
Each row stores scalar quantities (e.g. true_energy, total_dep_energy)
|
||||||
|
and fixed-size arrays for each sensor-level variable
|
||||||
|
(sensor_energy, sensor_x, sensor_y, ...).
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
- Automatically chooses the number of cores so that each core processes
|
||||||
|
at least ~80 events.
|
||||||
|
- Temporary ROOT files are written to /dev/shm or /tmp and removed after merging.
|
||||||
|
- Prints timing information for simulation and concatenation steps.
|
||||||
|
- For deterministic results, specify manual_seed.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> df = run_batch(gd, nEvents=1000, particleSpec="gamma", minEnergy_GeV=1.0)
|
||||||
|
>>> print(df.describe())
|
||||||
|
>>> df.to_pickle("results.pkl")
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
Function: display_event(...)
|
||||||
|
------------------------------------------------------------
|
||||||
|
Purpose
|
||||||
|
-------
|
||||||
|
Visualise the energy deposition of a single simulated event in 3D
|
||||||
|
using Plotly.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
`display_event` performs a one-event Geant4 simulation with the given
|
||||||
|
geometry, retrieves deposited sensor energies, and renders a 3D view
|
||||||
|
of the calorimeter including materials, layers, sensors, and the
|
||||||
|
incoming particle direction.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
gd : GeometryDescriptor
|
||||||
|
The calorimeter geometry descriptor (modified in-place).
|
||||||
|
|
||||||
|
particleSpec : str or list[str]
|
||||||
|
Particle type(s) to simulate, e.g. "e-", "pi+", "gamma".
|
||||||
|
|
||||||
|
energy : float
|
||||||
|
Particle kinetic energy in GeV.
|
||||||
|
|
||||||
|
logE : bool, optional
|
||||||
|
If True, use logarithmic scaling for sensor opacity
|
||||||
|
(currently not implemented).
|
||||||
|
|
||||||
|
renderer : str, optional
|
||||||
|
Optional Plotly renderer ("browser", "notebook", "png", ...).
|
||||||
|
If omitted, uses the default interactive renderer.
|
||||||
|
|
||||||
|
seed : int, optional
|
||||||
|
Optional random seed for reproducibility.
|
||||||
|
|
||||||
|
outfile : str, optional
|
||||||
|
If provided, saves the figure as an interactive HTML file
|
||||||
|
instead of displaying it.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None — the function displays or saves the visualisation.
|
||||||
|
|
||||||
|
What the Plot Shows
|
||||||
|
-------------------
|
||||||
|
- Coloured cubes for each material layer (colour legend included)
|
||||||
|
- Black semi-transparent cubes for sensors, opacity ∝ deposited energy
|
||||||
|
- Red arrow showing incoming particle direction
|
||||||
|
- Axes labelled in mm (x, y, z following HEP conventions)
|
||||||
|
- Interactive rotation and zoom supported
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> display_event(gd, particleSpec="gamma", energy=1.0, renderer="browser")
|
||||||
|
|
||||||
|
Typical Workflow
|
||||||
|
----------------
|
||||||
|
>>> df = run_batch(gd, 200, "e-", 1.0)
|
||||||
|
>>> print(df.describe())
|
||||||
|
>>> display_event(gd, "e-", 1.0, renderer="browser")
|
||||||
|
|
||||||
|
These two functions form the primary interface for student exercises:
|
||||||
|
`run_batch` to simulate, and `display_event` to visualise results.
|
||||||
|
All heavy lifting (parallelisation, I/O, and conversion) is handled
|
||||||
|
internally so that focus remains on physics interpretation.
|
||||||
|
"""
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import warnings
|
import warnings
|
||||||
@@ -6,16 +156,6 @@ import sys
|
|||||||
from minicalo_tools import TempFileManager
|
from minicalo_tools import TempFileManager
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
'''
|
|
||||||
give G4Calo package an executable that takes input and output json files names
|
|
||||||
|
|
||||||
This calls the actual Geant stuff.
|
|
||||||
The input are all the arguments and a geometry descriptor
|
|
||||||
The output is the data frame as well as the changed geometry descriptor
|
|
||||||
|
|
||||||
The user only interacts with a wrapper that calls this executable through a system call
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
from minicalo import GeometryDescriptor
|
from minicalo import GeometryDescriptor
|
||||||
|
|
||||||
@@ -199,12 +339,52 @@ def run_batch(
|
|||||||
filename: str = "",
|
filename: str = "",
|
||||||
no_mp: bool = False,
|
no_mp: bool = False,
|
||||||
manual_seed : int = -1):
|
manual_seed : int = -1):
|
||||||
'''
|
"""
|
||||||
splits the batch in jobs depending on how many cores are available and runs mini batches in parallel
|
Run a full Geant4 simulation batch with automatic parallelisation.
|
||||||
this will create threads.
|
|
||||||
If no_mp is set to True, it will run the batch in a single thread
|
Distributes the requested number of events across CPU cores, runs each
|
||||||
The seeding is not thread safe (unless provided by hand)
|
mini-batch in parallel, collects temporary ROOT files, converts them to
|
||||||
'''
|
numpy arrays, and merges everything into a lightweight, pandas-like
|
||||||
|
MiniFrame.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
gd : GeometryDescriptor
|
||||||
|
Calorimeter geometry to simulate.
|
||||||
|
nEvents : int
|
||||||
|
Total number of events to simulate.
|
||||||
|
particleSpec : str or list[str]
|
||||||
|
Particle type(s), e.g. "gamma" or ["e-", "pi+"].
|
||||||
|
minEnergy_GeV : float
|
||||||
|
Minimum kinetic energy in GeV.
|
||||||
|
maxEnergy_GeV : float, optional
|
||||||
|
Maximum kinetic energy in GeV. If negative, equals minEnergy_GeV.
|
||||||
|
filename : str, optional
|
||||||
|
Optional pickle filename for saving results.
|
||||||
|
no_mp : bool, optional
|
||||||
|
Disable multiprocessing (run single-threaded) for debugging.
|
||||||
|
manual_seed : int, optional
|
||||||
|
Optional random seed. If < 0, seeds are generated automatically.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
MiniFrame
|
||||||
|
Table-like structure with one row per event, containing both scalar
|
||||||
|
values and fixed-size sensor arrays.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
- Automatically chooses the number of cores to balance workload.
|
||||||
|
- Temporary files are written to /dev/shm or /tmp and removed after merging.
|
||||||
|
- Prints timing information for both simulation and data assembly.
|
||||||
|
- For reproducibility, set manual_seed.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> df = run_batch(gd, 1000, "gamma", 1.0)
|
||||||
|
>>> print(df.describe())
|
||||||
|
>>> df.to_pickle("results.pkl")
|
||||||
|
"""
|
||||||
assert nEvents > 0
|
assert nEvents > 0
|
||||||
assert minEnergy_GeV > 0
|
assert minEnergy_GeV > 0
|
||||||
|
|
||||||
@@ -346,6 +526,44 @@ def display_event(gd : GeometryDescriptor,
|
|||||||
particleSpec,
|
particleSpec,
|
||||||
energy: float,
|
energy: float,
|
||||||
logE = False, renderer=None, seed = -1, outfile : str = ""):
|
logE = False, renderer=None, seed = -1, outfile : str = ""):
|
||||||
|
"""
|
||||||
|
Visualise a single simulated event in 3D using Plotly.
|
||||||
|
|
||||||
|
Runs a one-event Geant4 simulation with the provided geometry, extracts
|
||||||
|
the deposited sensor energies, and renders an interactive 3D view of
|
||||||
|
the calorimeter with materials, layers, and energy deposition.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
gd : GeometryDescriptor
|
||||||
|
The calorimeter geometry (modified in place by the simulation).
|
||||||
|
particleSpec : str or list[str]
|
||||||
|
Particle type(s), e.g. "e-", "pi+", "gamma".
|
||||||
|
energy : float
|
||||||
|
Particle kinetic energy in GeV.
|
||||||
|
logE : bool, optional
|
||||||
|
Use logarithmic opacity scaling (currently not implemented).
|
||||||
|
renderer : str, optional
|
||||||
|
Plotly renderer ("browser", "notebook", etc.). Default: interactive.
|
||||||
|
seed : int, optional
|
||||||
|
Random seed for reproducibility.
|
||||||
|
outfile : str, optional
|
||||||
|
If set, saves the figure as an interactive HTML file.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None — displays or saves a 3D Plotly figure.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> display_event(gd, "gamma", 1.0, renderer="browser")
|
||||||
|
|
||||||
|
The plot shows:
|
||||||
|
- Coloured cubes for material layers
|
||||||
|
- Semi-transparent black cubes for sensors (opacity ∝ deposited energy)
|
||||||
|
- Red arrow for incoming particle direction
|
||||||
|
- Interactive rotation and zoom
|
||||||
|
"""
|
||||||
|
|
||||||
gd = _fill_event(gd, particleSpec, energy)
|
gd = _fill_event(gd, particleSpec, energy)
|
||||||
|
|
||||||
|
|||||||
+68
-3
@@ -1,4 +1,45 @@
|
|||||||
# minipandas.py
|
# minipandas.py
|
||||||
|
"""
|
||||||
|
MiniFrame: a minimal, NumPy-backed, pandas-like table.
|
||||||
|
|
||||||
|
Each column is a NumPy array sharing the same row length (axis 0).
|
||||||
|
Columns may be:
|
||||||
|
• scalar columns: shape (N,)
|
||||||
|
• vector columns: shape (N, M) with fixed M across the frame
|
||||||
|
|
||||||
|
Supports familiar pandas patterns:
|
||||||
|
• len(mf), mf.shape, mf.keys(), "col" in mf
|
||||||
|
• column access: mf["col"] -> ndarray
|
||||||
|
• row selection: mf[idx], mf[:N], mf[mask] -> MiniFrame (views where possible)
|
||||||
|
• head(), copy(), describe(), to_pandas(), to_pickle()/read_pickle()
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data : Mapping[str, np.ndarray or scalar]
|
||||||
|
Column data. Scalars are broadcast to length N.
|
||||||
|
length : int, optional (keyword-only)
|
||||||
|
Row count N to use when all provided values are scalars. If omitted,
|
||||||
|
N is inferred from the first non-scalar column.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
- Only 1D (N,) and 2D (N, M) columns are supported.
|
||||||
|
- Vector columns are expanded to wide columns (<name>_0..M-1) by to_pandas().
|
||||||
|
- Properties:
|
||||||
|
scalar_cols -> list[str] # columns with ndim == 1
|
||||||
|
vector_cols -> list[str] # columns with ndim == 2
|
||||||
|
vector_length(col) -> int or None
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
>>> mf = MiniFrame({"run": 7, "event": np.arange(5), "energy": np.random.rand(5, 4)})
|
||||||
|
>>> mf[:3] # first 3 rows
|
||||||
|
>>> mf["energy"] # raw ndarray (5, 4)
|
||||||
|
>>> mf.describe() # quick stats
|
||||||
|
>>> df = mf.to_pandas() # convert to pandas (energy -> energy_0.._3)
|
||||||
|
>>> mf.to_pickle("out.pkl")
|
||||||
|
>>> mf2 = MiniFrame.read_pickle("out.pkl")
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -213,9 +254,33 @@ def concat(
|
|||||||
copy: bool = False,
|
copy: bool = False,
|
||||||
) -> MiniFrame:
|
) -> MiniFrame:
|
||||||
"""
|
"""
|
||||||
Concatenate MiniFrames along rows (axis=0). Column sets and per-column shapes must match.
|
Concatenate MiniFrames along rows (axis=0), pandas-style.
|
||||||
Args match pandas.concat subset for familiarity; only axis=0 is supported.
|
|
||||||
"""
|
This is a lightweight analogue of pandas.concat for MiniFrame objects.
|
||||||
|
All frames must have identical column sets and per-column shapes
|
||||||
|
(except for the row dimension).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
objs : Iterable[MiniFrame]
|
||||||
|
Frames to concatenate.
|
||||||
|
axis : int, default 0
|
||||||
|
Only axis=0 (row-wise) is supported.
|
||||||
|
ignore_index : bool, default False
|
||||||
|
Accepted for API similarity; MiniFrame has no explicit index (no-op).
|
||||||
|
copy : bool, default False
|
||||||
|
If True, return a deep copy of the result.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
MiniFrame
|
||||||
|
The row-wise concatenation of the input frames.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
>>> mf = mpd.concat([mf1, mf2])
|
||||||
|
>>> mf.shape
|
||||||
|
"""
|
||||||
objs = list(objs)
|
objs = list(objs)
|
||||||
if not objs:
|
if not objs:
|
||||||
raise ValueError("minipandas.concat: empty iterable.")
|
raise ValueError("minipandas.concat: empty iterable.")
|
||||||
|
|||||||
Reference in New Issue
Block a user