|
| 1 | +// This is a main entry point for configuring a wire-cell CLI job to |
| 2 | +// simulate SBND. It is simplest signal-only simulation with |
| 3 | +// one set of nominal field response function. |
| 4 | + |
| 5 | +local epoch = std.extVar('epoch'); // eg "dynamic", "after", "before", "perfect" |
| 6 | +local reality = std.extVar('reality'); |
| 7 | +local sigoutform = std.extVar('signal_output_form'); // eg "sparse" or "dense" |
| 8 | +local savetid = std.extVar("save_track_id"); |
| 9 | + |
| 10 | +local g = import 'pgraph.jsonnet'; |
| 11 | +local f = import 'pgrapher/common/funcs.jsonnet'; |
| 12 | +local wc = import 'wirecell.jsonnet'; |
| 13 | +local io = import 'pgrapher/common/fileio.jsonnet'; |
| 14 | +local tools_maker = import 'pgrapher/common/tools.jsonnet'; |
| 15 | + |
| 16 | +local data_params = import 'params.jsonnet'; |
| 17 | +local simu_params = import 'simparams.jsonnet'; |
| 18 | +local params = if reality == 'data' then data_params else simu_params; |
| 19 | + |
| 20 | +local base = import 'pgrapher/experiment/sbnd/simparams.jsonnet'; |
| 21 | +local params = base { |
| 22 | + lar: super.lar { // <- super.lar overrides default values |
| 23 | + // Longitudinal diffusion constant |
| 24 | + DL: std.extVar('DL') * wc.cm2 / wc.s, |
| 25 | + // Transverse diffusion constant |
| 26 | + DT: std.extVar('DT') * wc.cm2 / wc.s, |
| 27 | + // Electron lifetime |
| 28 | + lifetime: std.extVar('lifetime') * wc.ms, |
| 29 | + // Electron drift speed, assumes a certain applied E-field |
| 30 | + drift_speed: std.extVar('driftSpeed') * wc.mm / wc.us, |
| 31 | + }, |
| 32 | +}; |
| 33 | + |
| 34 | +local tools = tools_maker(params); |
| 35 | +local sim_maker = import 'pgrapher/experiment/sbnd/sim.jsonnet'; |
| 36 | +local sim = sim_maker(params, tools); |
| 37 | +local nanodes = std.length(tools.anodes); |
| 38 | +local anode_iota = std.range(0, nanodes - 1); |
| 39 | + |
| 40 | +local wcls_maker = import "pgrapher/ui/wcls/nodes.jsonnet"; |
| 41 | +local wcls = wcls_maker(params, tools); |
| 42 | + |
| 43 | +// added Ewerton 2023-03-14 |
| 44 | +local wcls_input_sim = { |
| 45 | + depos: wcls.input.depos(name="", art_tag=std.extVar('inputTag')), |
| 46 | + deposet: g.pnode({ |
| 47 | + type: 'wclsSimDepoSetSource', |
| 48 | + name: "", |
| 49 | + data: { |
| 50 | + model: "", |
| 51 | + scale: -1, //scale is -1 to correct a sign error in the SimDepoSource converter. |
| 52 | + art_tag: std.extVar('inputTag'), //name of upstream art producer of depos "label:instance:processName" |
| 53 | + id_is_track: if (savetid == 'true') then false else true, |
| 54 | + assn_art_tag: "", |
| 55 | + }, |
| 56 | + }, nin=0, nout=1), |
| 57 | +}; |
| 58 | +// Collect all the wc/ls output converters for use below. Note the |
| 59 | +// "name" MUST match what is used in theh "outputers" parameter in the |
| 60 | +// FHiCL that loads this file. |
| 61 | +local mega_anode = { |
| 62 | + type: 'MegaAnodePlane', |
| 63 | + name: 'meganodes', |
| 64 | + data: { |
| 65 | + anodes_tn: [wc.tn(anode) for anode in tools.anodes], |
| 66 | + }, |
| 67 | +}; |
| 68 | +local wcls_output_sim = { |
| 69 | + // ADC output from simulation |
| 70 | + // sim_digits: wcls.output.digits(name="simdigits", tags=["orig"]), |
| 71 | + sim_digits: g.pnode({ |
| 72 | + type: 'wclsFrameSaver', |
| 73 | + name: 'simdigits', |
| 74 | + data: { |
| 75 | + anode: wc.tn(mega_anode), |
| 76 | + digitize: true, // true means save as RawDigit, else recob::Wire |
| 77 | + frame_tags: ['daq'], |
| 78 | + nticks: params.daq.nticks, |
| 79 | + pedestal_mean: 'native', |
| 80 | + }, |
| 81 | + }, nin=1, nout=1, uses=[mega_anode]), |
| 82 | + // The noise filtered "ADC" values. These are truncated for |
| 83 | + // art::Event but left as floats for the WCT SP. Note, the tag |
| 84 | + // "raw" is somewhat historical as the output is not equivalent to |
| 85 | + // "raw data". |
| 86 | + nf_digits: wcls.output.digits(name="nfdigits", tags=["raw"]), |
| 87 | + // The output of signal processing. Note, there are two signal |
| 88 | + // sets each created with its own filter. The "gauss" one is best |
| 89 | + // for charge reconstruction, the "wiener" is best for S/N |
| 90 | + // separation. Both are used in downstream WC code. |
| 91 | + sp_signals: wcls.output.signals(name="spsignals", tags=["gauss", "wiener"]), |
| 92 | + // save "threshold" from normal decon for each channel noise |
| 93 | + // used in imaging |
| 94 | + sp_thresholds: wcls.output.thresholds(name="spthresholds", tags=["threshold"]), |
| 95 | +}; |
| 96 | + |
| 97 | + |
| 98 | +local drifter = sim.drifter; |
| 99 | + |
| 100 | +local setdrifter = g.pnode({ |
| 101 | + type: 'DepoSetDrifter', |
| 102 | + data: { |
| 103 | + drifter: "Drifter" |
| 104 | + } |
| 105 | + }, nin=1, nout=1, |
| 106 | + uses=[drifter]); |
| 107 | + |
| 108 | +// signal plus noise pipelines |
| 109 | +local sn_pipes = sim.splusn_pipelines; |
| 110 | + |
| 111 | +local rng = tools.random; |
| 112 | +local wcls_depoflux_writer = g.pnode({ |
| 113 | + type: 'wclsDepoFluxWriter', |
| 114 | + name: 'postdrift', |
| 115 | + data: { |
| 116 | + anodes: [wc.tn(anode) for anode in tools.anodes], |
| 117 | + field_response: wc.tn(tools.field), |
| 118 | + tick: 0.5 * wc.us, |
| 119 | + window_start: 0.0 * wc.ms, |
| 120 | + window_duration: self.tick * 3400, |
| 121 | + nsigma: 3.0, |
| 122 | + |
| 123 | + reference_time: -1700 * wc.us, |
| 124 | + |
| 125 | + energy: 1, # equivalent to use_energy = true |
| 126 | + simchan_label: 'simpleSC', |
| 127 | + sed_label: if (savetid == 'true') then 'ionandscint' else '', |
| 128 | + sparse: false, |
| 129 | + }, |
| 130 | +}, nin=1, nout=1, uses=tools.anodes + [tools.field]); |
| 131 | + |
| 132 | +local sp_maker = import 'pgrapher/experiment/sbnd/sp.jsonnet'; |
| 133 | +local sp = sp_maker(params, tools, { sparse: sigoutform == 'sparse' }); |
| 134 | +local sp_pipes = [sp.make_sigproc(a) for a in tools.anodes]; |
| 135 | + |
| 136 | +local magoutput = 'sbnd-data-check.root'; |
| 137 | +local magnify = import 'pgrapher/experiment/sbnd/magnify-sinks.jsonnet'; |
| 138 | +local sinks = magnify(tools, magoutput); |
| 139 | + |
| 140 | +local perfect = import 'pgrapher/experiment/sbnd/chndb-perfect.jsonnet'; |
| 141 | + |
| 142 | +local chndb = [{ |
| 143 | + type: 'OmniChannelNoiseDB', |
| 144 | + name: 'ocndbperfect%d' % n, |
| 145 | + data: perfect(params, tools.anodes[n], tools.field, n){dft:wc.tn(tools.dft)}, |
| 146 | + // data: base(params, tools.anodes[n], tools.field, n){dft:wc.tn(tools.dft)}, |
| 147 | + uses: [tools.anodes[n], tools.field, tools.dft], |
| 148 | +} for n in anode_iota]; |
| 149 | + |
| 150 | +local nf_maker = import 'pgrapher/experiment/sbnd/nf.jsonnet'; |
| 151 | +local nf_pipes = [nf_maker(params, tools.anodes[n], chndb[n], n, name='nf%d' % n) for n in anode_iota]; |
| 152 | + |
| 153 | +local multipass1 = [ |
| 154 | + g.pipeline([ |
| 155 | + sn_pipes[n], |
| 156 | + ], |
| 157 | + 'multipass%d' % n) |
| 158 | + for n in anode_iota |
| 159 | +]; |
| 160 | + |
| 161 | +local multipass2 = [ |
| 162 | + g.pipeline([ |
| 163 | + sn_pipes[n], |
| 164 | + //sinks.orig_pipe[n], |
| 165 | + |
| 166 | + // nf_pipes[n], // NEED to include this pipe for channelmaskmaps |
| 167 | + //sinks.raw_pipe[n], |
| 168 | + |
| 169 | + sp_pipes[n], |
| 170 | + |
| 171 | + //sinks.decon_pipe[n], |
| 172 | + //sinks.threshold_pipe[n], |
| 173 | + ], |
| 174 | + 'multipass%d' % n) |
| 175 | + for n in anode_iota |
| 176 | +]; |
| 177 | + |
| 178 | + |
| 179 | +local f_sp = import 'pgrapher/experiment/sbnd/funcs.jsonnet'; |
| 180 | + |
| 181 | +local outtags = ['orig%d' % n for n in anode_iota]; |
| 182 | +local bi_manifold1 = f.fanpipe('DepoSetFanout', multipass1, 'FrameFanin', 'sn_mag_nf', outtags); |
| 183 | +local bi_manifold2 = f_sp.fanpipe('DepoSetFanout', multipass2, 'FrameFanin', 'sn_mag_nf_mod2', outtags, "true"); |
| 184 | + |
| 185 | +local retagger_sim = g.pnode({ |
| 186 | + type: 'Retagger', |
| 187 | + data: { |
| 188 | + // Note: retagger keeps tag_rules an array to be like frame fanin/fanout. |
| 189 | + tag_rules: [{ |
| 190 | + // Retagger also handles "frame" and "trace" like fanin/fanout |
| 191 | + // merge separately all traces like gaussN to gauss. |
| 192 | + frame: { |
| 193 | + '.*': 'orig', |
| 194 | + }, |
| 195 | + merge: { |
| 196 | + 'orig\\d': 'daq', |
| 197 | + }, |
| 198 | + }], |
| 199 | + }, |
| 200 | +}, nin=1, nout=1); |
| 201 | + |
| 202 | +local sink_sim = sim.frame_sink; |
| 203 | + |
| 204 | +//===============================NF+SP============================================ |
| 205 | + |
| 206 | +// Collect all the wc/ls output converters for use below. Note the |
| 207 | +// "name" MUST match what is used in theh "outputers" parameter in the |
| 208 | +// FHiCL that loads this file. |
| 209 | + |
| 210 | +local wcls_output_sp = { |
| 211 | + // The noise filtered "ADC" values. These are truncated for |
| 212 | + // art::Event but left as floats for the WCT SP. Note, the tag |
| 213 | + // "raw" is somewhat historical as the output is not equivalent to |
| 214 | + // "raw data". |
| 215 | + nf_digits: g.pnode({ |
| 216 | + type: 'wclsFrameSaver', |
| 217 | + name: 'nfsaver', |
| 218 | + data: { |
| 219 | + anode: wc.tn(mega_anode), |
| 220 | + digitize: true, // true means save as RawDigit, else recob::Wire |
| 221 | + frame_tags: ['raw'], |
| 222 | + }, |
| 223 | + }, nin=1, nout=1, uses=[mega_anode]), |
| 224 | + |
| 225 | + |
| 226 | + // The output of signal processing. Note, there are two signal |
| 227 | + // sets each created with its own filter. The "gauss" one is best |
| 228 | + // for charge reconstruction, the "wiener" is best for S/N |
| 229 | + // separation. Both are used in downstream WC code. |
| 230 | + sp_signals: g.pnode({ |
| 231 | + type: 'wclsFrameSaver', |
| 232 | + name: 'spsaver', |
| 233 | + data: { |
| 234 | + anode: wc.tn(mega_anode), |
| 235 | + digitize: false, // true means save as RawDigit, else recob::Wire |
| 236 | + frame_tags: ['gauss', 'wiener'], |
| 237 | + |
| 238 | + // this may be needed to convert the decon charge [units:e-] to be consistent with the LArSoft default |
| 239 | + // for SBND, this scale is about ~50. Frame scale needed when using LArSoft producers reading in recob::Wire. |
| 240 | + frame_scale: [0.02, 0.02], |
| 241 | + nticks: params.daq.nticks, |
| 242 | + chanmaskmaps: [], |
| 243 | + |
| 244 | + // uncomment the below configs to save summaries and cmm |
| 245 | + // summary_tags: ['wiener'], |
| 246 | + // summary_operator: {threshold: 'set'}, |
| 247 | + // summary_scale: [0.02], # summary scale should be the same as frame_scale |
| 248 | + // chanmaskmaps: ['bad'], |
| 249 | + }, |
| 250 | + }, nin=1, nout=1, uses=[mega_anode]), |
| 251 | + |
| 252 | +}; |
| 253 | + |
| 254 | + |
| 255 | +local chsel_pipes = [ |
| 256 | + g.pnode({ |
| 257 | + type: 'ChannelSelector', |
| 258 | + name: 'chsel%d' % n, |
| 259 | + data: { |
| 260 | + channels: std.range(5632 * n, 5632 * (n + 1) - 1), |
| 261 | + }, |
| 262 | + }, nin=1, nout=1) |
| 263 | + for n in anode_iota |
| 264 | +]; |
| 265 | + |
| 266 | + |
| 267 | +local nfsp_pipes = [ |
| 268 | + g.pipeline([ |
| 269 | + chsel_pipes[n], |
| 270 | + //sinks.orig_pipe[n], |
| 271 | + |
| 272 | + // nf_pipes[n], // NEED to include this pipe for channelmaskmaps |
| 273 | + //sinks.raw_pipe[n], |
| 274 | + |
| 275 | + sp_pipes[n], |
| 276 | + //sinks.decon_pipe[n], |
| 277 | + //sinks.threshold_pipe[n], |
| 278 | + // sinks.debug_pipe[n], // use_roi_debug_mode=true in sp.jsonnet |
| 279 | + ], |
| 280 | + 'nfsp_pipe_%d' % n) |
| 281 | + for n in anode_iota |
| 282 | +]; |
| 283 | + |
| 284 | +//local fanpipe = f_sp.fanpipe('FrameFanout', nfsp_pipes, 'FrameFanin', 'sn_mag_nf'); // commented Ewerton 2023-05-24 |
| 285 | +local fanpipe = f_sp.fanpipe('FrameFanout', nfsp_pipes, 'FrameFanin', 'sn_mag_nf_mod'); //added Ewerton 2023-05-24 |
| 286 | + |
| 287 | +local retagger_sp = g.pnode({ |
| 288 | + type: 'Retagger', |
| 289 | + name: 'sp', //added Ewerton 2023-05-24 |
| 290 | + data: { |
| 291 | + // Note: retagger keeps tag_rules an array to be like frame fanin/fanout. |
| 292 | + tag_rules: [{ |
| 293 | + // Retagger also handles "frame" and "trace" like fanin/fanout |
| 294 | + // merge separately all traces like gaussN to gauss. |
| 295 | + frame: { |
| 296 | + '.*': 'retagger', |
| 297 | + }, |
| 298 | + merge: { |
| 299 | + 'gauss\\d': 'gauss', |
| 300 | + 'wiener\\d': 'wiener', |
| 301 | + }, |
| 302 | + }], |
| 303 | + }, |
| 304 | +}, nin=1, nout=1); |
| 305 | + |
| 306 | +local sink_sp = g.pnode({ type: 'DumpFrames' }, nin=1, nout=0); |
| 307 | + |
| 308 | +local graph1 = g.pipeline([ |
| 309 | +wcls_input_sim.deposet, //sim |
| 310 | +setdrifter, //sim |
| 311 | +wcls_depoflux_writer, //sim |
| 312 | +bi_manifold1, //sim |
| 313 | +retagger_sim, //sim |
| 314 | +wcls_output_sim.sim_digits, //sim |
| 315 | +fanpipe, //sp |
| 316 | +retagger_sp, //sp |
| 317 | +wcls_output_sp.sp_signals, //sp |
| 318 | +sink_sp //sp |
| 319 | +]); |
| 320 | + |
| 321 | + |
| 322 | +local graph2 = g.pipeline([ |
| 323 | +wcls_input_sim.deposet, //sim |
| 324 | +setdrifter, //sim |
| 325 | +wcls_depoflux_writer, //sim |
| 326 | +bi_manifold2, //sim |
| 327 | +retagger_sp, //sp |
| 328 | +wcls_output_sp.sp_signals, //sp |
| 329 | +sink_sp //sp |
| 330 | +]); |
| 331 | + |
| 332 | +local save_simdigits = std.extVar('save_simdigits'); |
| 333 | + |
| 334 | +local graph = if save_simdigits == "true" then graph1 else graph2; |
| 335 | + |
| 336 | +local app = { |
| 337 | + type: 'TbbFlow', |
| 338 | + data: { |
| 339 | + edges: g.edges(graph), |
| 340 | + }, |
| 341 | +}; |
| 342 | + |
| 343 | +// Finally, the configuration sequence |
| 344 | +g.uses(graph) + [app] |
| 345 | + |
0 commit comments