Simulate realistic waveforms
You can also download this tutorial as a Jupyter notebook and a plain Julia source file.
Here we will use an example pet file csv format from LegendTestData corresponding to the Public Inverted Coax.
using LegendGeSim
using PlotsGet inputs from legend-test-data
using LegendTestDataDetector metadata
ldsim_path = joinpath(legend_test_data_path(), "data", "ldsim")
detector_name = "invcoax-metadata"
detector_metadata_filename = joinpath(ldsim_path, detector_name*".json");Alternatively, enter your own path to a real LEGEND detector JSON
detector_metadata_filename = "path/to/V04545A.json"PET input file
path_to_pet_file = joinpath(ldsim_path, "single-invcoax-th228-geant4.csv");Settings
See manual on Field Simulation and Ideal Pulse Simulation for a detailed explanation of environment and simulation settings, as well as the noise model settings
environment_settings = Dict(
"crystal_temperature_in_K" => 77,
"medium" => "vacuum",
);simple settings for point charge simulation with dummy constant impurity
simulation_settings = Dict(
"method" => "SSD",
"cached_name" => "", # a non-empty string will cache the simulation results
);daq_settings = Dict(
"preamp" => Dict(
"type" => "generic",
"t_decay_in_us" => 43, # from V04545A HADES data
"t_rise_in_ns" => 100, # by eye
"gain_ADC_eV" => 0.0138, # by eye from V04545A HADES data FEP @ 36100 ADC
"offset_in_ADC" => 11900, # from V04545A HADES data mean() of baseline
"noise_sigma_in_keV" => 2 # by eye
),
"fadc" => Dict(
"type" => "generic",
"sampling_interval" => 16 # ns, from HADES data
),
"trigger" => Dict(
"type" => "trapezoidal",
"window_lengths" => [250,250,250],
"threshold" => 9 # keV
),
"daq" => Dict(
"type" => "generic",
"nsamples" => 3748, # from HADES data
"baseline_length" => 1770 # by eye from data
)
);noise_model = Dict(
"type" => "sim"
);Simulate from scratch (pet -> raw)
raw_table = LegendGeSim.simulate_raw(detector_metadata_filename, path_to_pet_file, environment_settings, simulation_settings, daq_settings, noise_model; n_waveforms=10)Table with 11 columns and 20 rows:
baseline channel energy ievt numtraces packet_id ⋯
┌──────────────────────────────────────────────────────────────
1 │ 0.0 1 0.0 keV 2 1.0 0.0 ⋯
2 │ 11916.7 1 185.304 keV 7 1.0 0.0 ⋯
3 │ 11916.4 1 82.5646 keV 12 1.0 0.0 ⋯
4 │ 11912.5 1 484.552 keV 13 1.0 0.0 ⋯
5 │ 11912.3 1 553.347 keV 17 1.0 0.0 ⋯
6 │ 11913.9 1 125.248 keV 40 1.0 0.0 ⋯
7 │ 11918.8 1 2481.31 keV 44 1.0 0.0 ⋯
8 │ 11911.8 1 338.64 keV 55 1.0 0.0 ⋯
9 │ 11916.3 1 90.5444 keV 62 1.0 0.0 ⋯
10 │ 11917.1 1 225.37 keV 64 1.0 0.0 ⋯
11 │ 0.0 2 0.0 keV 2 1.0 0.0 ⋯
12 │ 11919.2 2 184.571 keV 7 1.0 0.0 ⋯
13 │ 11915.6 2 81.5356 keV 12 1.0 0.0 ⋯
14 │ 11917.2 2 484.16 keV 13 1.0 0.0 ⋯
15 │ 11918.5 2 553.269 keV 17 1.0 0.0 ⋯
16 │ 11920.5 2 124.732 keV 40 1.0 0.0 ⋯
17 │ 11916.8 2 2481.54 keV 44 1.0 0.0 ⋯
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋱plot(raw_table.waveform)The file contains double the amount of waveforms (20) compared to what we asked to simulate (n_waveforms = 10). That's because at the pulse simulation level, n+ contact pulses are kept as well. The first 10 entries are p+ contact waveforms.
length(raw_table)20plot(raw_table.waveform[1:10], legend=false, title="only p+ contact waveforms")You can save simulated waveforms in a file that can be used later
using LegendHDF5IOraw_name = "cache/test_100wfs_raw.hdf5"
lh5open(raw_name, "w") do f
LegendHDF5IO.writedata(f.data_store, "raw", raw_table[1:10])
endSimulate from pss table in code
Rather that simulating from scratch pet->raw, you may also input an already ready pss table with ideal pulses, and simulate only the pss->raw step, i.e. the DAQ chain simulation
pss_table, pss_truth = LegendGeSim.simulate_pulses(detector_metadata_filename, path_to_pet_file, environment_settings, simulation_settings, noise_model; n_waveforms=10);[ Info: ---------------------- pet -> stp (stepping info)
┌ Info: Legend SolidStateDetector - Public Inverted Coax
│ ╭───╮ ╭───╮ ╰─ ✔ Operational voltage (L200 characterization): 3800.0 V
│ │ │ │ │ ╰─ ✔ n⁺contact thickness (0νββ analysis): 0.75 mm
│ │ │ │ │ ╰─ ⚠ Impurity model (DEFAULT) / Detector Offset: constant / unknown
│ │ ╰─╯ │ ╰─ value: 0
│ │ │ ╰─ Corrections: Scale / Offset: - / -
└ ╰── ─── ──╯ ╰─ ✔ Volume / Active volume: 240 cm^3 / 224 cm^3
Processing file: /home/runner/.julia/artifacts/11b2a7ab47d6f84b449b8f16112d0f3489ec697d/legend-exp-legend-testdata-cd70a8d/data/ldsim/single-invcoax-th228-geant4.csv for detector Public Inverted Coax
[ Info: ...clustering
1411 hits before clustering
0.001368 seconds (22.78 k allocations: 1.823 MiB)
244 hits after clustering
[ Info: ...removing hits with no energy deposits
[ Info: ...removing events outside of the detector
[ Info: Simulation method: SSD
┌ Warning: No crystal metadata path given. Simulation with dummy constant impurity density.
└ @ LegendGeSim ~/work/LegendGeSim.jl/LegendGeSim.jl/src/pss.jl:74
[ Info: ---------------------- stp -> pss (ideal pulses)
[ Info: _||_||_||_ Simulate detector
[ Info: Simulating with SSD from scratch for given settings
[ Info: DL = 0mm
┌ Info: Legend SolidStateDetector - Public Inverted Coax
│ ╭───╮ ╭───╮ ╰─ ✔ Operational voltage (L200 characterization): 3800.0 V
│ │ │ │ │ ╰─ ✔ n⁺contact thickness (0νββ analysis): 0.75 mm
│ │ │ │ │ ╰─ ⚠ Impurity model (DEFAULT) / Detector Offset: constant / unknown
│ │ ╰─╯ │ ╰─ value: 0
│ │ │ ╰─ Corrections: Scale / Offset: - / -
└ ╰── ─── ──╯ ╰─ ✔ Volume / Active volume: 240 cm^3 / 224 cm^3
┌ Warning: You did not provide operating voltage in environment settings -> taking recommended voltage from metadata
└ @ LegendGeSim ~/work/LegendGeSim.jl/LegendGeSim.jl/src/legend_detector_to_ssd.jl:45
[ Info: Simulating at 3800.0V
...electric potential
...electric field
...weighting potential 1
...weighting potential 2
[ Info: //\//\//\ Adding fano noise
[ Info: DL = 0mm
┌ Info: Legend SolidStateDetector - Public Inverted Coax
│ ╭───╮ ╭───╮ ╰─ ✔ Operational voltage (L200 characterization): 3800.0 V
│ │ │ │ │ ╰─ ✔ n⁺contact thickness (0νββ analysis): 0.75 mm
│ │ │ │ │ ╰─ ⚠ Impurity model (DEFAULT) / Detector Offset: constant / unknown
│ │ ╰─╯ │ ╰─ value: 0
│ │ │ ╰─ Corrections: Scale / Offset: - / -
└ ╰── ─── ──╯ ╰─ ✔ Volume / Active volume: 240 cm^3 / 224 cm^3
┌ Warning: You did not provide operating voltage in environment settings -> taking recommended voltage from metadata
└ @ LegendGeSim ~/work/LegendGeSim.jl/LegendGeSim.jl/src/legend_detector_to_ssd.jl:45
[ Info: Simulating at 3800.0V
[ Info: ~.~.~.~.~ Simulate charge pulses
[ Info: ~.~.~ SolidStateDetectors
[ Info: Detector has 2 contacts
[ Info: Table has 10 physics events (23 single charge depositions).
[ Info: Generating waveforms...plot(pss_table.waveform[1:10])raw_table1 = LegendGeSim.pss_to_raw(pss_table, pss_truth, daq_settings, noise_model)Table with 11 columns and 20 rows:
baseline channel energy ievt numtraces packet_id ⋯
┌──────────────────────────────────────────────────────────────
1 │ 0.0 1 0.0 keV 2 1.0 0.0 ⋯
2 │ 11917.7 1 184.021 keV 7 1.0 0.0 ⋯
3 │ 11914.8 1 83.1359 keV 12 1.0 0.0 ⋯
4 │ 11916.7 1 484.915 keV 13 1.0 0.0 ⋯
5 │ 11914.9 1 553.659 keV 17 1.0 0.0 ⋯
6 │ 11917.6 1 125.228 keV 40 1.0 0.0 ⋯
7 │ 11914.3 1 2479.64 keV 44 1.0 0.0 ⋯
8 │ 11918.2 1 339.19 keV 55 1.0 0.0 ⋯
9 │ 11915.6 1 90.457 keV 62 1.0 0.0 ⋯
10 │ 11914.0 1 226.151 keV 64 1.0 0.0 ⋯
11 │ 0.0 2 0.0 keV 2 1.0 0.0 ⋯
12 │ 11913.5 2 184.531 keV 7 1.0 0.0 ⋯
13 │ 11916.5 2 83.3455 keV 12 1.0 0.0 ⋯
14 │ 11913.8 2 484.544 keV 13 1.0 0.0 ⋯
15 │ 11915.0 2 554.163 keV 17 1.0 0.0 ⋯
16 │ 11920.1 2 123.761 keV 40 1.0 0.0 ⋯
17 │ 11920.5 2 2479.54 keV 44 1.0 0.0 ⋯
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋱plot(
plot(pss_table.waveform[1:10]),
plot(raw_table1.waveform[1:10]),
size=(800,400)
)Save the pss file for the next section
using LegendHDF5IOpss_name = "cache/test_100wfs_pss.hdf5"
lh5open(pss_name, "w") do f
LegendHDF5IO.writedata(f.data_store, "pss/pss", pss_table[1:10])
LegendHDF5IO.writedata(f.data_store, "pss/truth", pss_truth[1:10])
endSimulate from pre-saved pss hdf5 file
Rather than simulating form scratch pet->raw, you may input the name of a pre-saved pss file containing pss and pss truth information
raw_table2 = LegendGeSim.pss_to_raw(pss_name, daq_settings, noise_model)Table with 11 columns and 10 rows:
baseline channel energy ievt numtraces packet_id ⋯
┌──────────────────────────────────────────────────────────────
1 │ 0.0 1 0.0 keV 2 1.0 0.0 ⋯
2 │ 11916.3 1 184.207 keV 7 1.0 0.0 ⋯
3 │ 11916.9 1 83.8926 keV 12 1.0 0.0 ⋯
4 │ 11912.0 1 484.724 keV 13 1.0 0.0 ⋯
5 │ 11914.1 1 553.87 keV 17 1.0 0.0 ⋯
6 │ 11913.7 1 125.476 keV 40 1.0 0.0 ⋯
7 │ 11918.2 1 2479.1 keV 44 1.0 0.0 ⋯
8 │ 11913.8 1 339.48 keV 55 1.0 0.0 ⋯
9 │ 11915.6 1 90.5926 keV 62 1.0 0.0 ⋯
10 │ 11916.4 1 226.305 keV 64 1.0 0.0 ⋯This page was generated using Literate.jl.