From 3b665b094ccd08440f6996f47285351ea4a74f66 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Tue, 20 Jul 2021 10:31:47 -0400 Subject: [PATCH 01/15] Snapshot of adding multithreaded wct --- README.org | 2 +- cfg/main-depos-sim-adc.jsonnet | 2 +- cfg/toyzero.jsonnet | 20 +++++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.org b/README.org index 2dd8c98..9f812a9 100644 --- a/README.org +++ b/README.org @@ -3,7 +3,7 @@ #+setupfile: https://ls4gan.github.io/other/setup-page.org #+options: toc:t -The toyzero package provides first steps toward [[https://ls4gan.github.io/][LS4GAN]]. +The toyzero package provides first steps toward [[https://ls4gan.github.io/][LS4GAN]]. See also [[file:next-steps.org]] * Goals :PROPERTIES: diff --git a/cfg/main-depos-sim-adc.jsonnet b/cfg/main-depos-sim-adc.jsonnet index 6aceacb..96b83d3 100644 --- a/cfg/main-depos-sim-adc.jsonnet +++ b/cfg/main-depos-sim-adc.jsonnet @@ -10,7 +10,7 @@ // -A input=depos.npz -A output=frames.npz \ // -A wires=wires-geometry.json.bz2 \ // -A resps=field-response.json.bz2 \ -// -A noise="yes" \ +// -A noisef=/path/to/noise/model/file \ // -c main-depos-sim-adc.jsonnet // // The 'noise' TLA is optional and defaults to "no". diff --git a/cfg/toyzero.jsonnet b/cfg/toyzero.jsonnet index ca3721a..bab57f6 100644 --- a/cfg/toyzero.jsonnet +++ b/cfg/toyzero.jsonnet @@ -111,7 +111,9 @@ local pg = import "pgraph.jsonnet"; // The vol, daq, adc, lar likely comes from params. // The noisef should be a file name. // fixme: probably should be broken up... - apasim(anode, pirs, vol, daq, adc, lar, noisef=null, rnd=$.random()) : { + apasim(anode, pirs, vol, daq, adc, lar, noisef=null, tier='adc', rnd=$.random()) : { + + local tagbase = if tier == 'adc' then 'orig' else 'volt', local apaid = anode.data.ident, @@ -150,7 +152,7 @@ local pg = import "pgraph.jsonnet"; name: 'Digitizer%d' % apaid, data : adc { anode: wc.tn(anode), - frame_tag: "orig%d"%apaid, + frame_tag: "%s%d"%[tagbase, apaid], } }, nin=1, nout=1, uses=[anode]), @@ -184,7 +186,7 @@ local pg = import "pgraph.jsonnet"; local mid = if std.type(noisef) == "null" then [] else [noise], - local end = [digitizer], + local end = if tier == 'adc' then [digitizer] else [], pipeline: pg.pipeline(beg + mid + end), }.pipeline, @@ -192,12 +194,12 @@ local pg = import "pgraph.jsonnet"; local plugins = [ "WireCellSio", "WireCellAux", "WireCellGen", "WireCellSigProc", - "WireCellApps", "WireCellPgraph"], + "WireCellApps", "WireCellPgraph", "WireCellTbb"], - main(graph) :: { - local app = { - type: 'Pgrapher', + main(graph, app) :: { + local appcfg = { + type: app, data: { edges: pg.edges(graph) }, @@ -206,9 +208,9 @@ local pg = import "pgraph.jsonnet"; type: "wire-cell", data: { plugins: plugins, - apps: ["Pgrapher"], + apps: [appcfg.type] } }, - seq: [cmdline] + pg.uses(graph) + [app], + seq: [cmdline] + pg.uses(graph) + [appcfg], }.seq } From 834129d5a42c56794a84bf2e0f93438eec488ff7 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Wed, 21 Jul 2021 15:07:00 -0400 Subject: [PATCH 02/15] Factor config further and start longer sigproc chain --- cfg/main-depos-sigproc.jsonnet | 71 +++++++++++++ cfg/main-depos-sim-adc.jsonnet | 21 ++-- cfg/nf.jsonnet | 152 +++++++++++++++++++++++++++ cfg/sp.jsonnet | 85 +++++++++++++++ cfg/toyzero.jsonnet | 184 +++++++++++++++++++-------------- 5 files changed, 424 insertions(+), 89 deletions(-) create mode 100644 cfg/main-depos-sigproc.jsonnet create mode 100644 cfg/nf.jsonnet create mode 100644 cfg/sp.jsonnet diff --git a/cfg/main-depos-sigproc.jsonnet b/cfg/main-depos-sigproc.jsonnet new file mode 100644 index 0000000..9edb6e2 --- /dev/null +++ b/cfg/main-depos-sigproc.jsonnet @@ -0,0 +1,71 @@ +// This is a main wire-cell configuration file. +// +// It implements the chain: +// (depos)->sim(signal,noise)->sigproc->(signals) +// +// It takes a number of top-level-arguments (TLA). +// +// Run like +// +// wire-cell \ +// -A input=depos.npz -A output=frames.npz \ +// -A wires=wires-geometry.json.bz2 \ +// -A resps=field-response.json.bz2 \ +// -A noisef=/path/to/noise/model/file \ +// -A app=[Pgrapher|TbbFlow] \ +// -c main-depos-sim-adc.jsonnet +// +// The 'noisef' TLA is optional and specifies a noise model. If not +// given, only signal is simulated. + +local wc = import "wirecell.jsonnet"; +local pg = import "pgraph.jsonnet"; + +local tz = import "toyzero.jsonnet"; +local io = import "ioutils.jsonnet"; +local nf = import "nf.jsonnet"; +local sp = import "sp.jsonnet"; + +function(input, output, wires, resps, noisef=null, app='Pgrapher') +local seeds = [0,1,2,3,4]; // maybe let CLI set? +local depos = io.npz_source_depo("depos", input); + +local params = import "pgrapher/experiment/pdsp/simparams.jsonnet"; +local spfilt = import "pgrapher/experiment/pdsp/sp-filters.jsonnet"; + +local wireobj = tz.wire_file(wires); +local anodes = tz.anodes(wireobj, params.det.volumes); + +local apaids = std.range(0, std.length(anodes)-1); + +local robjs = tz.responses(resps, params.elec, params.daq); + +local random = tz.random(seeds); + +local drifter = tz.drifter(params.det.volumes, params.lar, random); +local bagger = tz.bagger(params.daq); + +local sim(n) = + tz.sim(anodes[n], robjs.pirs, + params.daq, params.adc, params.lar, + noisef, 'adc', random); + +local adcpermv = tz.adcpermv(params.adc); +local nfsp(n) = + pg.pipeline([ + nf(anodes[n], robjs.fr, params.daq.nticks, params.daq.tick), + sp(anodes[n], robjs.fr, robjs.er, spfilt, adcpermv)]); + +local oneapa(n) = pg.pipeline([sim(n), nfsp(n)]); + +local pipes = [oneapa(n) for n in apaids]; + +local frame_tags = ["gaus%d"%n for n in apaids]; +local frames = io.frame_save_npz("frames", output, + digitize = false, tags=frame_tags); +local dump = io.frame_sink(); + +local body = pg.fan.pipe('DepoSetFanout', pipes, 'FrameFanin'); +local full = pg.pipeline([depos, drifter, bagger, body, frames, dump]); +tz.main(full, app) + diff --git a/cfg/main-depos-sim-adc.jsonnet b/cfg/main-depos-sim-adc.jsonnet index 96b83d3..63b09f6 100644 --- a/cfg/main-depos-sim-adc.jsonnet +++ b/cfg/main-depos-sim-adc.jsonnet @@ -21,36 +21,35 @@ local pg = import "pgraph.jsonnet"; local tz = import "toyzero.jsonnet"; local io = import "ioutils.jsonnet"; -function(input, output, wires, resps, noisef=null) +function(input, output, wires, resps, noisef=null, app='Pgrapher') local seeds = [0,1,2,3,4]; // maybe let CLI set? local depos = io.npz_source_depo("depos", input); -local params = tz.protodune_params; +local params = import "pgrapher/experiment/pdsp/simparams.jsonnet"; local wireobj = tz.wire_file(wires); local anodes = tz.anodes(wireobj, params.det.volumes); local apaids = std.range(0, std.length(anodes)-1); -local pirs = tz.pirs(resps, params.daq, params.elec); +local robjs = tz.responses(resps, params.elec, params.daq); local random = tz.random(seeds); local drifter = tz.drifter(params.det.volumes, params.lar, random); local bagger = tz.bagger(params.daq); -local apasim(n) = - tz.apasim(anodes[n], pirs, params.det.volumes[n], - params.daq, params.adc, params.lar, - noisef, random); - -local simpipes = [apasim(n) for n in apaids]; +local oneapa(n) = + tz.sim(anodes[n], robjs.pirs, + params.daq, params.adc, params.lar, + noisef, 'adc', random); +local pipes = [oneapa(n) for n in apaids]; local frames = io.frame_save_npz("frames", output); local dump = io.frame_sink(); -local simbody = pg.fan.pipe('DepoSetFanout', simpipes, 'FrameFanin'); +local simbody = pg.fan.pipe('DepoSetFanout', pipes, 'FrameFanin'); local full = pg.pipeline([depos, drifter, bagger, simbody, frames, dump]); -tz.main(full) +tz.main(full, app) diff --git a/cfg/nf.jsonnet b/cfg/nf.jsonnet new file mode 100644 index 0000000..7a10551 --- /dev/null +++ b/cfg/nf.jsonnet @@ -0,0 +1,152 @@ +// Produce a noise filter component for an anode + +// This can be arbitrarily hairy and probably best to make a new file +// if substantial changes are needed. + +local wc = import "wirecell.jsonnet"; +local pg = import "pgraph.jsonnet"; + +function(anode, fieldresp, nsamples, tick=0.5*wc.us, rms_cuts=[]) { + local apaid = anode.data.ident, + + local chndb = { + type: 'OmniChannelNoiseDB', + name: 'ocndbperfect%d' % apaid, + uses: [anode, fieldresp], + data: { + anode: wc.tn(anode), + field_response: wc.tn(fieldresp), + tick: tick, + + // This sets the number of frequency-domain bins used in the noise + // filtering. It is not necessarily true that the time-domain + // waveforms have the same number of ticks. This must be non-zero. + nsamples: nsamples, + + // Group channels into their domains of coherency + groups: [ + std.range(apaid * 2560 + u * 40, apaid * 2560 + (u + 1) * 40 - 1) + for u in std.range(0, 19) + ] + [ + std.range(apaid * 2560 + 800 + v * 40, apaid * 2560 + 800 + (v + 1) * 40 - 1) + for v in std.range(0, 19) + ] + [ + std.range(apaid * 2560 + 1600 + w * 48, apaid * 2560 + 1600 + (w + 1) * 48 - 1) + for w in std.range(0, 19) + ], + + + // Overide defaults for specific channels. If an info is + // mentioned for a particular channel in multiple objects in this + // list then last mention wins. + channel_info: [ + // First entry provides default channel info across ALL + // channels. Subsequent entries override a subset of channels + // with a subset of these entries. There's no reason to + // repeat values found here in subsequent entries unless you + // wish to change them. + { + channels: std.range(apaid * 2560, (apaid + 1) * 2560 - 1), + nominal_baseline: 2048.0, // adc count + gain_correction: 1.0, // unitless + response_offset: 0.0, // ticks? + pad_window_front: 10, // ticks? + pad_window_back: 10, // ticks? + decon_limit: 0.02, + decon_limit1: 0.09, + adc_limit: 15, + roi_min_max_ratio: 0.8, // default 0.8 + min_rms_cut: 1.0, // units??? + max_rms_cut: 30.0, // units??? + + // parameter used to make "rcrc" spectrum + rcrc: 1.1 * wc.millisecond, // 1.1 for collection, 3.3 for induction + rc_layers: 1, // default 2 + + // parameters used to make "config" spectrum + reconfig: {}, + + // list to make "noise" spectrum mask + freqmasks: [], + + // field response waveform to make "response" spectrum. + response: {}, + }, + { + //channels: { wpid: wc.WirePlaneId(wc.Ulayer) }, + channels: std.range(apaid * 2560, apaid * 2560 + 800- 1), + response: { wpid: wc.WirePlaneId(wc.Ulayer) }, + response_offset: 120, // offset of the negative peak + pad_window_front: 20, + decon_limit: 0.02, + decon_limit1: 0.07, + roi_min_max_ratio: 3.0, + }, + { + //channels: { wpid: wc.WirePlaneId(wc.Vlayer) }, + channels: std.range(apaid * 2560 + 800, apaid * 2560 + 1600- 1), + response: { wpid: wc.WirePlaneId(wc.Vlayer) }, + response_offset: 124, + decon_limit: 0.01, + decon_limit1: 0.08, + roi_min_max_ratio: 1.5, + }, + { + //channels: { wpid: wc.WirePlaneId(wc.Wlayer) }, + channels: std.range(apaid * 2560 + 1600, apaid * 2560 + 2560- 1), + nominal_baseline: 400.0, + decon_limit: 0.05, + decon_limit1: 0.08, + }, + ] + rms_cuts, + }, // data + }, // chndb + + local single = { + type: 'pdOneChannelNoise', + name: 'ocn%d'%apaid, + data: { + noisedb: wc.tn(chndb), + anode: wc.tn(anode), + resmp: [ + {channels: std.range(2128, 2175), sample_from: 5996}, + {channels: std.range(1520, 1559), sample_from: 5996}, + {channels: std.range( 440, 479), sample_from: 5996}, + ], + }, + }, + + // In principle there may be multiple filters of each of each + // type. Define them above and collect them by type here. + local filters = { + channel: [ single, ], + group: [ ], + status: [ ], + }, + + local obnf = pg.pnode({ + + + type: 'OmnibusNoiseFilter', + name: 'nf%d'%apaid, + data: { + + // Nonzero forces the number of ticks in the waveform + nticks: 0, + + // channel bin ranges are ignored + // only when the channelmask is merged to `bad` + maskmap: {sticky: "bad", ledge: "bad", noisy: "bad"}, + channel_filters: [wc.tn(f) for f in filters.channel], + grouped_filters: [wc.tn(f) for f in filters.group], + channel_status_filters: [wc.tn(f) for f in filters.status], + noisedb: wc.tn(chndb), + intraces: 'orig%d' % apaid, // frame tag get all traces + outtraces: 'raw%d' % apaid, + }, + }, uses=[chndb, anode]+filters.channel+filters.group+filters.status, nin=1, nout=1), + + res: obnf + +}.res + diff --git a/cfg/sp.jsonnet b/cfg/sp.jsonnet new file mode 100644 index 0000000..a5928e4 --- /dev/null +++ b/cfg/sp.jsonnet @@ -0,0 +1,85 @@ +local wc = import "wirecell.jsonnet"; +local pg = import "pgraph.jsonnet"; + + +// Signal processing. +// +// Note, spfilt are a list of filter objects which MUST match +// hard-wired names in the C++, sorry. See, eg +// pgrapher/experiment/pdsp/sp-filters.jsonnet. +function(anode, fieldresp, elecresp, spfilt, adcpermv, perchan=null) { + local apaid = anode.data.ident, + + // if perchan file name is given we need to add this to a + // "uses" list and to set the name in OmnibusSigProc. + local pcresp = { + type: "PerChannelResponse", + data: { + filename: perchan + } + }, + local pcname = if std.type(perchan) == 'null' then "" else wc.tn(pcresp), + local pcuses = if std.type(perchan) == 'null' then [] else [pcresp], + + + local sigproc = pg.pnode({ + type: 'OmnibusSigProc', + name: 'sigproc%d' % apaid, + data: { + /** + * Optimized SP parameters (May 2019) + * Associated tuning in sp-filters.jsonnet + */ + anode: wc.tn(anode), + field_response: wc.tn(fieldresp), + elecresponse: wc.tn(elecresp), + ftoffset: 0.0, // default 0.0 + ctoffset: 1.0*wc.microsecond, // default -8.0 + per_chan_resp: pcname, + fft_flag: 0, // 1 is faster but higher memory, 0 is slightly slower but lower memory + postgain: 1.0, // default 1.2 + ADC_mV: adcpermv, // 4096 / (1400.0 * wc.mV), + troi_col_th_factor: 5.0, // default 5 + troi_ind_th_factor: 3.0, // default 3 + lroi_rebin: 6, // default 6 + lroi_th_factor: 3.5, // default 3.5 + lroi_th_factor1: 0.7, // default 0.7 + lroi_jump_one_bin: 1, // default 0 + + r_th_factor: 3.0, // default 3 + r_fake_signal_low_th: 375, // default 500 + r_fake_signal_high_th: 750, // default 1000 + r_fake_signal_low_th_ind_factor: 1.0, // default 1 + r_fake_signal_high_th_ind_factor: 1.0, // default 1 + r_th_peak: 3.0, // default 3.0 + r_sep_peak: 6.0, // default 6.0 + r_low_peak_sep_threshold_pre: 1200, // default 1200 + + // frame tags + wiener_tag: 'wiener%d' % apaid, + wiener_threshold_tag: 'threshold%d' % apaid, + decon_charge_tag: 'decon_charge%d' % apaid, + gauss_tag: 'gauss%d' % apaid, + + use_roi_debug_mode: false, + tight_lf_tag: 'tight_lf%d' % apaid, + loose_lf_tag: 'loose_lf%d' % apaid, + cleanup_roi_tag: 'cleanup_roi%d' % apaid, + break_roi_loop1_tag: 'break_roi_1st%d' % apaid, + break_roi_loop2_tag: 'break_roi_2nd%d' % apaid, + shrink_roi_tag: 'shrink_roi%d' % apaid, + extend_roi_tag: 'extend_roi%d' % apaid, + + use_multi_plane_protection: false, + mp3_roi_tag: 'mp3_roi%d' % apaid, + mp2_roi_tag: 'mp2_roi%d' % apaid, + + isWrapped: false, + sparse: false, + // process_planes: [0, 2], + } + }, nin=1, nout=1, uses=[anode, fieldresp, elecresp] + pcuses + spfilt), + + res: sigproc +}.res + diff --git a/cfg/toyzero.jsonnet b/cfg/toyzero.jsonnet index bab57f6..4445cd1 100644 --- a/cfg/toyzero.jsonnet +++ b/cfg/toyzero.jsonnet @@ -5,7 +5,6 @@ local pg = import "pgraph.jsonnet"; // define general object for toyzero { - protodune_params: import "pgrapher/experiment/pdsp/simparams.jsonnet", // return a "wireobj" wire_file(filename) : { @@ -25,55 +24,66 @@ local pg = import "pgraph.jsonnet"; } for vol in volumes], - // Return "PlaneImpactResponse" objects. daq and elec likely come - // from params. - pirs(respf, daq, elec): { - - local field_resp = { - type: "FieldResponse", - name: respf, - data: { - filename: respf - } - }, - - local binning = { nticks: daq.nticks, tick: daq.tick }, - - local rc_resp = { - type: "RCResponse", - data: binning { - width: 1.0*wc.ms, - } - }, - - local elec_resp = { - type: "ColdElecResponse", - data: binning { - shaping: elec.shaping, - gain: elec.gain, - postgain: elec.postgain, - }, - }, + field_response(filename) :: { + type: "FieldResponse", + data: { filename: filename } + }, + elec_response(shaping, gain, postgain, nticks, tick=0.5*wc.us) :: { + type: "ColdElecResponse", + data: { + shaping: shaping, + gain: gain, + postgain: postgain, + nticks: nticks, + tick: tick, + }, + }, + rc_response(width, nticks, tick=0.5*wc.us) :: { + type: "RCResponse", + data: { + width: width, + nticks: nticks, + tick: tick, + } + }, + + // Return "PlaneImpactResponse" objects. + // fr is field_response object. + // srs is list of short response objects, eg elec_resp + // lrs is list of long response objects, eg rc_response + pirs(fr, srs, lrs): { ret: [ { type: "PlaneImpactResponse", name : "PIRplane%d" % plane, data : { plane: plane, - field_response: wc.tn(field_resp), - short_responses: [wc.tn(elec_resp)], + field_response: wc.tn(fr), + short_responses: [wc.tn(r) for r in srs], // this needs to be big enough for convolving FR*CE overall_short_padding: 200*wc.us, - long_responses: [wc.tn(rc_resp)], + long_responses: [wc.tn(r) for r in lrs], // this needs to be big enough to convolve RC long_padding: 1.5*wc.ms, }, - uses: [field_resp, elec_resp, rc_resp], + uses: [fr] + srs + lrs, } for plane in [0,1,2]], - }.ret, + adcpermv(adc) :: + ((1 << adc.resolution)-1) / (adc.fullscale[1]-adc.fullscale[0]), + + responses(frfile, elec, daq, width=1.0*wc.ms) :: { + + fr: $.field_response(frfile), + er: $.elec_response(elec.shaping, elec.gain, elec.postgain, + daq.nticks, daq.tick), + rc: $.rc_response(width, daq.nticks, daq.tick), + pirs: $.pirs(self.fr, [self.er], [self.rc]) + }, + + default_seeds: [0, 1, 2, 3, 4], random(seeds = $.default_seeds) : { type: "Random", @@ -105,21 +115,53 @@ local pg = import "pgraph.jsonnet"; }, nin=1, nout=1), + // A per anode configure node for simulating noise. + noisesim(anode, noisef, daq, csdb=null, rnd=$.random()) : { + local apaid = anode.data.ident, + + local noise_model = { + type: "EmpiricalNoiseModel", + name: "emperical-noise-model-%d" % apaid, + data: { + anode: wc.tn(anode), + chanstat: if std.type(csdb) == "null" then "" else wc.tn(csdb), + spectra_file: noisef, + nsamples: daq.nticks, + period: daq.tick, + wire_length_scale: 1.0*wc.cm, // optimization binning + }, + uses: [anode] + if std.type(csdb) == "null" then [] else [csdb], + }, + ret: pg.pnode({ + type: "AddNoise", + name: "addnoise-" + noise_model.name, + data: { + rng: wc.tn(rnd), + model: wc.tn(noise_model), + nsamples: daq.nticks, + replacement_percentage: 0.02, // random optimization + }}, nin=1, nout=1, uses=[rnd, noise_model]), + }.ret, - // A pipeline of nodes to simulate one APA. - // - // The vol, daq, adc, lar likely comes from params. - // The noisef should be a file name. - // fixme: probably should be broken up... - apasim(anode, pirs, vol, daq, adc, lar, noisef=null, tier='adc', rnd=$.random()) : { + digisim(anode, adc) :: { + local apaid = anode.data.ident, + ret: pg.pnode({ + type: "Digitizer", + name: 'Digitizer%d' % apaid, + data : adc { + anode: wc.tn(anode), + frame_tag: "orig%d"%apaid, + } + }, nin=1, nout=1, uses=[anode]), + }.ret, - local tagbase = if tier == 'adc' then 'orig' else 'volt', + sigsim(anode, pirs, daq, lar, rnd=$.random()) : { local apaid = anode.data.ident, local ductor = pg.pnode({ type:'DepoTransform', - name:'DepoTransformt%d' % apaid, + name:'DepoTransform%d' % apaid, data: { rng: wc.tn(rnd), anode: wc.tn(anode), @@ -147,50 +189,36 @@ local pg = import "pgraph.jsonnet"; }, }, nin=1, nout=1), - local digitizer = pg.pnode({ - type: "Digitizer", - name: 'Digitizer%d' % apaid, - data : adc { - anode: wc.tn(anode), - frame_tag: "%s%d"%[tagbase, apaid], - } - }, nin=1, nout=1, uses=[anode]), + ret: pg.pipeline([ductor, reframer]), + }.ret, - + // A kitchen sink pipeline of nodes to simulate one APA. + // + // The daq, adc, lar likely comes from params. + // + // Fullest chain is sig + noise -> adc + // + // If no lar or pirs is given, then no signal. + // If no noisef given, then no noise. + // If no adc given, then no digitizer + // The tier can be 'adc' or something else if no digitizer. + sim(anode, pirs, daq, adc, lar, noisef=null, tier='adc', rnd=$.random()) : { + + // fixme: make configurable local csdb = null, - local noise_model = { - type: "EmpiricalNoiseModel", - name: "emperical-noise-model-%d" % apaid, - data: { - anode: wc.tn(anode), - chanstat: if std.type(csdb) == "null" then "" else wc.tn(csdb), - spectra_file: noisef, - nsamples: daq.nticks, - period: daq.tick, - wire_length_scale: 1.0*wc.cm, // optimization binning - }, - uses: [anode] + if std.type(csdb) == "null" then [] else [csdb], - }, - local noise = pg.pnode({ - type: "AddNoise", - name: "addnoise-" + noise_model.name, - data: { - rng: wc.tn(rnd), - model: wc.tn(noise_model), - nsamples: daq.nticks, - replacement_percentage: 0.02, // random optimization - }}, nin=1, nout=1, uses=[rnd, noise_model]), - - local beg = [ductor, reframer], + local beg = if std.type(lar) == "null" || std.type(pirs) == "null" then [] else [ + $.sigsim(anode, pirs, daq, lar, rnd)], - local mid = if std.type(noisef) == "null" then [] else [noise], + local mid = if std.type(noisef) == "null" then [] else [ + $.noisesim(anode, noisef, daq, rnd, csdb)], - local end = if tier == 'adc' then [digitizer] else [], + local end = if tier == 'adc' then [$.digisim(anode, adc)] else [], pipeline: pg.pipeline(beg + mid + end), }.pipeline, + local plugins = [ "WireCellSio", "WireCellAux", "WireCellGen", "WireCellSigProc", From aa660bb2eaa67b3ba798cc62e3bc8163caa33401 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Wed, 21 Jul 2021 16:49:50 -0400 Subject: [PATCH 03/15] Sigproc working and with per-apa output files --- README.org | 30 +- Snakefile | 73 +- cfg/main-depos-sigproc.jsonnet | 29 +- index.html | 75 +- toyzero-all-dag.dot | 410 +++--- toyzero-all-dag.pdf | Bin 26783 -> 27329 bytes toyzero-all-dag.svg | 2436 ++++++++++++++++---------------- 7 files changed, 1565 insertions(+), 1488 deletions(-) diff --git a/README.org b/README.org index 00b1488..228ea67 100644 --- a/README.org +++ b/README.org @@ -291,6 +291,28 @@ The ~all_frames~ target generates "frame" data from depos by running the Wire-Cell simulation. The "frame" file format is described elsewhere. For here, we treat it as a temporary. +The simulation is internally structured as a DAG of components +exchanging data flow as shown: + +#+ATTR_HTML: :width 90% +[[file:plots/dots/depos-sim-adc/dag.png]] + +Frames come in different flavor depending on the final output data +tier. We name and describe them as: + +- noiseless :: these are integer ADC waveforms reflecting existence of + only ionization electrons and no electronics noise. They are + bipolar on the induction planes "U" and "V and unipolar on + collection plane "W". They have a fixed "baseline" at an ADC value + depending on the plane and are sparse (baseline-padded) at locations + away from any ionization. + +- signal :: these floating point measures of the original ionization + electrons after signal processing and noise filtering is applied to + signal and noise simulation. The waveforms from all planes are + unipolar and give a measure of the original ionization distribution. + The waveforms have a "baseline" of 0.0 and are sparse + (baseline-padded) at locations away from any ionization. ** images :PROPERTIES: @@ -308,8 +330,8 @@ match. For example: protodune-orig-0-1-W (960, 6000) #+end_example -These image arrays have shape ~(nchan, ntick)~. That is each row is the -waveform from one channel and has ~ntick=6000~ samples (3ms). When +These image arrays have shape ~(nchan, ntick)~. That is, each row is +the waveform from one channel and has ~ntick=6000~ samples (3ms). When visualized with matplotlib's ~imshow()~ you will see channels as Y-axis, tics as X-axis. @@ -319,6 +341,10 @@ This "W" file holds one array of 960 channels and 6000 sample time induction planes and each will have 800 channels and the contemporaneous 6000 ticks. +Like frames above, images come in corresponding flavors. See section +[[frames]] for description. + + * Plotting :PROPERTIES: :CUSTOM_ID: plotting diff --git a/Snakefile b/Snakefile index 3f87317..371699b 100644 --- a/Snakefile +++ b/Snakefile @@ -22,9 +22,13 @@ seed = config.get("seed", "1,2,3,4") ntracks = config.get("ntracks", 10) nevents = config.get("nevents", 10) wcloglvl = config.get("wcloglvl", "info") + +tiers = config.get("tiers", ["noiseless"]) + # print(f"OUTDIR:{outdir}") # print(f"NEVENTS:{nevents}") # print(f"WCLOGLVL:{wcloglvl}") +print(f"TIERS:{tiers}, {type(tiers)}") # The rest are hard wired for now wcdata_url = "https://github.com/WireCell/wire-cell-data/raw/master" @@ -39,13 +43,20 @@ wcdata_ext = "json.bz2" resps = "dune-garfield-1d565" wires = "protodune-wires-larsoft-v4" +# The data domains describe a universe. For toyzero, they are +# associated with a particular detector response. +DOMAINS = ["fake", "real"] + +# The data tiers for frame or image +TIERS = ["noiseless", "signal"] + # some important file names real_resps = f'{datadir}/resps/real-resps.{wcdata_ext}' fake_resps = f'{datadir}/resps/fake-resps.{wcdata_ext}' domain_resps = f'{datadir}/resps/{{domain}}-resps.{wcdata_ext}' wires_file = f'{datadir}/wires/wires.{wcdata_ext}' depos_file = f'{datadir}/depos/depos.npz' -domain_frames = f'{datadir}/frames/{{domain}}-frames.npz' +domain_frames = f'{datadir}/frames/{{tier}}/{{domain}}-frames.npz' # resp - prepare response files @@ -167,15 +178,24 @@ rule all_depos: # frames -rule sim_dots: +def wct_cfg_file(w): + slugs = dict(signal="depos-sigproc", + noiseless="depos-sim-adc") + slug = slugs[w.tier] + return f'cfg/main-{slug}.jsonnet' + + +# note, passes bogus TLAs so don't use the genearted json! +rule wct_dots: input: - config = 'cfg/main-depos-sim-adc.jsonnet' + config = wct_cfg_file output: - json = f'{plotdir}/sim-graph.json', - dot = f'{plotdir}/sim-graph.dot', - png = f'{plotdir}/sim-graph.png', - pdf = f'{plotdir}/sim-graph.pdf' + json = temp(f'{plotdir}/dots/{{tier}}/cfg.json'), + dot = temp(f'{plotdir}/dots/{{tier}}/dag.dot'), + png = f'{plotdir}/dots/{{tier}}/dag.png', + pdf = f'{plotdir}/dots/{{tier}}/dag.pdf' shell: ''' + mkdir -p {plotdir}/dots/{wildcards.tier}; wcsonnet \ -P cfg \ -A input=DEPOS-FILE \ @@ -183,17 +203,20 @@ rule sim_dots: -A wires=WIRES-FILE \ -A resps=RESPS-FILE \ {input.config} > {output.json}; - wirecell-pgraph dotify --jpath=-1 {output.json} {output.dot} ; + wirecell-pgraph dotify --no-params --jpath=-1 {output.json} {output.dot} ; dot -Tpng -o {output.png} {output.dot} ; dot -Tpdf -o {output.pdf} {output.dot} ''' +rule all_dots: + input: + expand(rules.wct_dots.output, tier=tiers) rule sim_frames: input: wires = wires_file, resps = domain_resps, depos = depos_file, - config = 'cfg/main-depos-sim-adc.jsonnet' + config = wct_cfg_file output: frames = temp(domain_frames) shell: ''' @@ -216,7 +239,7 @@ rule plot_frames: input: domain_frames output: - f'{plotdir}/frames-{{domain}}-apa{{apa}}.{{ext}}' + f'{plotdir}/frames-{{tier}}-{{domain}}-apa{{apa}}.{{ext}}' params: p = gen_plot_frames shell:''' @@ -224,9 +247,9 @@ rule plot_frames: ''' rule plot_frames_hidpi: input: - f'{plotdir}/frames-{{domain}}-apa{{apa}}.pdf' + f'{plotdir}/frames-{{tier}}-{{domain}}-apa{{apa}}.pdf' output: - f'{plotdir}/hidpi/frames-{{domain}}-apa{{apa}}.png' + f'{plotdir}/hidpi/frames-{{tier}}-{{domain}}-apa{{apa}}.png' params: shell:''' @@ -236,11 +259,13 @@ rule plot_frames_hidpi: rule all_frames: input: - expand(rules.sim_frames.output, domain=["real","fake"]), + expand(rules.sim_frames.output, domain=["real","fake"], + tier=tiers), expand(rules.plot_frames.output, domain=["real","fake"], + tier=tiers, ext=["png","pdf"], apa=list(range(6))), expand(rules.plot_frames_hidpi.output, domain=["real","fake"], - apa=[0]) + tier=tiers, apa=[0]) @@ -255,7 +280,7 @@ rule all_frames: ## static data (ie, defined right here). So, that is what we do. # split_outer_product = dict( - domain = ["protodune"], +# domain = ["protodune"], event = list(range(nevents)), apa = list(range(6)), plane = ["U","V","W"], @@ -265,11 +290,11 @@ rule split_images: input: domain_frames output: - expand(datadir+'/images/{{domain}}/protodune-orig-{event}-{apa}-{plane}.npz', + expand(datadir+'/images/{{tier}}/{{domain}}/protodune-orig-{event}-{apa}-{plane}.npz', **split_outer_product) shell: ''' wirecell-util frame-split \ - -f {datadir}/images/{wildcards.domain}/{{detector}}-{{tag}}-{{index}}-{{anodeid}}-{{planeletter}}.npz \ + -f {datadir}/images/{wildcards.tier}/{wildcards.domain}/{{detector}}-{{tag}}-{{index}}-{{anodeid}}-{{planeletter}}.npz \ {input} ''' @@ -278,16 +303,16 @@ def gen_title(w): dim='2D' else: dim='q1D' - return f'"{w.domain}/{dim}, event {w.event}, APA {w.apa}, {w.plane} plane"', + return f'"{w.tier} {w.domain}/{dim}, event {w.event}, APA {w.apa}, {w.plane} plane"', ## Note, we must match the input here by hand to the output above ## because the domain is not included in the expand above but is here. ## Above is 1->N, here is 1->1. rule plot_split_images: input: - datadir+'/images/{domain}/protodune-orig-{event}-{apa}-{plane}.npz', + datadir+'/images/{tier}/{domain}/protodune-orig-{event}-{apa}-{plane}.npz', output: - plotdir+'/images/{domain}/{cmap}/protodune-orig-{event}-{apa}-{plane}.{ext}' + plotdir+'/images/{tier}/{domain}/{cmap}/protodune-orig-{event}-{apa}-{plane}.{ext}' params: title = gen_title shell: ''' @@ -302,10 +327,12 @@ rule plot_split_images: ## note, list-of-list for the split_images rule rule all_images: input: - expand(rules.split_images.output, domain=["real","fake"]), + expand(rules.split_images.output, + domain=["real","fake"], tier=tiers), expand( rules.plot_split_images.output, domain = ["real","fake"], + tier = tiers, event = [0], apa=[2], plane=["U"], ext = ["png", "pdf", "svg"], cmap = ["seismic", "Spectral", "terrain", "coolwarm", "viridis"], @@ -313,7 +340,9 @@ rule all_images: rule just_images: input: - expand(rules.split_images.output, domain=["real","fake"]), + expand(rules.split_images.output, + domain=["real","fake"], + tier=tiers) rule all: input: diff --git a/cfg/main-depos-sigproc.jsonnet b/cfg/main-depos-sigproc.jsonnet index 9edb6e2..847afca 100644 --- a/cfg/main-depos-sigproc.jsonnet +++ b/cfg/main-depos-sigproc.jsonnet @@ -53,19 +53,32 @@ local sim(n) = local adcpermv = tz.adcpermv(params.adc); local nfsp(n) = pg.pipeline([ - nf(anodes[n], robjs.fr, params.daq.nticks, params.daq.tick), + //nf(anodes[n], robjs.fr, params.daq.nticks, params.daq.tick), sp(anodes[n], robjs.fr, robjs.er, spfilt, adcpermv)]); -local oneapa(n) = pg.pipeline([sim(n), nfsp(n)]); +local outfile(n) = { + local l = std.split(output,"."), + ret:"%s-apa%d.%s"%[l[0],n,l[1]] +}.ret; +local oneapa(n) = { + local name = "gauss%d"%n, + local outf = outfile(n), + pipe: pg.pipeline([ + sim(n), nfsp(n), + io.frame_save_npz(name, outf, digitize=false, tags=[name]), + io.frame_sink(name)]) +}.pipe; local pipes = [oneapa(n) for n in apaids]; -local frame_tags = ["gaus%d"%n for n in apaids]; -local frames = io.frame_save_npz("frames", output, - digitize = false, tags=frame_tags); -local dump = io.frame_sink(); +// local frame_tags = ["gauss%d"%n for n in apaids]; +// local frames = io.frame_save_npz("frames", output, +// digitize = false, tags=frame_tags); +// local dump = io.frame_sink(); +// local body = pg.fan.pipe('DepoSetFanout', pipes, 'FrameFanin', +// outtags=frame_tags); -local body = pg.fan.pipe('DepoSetFanout', pipes, 'FrameFanin'); -local full = pg.pipeline([depos, drifter, bagger, body, frames, dump]); +local body = pg.fan.fanout('DepoSetFanout', pipes); +local full = pg.pipeline([depos, drifter, bagger, body]); tz.main(full, app) diff --git a/index.html b/index.html index e39dfc5..d2b8e5c 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - + LS4GAN's Toy Zero @@ -257,7 +257,7 @@

Table of Contents

-The toyzero package provides first steps toward LS4GAN. +The toyzero package provides first steps toward LS4GAN. See also next-steps.html

@@ -334,7 +334,7 @@

2.1 Environment

A one time setup of direnv:

-
+
 $ cd toyzero
 $ cp dot.envrc .envrc
 $ emacs .envrc  # edit to taste
@@ -352,7 +352,7 @@ 

2.2 Check Wire-Cell Toolki Check if WCT is available with these commands:

-
+
 $ wire-cell --help
 $ wcsonnet wirecell.jsonnet
 $ echo $WIRECELL_PATH
@@ -374,7 +374,7 @@ 

2.3 Python

various other Python 3 packages are required.

-
+
 $ cd toyzero
 $ pip install -r requirements.txt
 
@@ -383,7 +383,7 @@

2.3 Python

Some checks:

-
+
 $ wirecell-<TAB>
 $ wirecell-util --help
 $ snakemake --help
@@ -406,7 +406,7 @@ 

3 Usage

need run only:

-
+
 $ snakemake -jall all
 
@@ -415,7 +415,7 @@

3 Usage

To see what is produced:

-
+
 $ tree data plots
 
@@ -424,7 +424,7 @@

3 Usage

you can do:

-
+
 $ snakemake -jall just_images
 
@@ -435,7 +435,7 @@

3 Usage

may run as:

-
+
 $ snakemake -jall just_images --notemp
 
@@ -460,7 +460,7 @@

4 Details

manner:

-
+
 $ snakemake -jall all_resp
 $ snakemake -jall all_wires
 $ snakemake -jall all_depos
@@ -506,7 +506,7 @@ 

4.1 resp

-
+

fake-resps-diagnostic.png

@@ -516,7 +516,7 @@

4.1 resp

-
+

real-resps-diagnostic.png

@@ -549,7 +549,7 @@

4.3 depos

for input to the WCT simulation. It produces a file:

-
+
 data/depos/depos.npz
 
@@ -557,7 +557,7 @@

4.3 depos

The file is in Numpy format with three types of arrays named like:

-
+
 depo_data_<N>
 depo_info_<N>
 track_info_<N>
@@ -590,7 +590,7 @@ 

4.3 depos

-
+

depos-diagnostic.png

@@ -624,9 +624,18 @@

4.4 frames

Wire-Cell simulation. The "frame" file format is described elsewhere. For here, we treat it as a temporary.

+ +

+The simulation is internally structured as a DAG as shown: +

+ + +
+

dag.png +

+
-

4.5 images

@@ -638,7 +647,7 @@

4.5 images

match. For example:

-
+
 ❯ wirecell-util npzls data/images/real/protodune-orig-0-1-W.npz 
 protodune-orig-0-1-W (960, 6000)
 
@@ -698,7 +707,7 @@

5.1 Image formats

-
+

frames-fake-apa0.png

@@ -708,7 +717,7 @@

5.1 Image formats

-
+

frames-fake-apa0.png

@@ -717,7 +726,7 @@

5.1 Image formats

If you need more zoom from your evince PDF viewer try

-
+
 ❯ gsettings set org.gnome.Evince page-cache-size 2014
 
@@ -747,7 +756,7 @@

5.2 Color maps

as command line options:

-
+
 ❯ wirecell-util npz-to-img --help
 Usage: wirecell-util npz-to-img [OPTIONS] NPZFILE
 
@@ -782,7 +791,7 @@ 

5.2 Color maps

-
+

protodune-orig-0-2-U.png

@@ -792,7 +801,7 @@

5.2 Color maps

-
+

protodune-orig-0-2-U.png

@@ -802,7 +811,7 @@

5.2 Color maps

-
+

protodune-orig-0-2-U.png

@@ -812,7 +821,7 @@

5.2 Color maps

-
+

protodune-orig-0-2-U.png

@@ -833,7 +842,7 @@

5.3 Real vs Fake

-
+

Sorry, your browser does not support SVG.

@@ -845,7 +854,7 @@

5.3 Real vs Fake

-
+

Sorry, your browser does not support SVG.

@@ -891,7 +900,7 @@

6 Configuration

To customize,

-
+
 $ cp toyzero.yaml mycfg.yaml
 $ emacs mycfg.yaml
 $ snakemake -jall --configfile mycfg.yaml all
@@ -907,7 +916,7 @@ 

6 Configuration

You may also customize specific parameters from the command line:

-
+
 $ snakemake -jall --config outdir=junk ntracks=10 all_depos
 $ eom junk/plots/depos-diagnostic.png 
 
@@ -948,7 +957,7 @@

7 Snakemake

-
+

Sorry, your browser does not support SVG.

@@ -974,7 +983,7 @@

8 Hacking

so like:

-
+
 $ git clone git@github.com:wirecell/wire-cell-python.git
 $ cd wire-cell-python/
 $ pip install -U -e .
@@ -990,7 +999,7 @@ 

8 Hacking

Author: BV

-

Created: 2021-07-20 Tue 10:49

+

Created: 2021-07-21 Wed 15:18

Validate

diff --git a/toyzero-all-dag.dot b/toyzero-all-dag.dot index ba373d9..3c12237 100644 --- a/toyzero-all-dag.dot +++ b/toyzero-all-dag.dot @@ -2,214 +2,214 @@ digraph snakemake_dag { graph[bgcolor=white, margin=0]; node[shape=box, style=rounded, fontname=sans, fontsize=10, penwidth=2]; edge[penwidth=2, color=grey]; - 0[label = "just_images", color = "0.17 0.6 0.85", style="rounded"]; - 1[label = "split_images", color = "0.37 0.6 0.85", style="rounded"]; - 2[label = "sim_frames\ndomain: real", color = "0.60 0.6 0.85", style="rounded"]; - 3[label = "get_wires", color = "0.43 0.6 0.85", style="rounded"]; - 4[label = "get_resp_real", color = "0.13 0.6 0.85", style="rounded"]; - 5[label = "gen_depos", color = "0.23 0.6 0.85", style="rounded"]; - 6[label = "split_images", color = "0.37 0.6 0.85", style="rounded"]; - 7[label = "sim_frames\ndomain: fake", color = "0.60 0.6 0.85", style="rounded"]; - 8[label = "gen_resp_fake", color = "0.30 0.6 0.85", style="rounded"]; - 9[label = "all", color = "0.27 0.6 0.85", style="rounded"]; - 10[label = "plot_resp\ndomain: real", color = "0.57 0.6 0.85", style="rounded"]; - 11[label = "plot_resp\ndomain: fake", color = "0.57 0.6 0.85", style="rounded"]; - 12[label = "plot_wires", color = "0.07 0.6 0.85", style="rounded"]; - 13[label = "plot_depos", color = "0.40 0.6 0.85", style="rounded"]; - 14[label = "plot_frames\napa: 0\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 15[label = "plot_frames\napa: 1\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 16[label = "plot_frames\napa: 2\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 17[label = "plot_frames\napa: 3\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 18[label = "plot_frames\napa: 4\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 19[label = "plot_frames\napa: 5\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 20[label = "plot_frames\napa: 0\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 21[label = "plot_frames\napa: 1\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 22[label = "plot_frames\napa: 2\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 23[label = "plot_frames\napa: 3\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 24[label = "plot_frames\napa: 4\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 25[label = "plot_frames\napa: 5\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 26[label = "plot_frames\napa: 0\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 27[label = "plot_frames\napa: 1\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 28[label = "plot_frames\napa: 2\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 29[label = "plot_frames\napa: 3\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 30[label = "plot_frames\napa: 4\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 31[label = "plot_frames\napa: 5\next: png", color = "0.00 0.6 0.85", style="rounded"]; - 32[label = "plot_frames\napa: 0\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 33[label = "plot_frames\napa: 1\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 34[label = "plot_frames\napa: 2\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 35[label = "plot_frames\napa: 3\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 36[label = "plot_frames\napa: 4\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 37[label = "plot_frames\napa: 5\next: pdf", color = "0.00 0.6 0.85", style="rounded"]; - 38[label = "plot_frames_hidpi", color = "0.33 0.6 0.85", style="rounded"]; - 39[label = "plot_frames_hidpi", color = "0.33 0.6 0.85", style="rounded"]; - 40[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 41[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 42[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 43[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 44[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 45[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 46[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 47[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 48[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 49[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 50[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 51[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 52[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 53[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 54[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 55[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 56[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 57[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 58[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 59[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 60[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 61[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 62[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 63[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 64[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 65[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 66[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 67[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 68[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; - 69[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 0[label = "all", color = "0.38 0.6 0.85", style="rounded"]; + 1[label = "get_resp_real", color = "0.00 0.6 0.85", style="rounded"]; + 2[label = "gen_resp_fake", color = "0.03 0.6 0.85", style="rounded"]; + 3[label = "plot_resp\ndomain: real", color = "0.60 0.6 0.85", style="rounded"]; + 4[label = "plot_resp\ndomain: fake", color = "0.60 0.6 0.85", style="rounded"]; + 5[label = "get_wires", color = "0.54 0.6 0.85", style="rounded"]; + 6[label = "plot_wires", color = "0.48 0.6 0.85", style="rounded"]; + 7[label = "gen_depos", color = "0.06 0.6 0.85", style="rounded"]; + 8[label = "plot_depos", color = "0.25 0.6 0.85", style="rounded"]; + 9[label = "sim_frames\ndomain: real", color = "0.19 0.6 0.85", style="rounded"]; + 10[label = "sim_frames\ndomain: fake", color = "0.19 0.6 0.85", style="rounded"]; + 11[label = "plot_frames\napa: 0\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 12[label = "plot_frames\napa: 1\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 13[label = "plot_frames\napa: 2\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 14[label = "plot_frames\napa: 3\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 15[label = "plot_frames\napa: 4\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 16[label = "plot_frames\napa: 5\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 17[label = "plot_frames\napa: 0\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 18[label = "plot_frames\napa: 1\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 19[label = "plot_frames\napa: 2\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 20[label = "plot_frames\napa: 3\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 21[label = "plot_frames\napa: 4\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 22[label = "plot_frames\napa: 5\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 23[label = "plot_frames\napa: 0\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 24[label = "plot_frames\napa: 1\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 25[label = "plot_frames\napa: 2\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 26[label = "plot_frames\napa: 3\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 27[label = "plot_frames\napa: 4\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 28[label = "plot_frames\napa: 5\next: png", color = "0.35 0.6 0.85", style="rounded"]; + 29[label = "plot_frames\napa: 0\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 30[label = "plot_frames\napa: 1\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 31[label = "plot_frames\napa: 2\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 32[label = "plot_frames\napa: 3\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 33[label = "plot_frames\napa: 4\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 34[label = "plot_frames\napa: 5\next: pdf", color = "0.35 0.6 0.85", style="rounded"]; + 35[label = "plot_frames_hidpi", color = "0.10 0.6 0.85", style="rounded"]; + 36[label = "plot_frames_hidpi", color = "0.10 0.6 0.85", style="rounded"]; + 37[label = "split_images", color = "0.22 0.6 0.85", style="rounded"]; + 38[label = "split_images", color = "0.22 0.6 0.85", style="rounded"]; + 39[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 40[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 41[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 42[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 43[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 44[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 45[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 46[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 47[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 48[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 49[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 50[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 51[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 52[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 53[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 54[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 55[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 56[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 57[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 58[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: png\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 59[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 60[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 61[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 62[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 63[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: pdf\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 64[label = "plot_split_images\napa: 2\ncmap: seismic\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 65[label = "plot_split_images\napa: 2\ncmap: Spectral\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 66[label = "plot_split_images\napa: 2\ncmap: terrain\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 67[label = "plot_split_images\napa: 2\ncmap: coolwarm\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 68[label = "plot_split_images\napa: 2\ncmap: viridis\nevent: 0\next: svg\nplane: U", color = "0.63 0.6 0.85", style="rounded"]; + 69[label = "just_images", color = "0.29 0.6 0.85", style="rounded"]; 1 -> 0 + 2 -> 0 + 3 -> 0 + 4 -> 0 + 5 -> 0 6 -> 0 - 2 -> 1 - 3 -> 2 - 4 -> 2 - 5 -> 2 - 3 -> 5 - 7 -> 6 - 3 -> 7 - 8 -> 7 + 7 -> 0 + 8 -> 0 + 9 -> 0 + 10 -> 0 + 11 -> 0 + 12 -> 0 + 13 -> 0 + 14 -> 0 + 15 -> 0 + 16 -> 0 + 17 -> 0 + 18 -> 0 + 19 -> 0 + 20 -> 0 + 21 -> 0 + 22 -> 0 + 23 -> 0 + 24 -> 0 + 25 -> 0 + 26 -> 0 + 27 -> 0 + 28 -> 0 + 29 -> 0 + 30 -> 0 + 31 -> 0 + 32 -> 0 + 33 -> 0 + 34 -> 0 + 35 -> 0 + 36 -> 0 + 37 -> 0 + 38 -> 0 + 39 -> 0 + 40 -> 0 + 41 -> 0 + 42 -> 0 + 43 -> 0 + 44 -> 0 + 45 -> 0 + 46 -> 0 + 47 -> 0 + 48 -> 0 + 49 -> 0 + 50 -> 0 + 51 -> 0 + 52 -> 0 + 53 -> 0 + 54 -> 0 + 55 -> 0 + 56 -> 0 + 57 -> 0 + 58 -> 0 + 59 -> 0 + 60 -> 0 + 61 -> 0 + 62 -> 0 + 63 -> 0 + 64 -> 0 + 65 -> 0 + 66 -> 0 + 67 -> 0 + 68 -> 0 + 1 -> 2 + 1 -> 3 + 2 -> 4 + 5 -> 6 5 -> 7 - 4 -> 8 - 4 -> 9 - 8 -> 9 - 10 -> 9 - 11 -> 9 - 3 -> 9 - 12 -> 9 + 7 -> 8 5 -> 9 - 13 -> 9 - 2 -> 9 - 7 -> 9 - 14 -> 9 - 15 -> 9 - 16 -> 9 - 17 -> 9 - 18 -> 9 - 19 -> 9 - 20 -> 9 - 21 -> 9 - 22 -> 9 - 23 -> 9 - 24 -> 9 - 25 -> 9 - 26 -> 9 - 27 -> 9 - 28 -> 9 - 29 -> 9 - 30 -> 9 - 31 -> 9 - 32 -> 9 - 33 -> 9 - 34 -> 9 - 35 -> 9 - 36 -> 9 - 37 -> 9 - 38 -> 9 - 39 -> 9 1 -> 9 - 6 -> 9 - 40 -> 9 - 41 -> 9 - 42 -> 9 - 43 -> 9 - 44 -> 9 - 45 -> 9 - 46 -> 9 - 47 -> 9 - 48 -> 9 - 49 -> 9 - 50 -> 9 - 51 -> 9 - 52 -> 9 - 53 -> 9 - 54 -> 9 - 55 -> 9 - 56 -> 9 - 57 -> 9 - 58 -> 9 - 59 -> 9 - 60 -> 9 - 61 -> 9 - 62 -> 9 - 63 -> 9 - 64 -> 9 - 65 -> 9 - 66 -> 9 - 67 -> 9 - 68 -> 9 - 69 -> 9 - 4 -> 10 - 8 -> 11 - 3 -> 12 - 5 -> 13 - 2 -> 14 - 2 -> 15 - 2 -> 16 - 2 -> 17 - 2 -> 18 - 2 -> 19 - 2 -> 20 - 2 -> 21 - 2 -> 22 - 2 -> 23 - 2 -> 24 - 2 -> 25 - 7 -> 26 - 7 -> 27 - 7 -> 28 - 7 -> 29 - 7 -> 30 - 7 -> 31 - 7 -> 32 - 7 -> 33 - 7 -> 34 - 7 -> 35 - 7 -> 36 - 7 -> 37 - 20 -> 38 - 32 -> 39 - 1 -> 40 - 1 -> 41 - 1 -> 42 - 1 -> 43 - 1 -> 44 - 1 -> 45 - 1 -> 46 - 1 -> 47 - 1 -> 48 - 1 -> 49 - 1 -> 50 - 1 -> 51 - 1 -> 52 - 1 -> 53 - 1 -> 54 - 6 -> 55 - 6 -> 56 - 6 -> 57 - 6 -> 58 - 6 -> 59 - 6 -> 60 - 6 -> 61 - 6 -> 62 - 6 -> 63 - 6 -> 64 - 6 -> 65 - 6 -> 66 - 6 -> 67 - 6 -> 68 - 6 -> 69 + 7 -> 9 + 5 -> 10 + 2 -> 10 + 7 -> 10 + 9 -> 11 + 9 -> 12 + 9 -> 13 + 9 -> 14 + 9 -> 15 + 9 -> 16 + 9 -> 17 + 9 -> 18 + 9 -> 19 + 9 -> 20 + 9 -> 21 + 9 -> 22 + 10 -> 23 + 10 -> 24 + 10 -> 25 + 10 -> 26 + 10 -> 27 + 10 -> 28 + 10 -> 29 + 10 -> 30 + 10 -> 31 + 10 -> 32 + 10 -> 33 + 10 -> 34 + 17 -> 35 + 29 -> 36 + 9 -> 37 + 10 -> 38 + 37 -> 39 + 37 -> 40 + 37 -> 41 + 37 -> 42 + 37 -> 43 + 37 -> 44 + 37 -> 45 + 37 -> 46 + 37 -> 47 + 37 -> 48 + 37 -> 49 + 37 -> 50 + 37 -> 51 + 37 -> 52 + 37 -> 53 + 38 -> 54 + 38 -> 55 + 38 -> 56 + 38 -> 57 + 38 -> 58 + 38 -> 59 + 38 -> 60 + 38 -> 61 + 38 -> 62 + 38 -> 63 + 38 -> 64 + 38 -> 65 + 38 -> 66 + 38 -> 67 + 38 -> 68 + 37 -> 69 + 38 -> 69 } diff --git a/toyzero-all-dag.pdf b/toyzero-all-dag.pdf index 3673f63dc8842518805b519adfb0ce4b317900a3..17ad3849b49efa47a560f09f2b275efb32f14148 100644 GIT binary patch delta 25873 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 AY5)KL delta 25298 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 diff --git a/toyzero-all-dag.svg b/toyzero-all-dag.svg index 4255248..2e349af 100644 --- a/toyzero-all-dag.svg +++ b/toyzero-all-dag.svg @@ -4,1472 +4,1472 @@ - + snakemake_dag - + 0 - -just_images + +all 1 - -split_images + +get_resp_real 1->0 - - + + + + + +2 + +gen_resp_fake + + + +1->2 + + + + + +3 + +plot_resp +domain: real + + + +1->3 + + 9 - -all + +sim_frames +domain: real - + 1->9 - - + + - - -40 - -plot_split_images -apa: 2 -cmap: seismic -event: 0 -ext: png -plane: U - - - -1->40 - - + + +2->0 + + - - -41 - -plot_split_images -apa: 2 -cmap: Spectral -event: 0 -ext: png -plane: U - - - -1->41 - - + + +4 + +plot_resp +domain: fake - - -42 - -plot_split_images -apa: 2 -cmap: terrain -event: 0 -ext: png -plane: U - - - -1->42 - - + + +2->4 + + - - -43 - -plot_split_images -apa: 2 -cmap: coolwarm -event: 0 -ext: png -plane: U - - - -1->43 - - + + +10 + +sim_frames +domain: fake - - -44 - -plot_split_images -apa: 2 -cmap: viridis -event: 0 -ext: png -plane: U - - - -1->44 - - + + +2->10 + + - - -45 - -plot_split_images -apa: 2 -cmap: seismic -event: 0 -ext: pdf -plane: U - - - -1->45 - - + + +3->0 + + - - -46 - -plot_split_images -apa: 2 -cmap: Spectral -event: 0 -ext: pdf -plane: U - - - -1->46 - - + + +4->0 + + - - -47 - -plot_split_images -apa: 2 -cmap: terrain -event: 0 -ext: pdf -plane: U - - - -1->47 - - + + +5 + +get_wires - - -48 - -plot_split_images -apa: 2 -cmap: coolwarm -event: 0 -ext: pdf -plane: U - - - -1->48 - - + + +5->0 + + - - -49 - -plot_split_images -apa: 2 -cmap: viridis -event: 0 -ext: pdf -plane: U - - - -1->49 - - + + +6 + +plot_wires - - -50 - -plot_split_images -apa: 2 -cmap: seismic -event: 0 -ext: svg -plane: U - - - -1->50 - - + + +5->6 + + - - -51 - -plot_split_images -apa: 2 -cmap: Spectral -event: 0 -ext: svg -plane: U - - - -1->51 - - + + +7 + +gen_depos - - -52 - -plot_split_images -apa: 2 -cmap: terrain -event: 0 -ext: svg -plane: U - - - -1->52 - - + + +5->7 + + - - -53 - -plot_split_images -apa: 2 -cmap: coolwarm -event: 0 -ext: svg -plane: U - - - -1->53 - - + + +5->9 + + - - -54 - -plot_split_images -apa: 2 -cmap: viridis -event: 0 -ext: svg -plane: U - - - -1->54 - - + + +5->10 + + - - -2 - -sim_frames -domain: real + + +6->0 + + - - -2->1 - - + + +7->0 + + - - -2->9 - - + + +8 + +plot_depos + + + +7->8 + + + + + +7->9 + + + + + +7->10 + + + + + +8->0 + + + + + +9->0 + + + + + +11 + +plot_frames +apa: 0 +ext: png + + + +9->11 + + + + + +12 + +plot_frames +apa: 1 +ext: png + + + +9->12 + + + + + +13 + +plot_frames +apa: 2 +ext: png + + + +9->13 + + 14 - -plot_frames -apa: 0 -ext: png + +plot_frames +apa: 3 +ext: png - - -2->14 - - + + +9->14 + + 15 - -plot_frames -apa: 1 -ext: png + +plot_frames +apa: 4 +ext: png - - -2->15 - - + + +9->15 + + 16 - -plot_frames -apa: 2 -ext: png + +plot_frames +apa: 5 +ext: png - - -2->16 - - + + +9->16 + + 17 - -plot_frames -apa: 3 -ext: png + +plot_frames +apa: 0 +ext: pdf - - -2->17 - - + + +9->17 + + 18 - -plot_frames -apa: 4 -ext: png + +plot_frames +apa: 1 +ext: pdf - - -2->18 - - + + +9->18 + + 19 - -plot_frames -apa: 5 -ext: png + +plot_frames +apa: 2 +ext: pdf - - -2->19 - - + + +9->19 + + 20 - -plot_frames -apa: 0 -ext: pdf + +plot_frames +apa: 3 +ext: pdf - - -2->20 - - + + +9->20 + + 21 - -plot_frames -apa: 1 -ext: pdf + +plot_frames +apa: 4 +ext: pdf - - -2->21 - - + + +9->21 + + 22 - -plot_frames -apa: 2 -ext: pdf + +plot_frames +apa: 5 +ext: pdf - - -2->22 - - + + +9->22 + + + + + +37 + +split_images + + + +9->37 + + + + + +10->0 + + 23 - -plot_frames -apa: 3 -ext: pdf + +plot_frames +apa: 0 +ext: png - - -2->23 - - + + +10->23 + + 24 - -plot_frames -apa: 4 -ext: pdf + +plot_frames +apa: 1 +ext: png - - -2->24 - - + + +10->24 + + 25 - -plot_frames -apa: 5 -ext: pdf - - - -2->25 - - - - - -3 - -get_wires - - - -3->2 - - - - - -5 - -gen_depos - - - -3->5 - - - - - -7 - -sim_frames -domain: fake - - - -3->7 - - - - - -3->9 - - - - - -12 - -plot_wires - - - -3->12 - - - - - -4 - -get_resp_real - - - -4->2 - - + +plot_frames +apa: 2 +ext: png - - -8 - -gen_resp_fake - - - -4->8 - - - - - -4->9 - - - - - -10 - -plot_resp -domain: real - - - -4->10 - - - - - -5->2 - - - - - -5->7 - - - - - -5->9 - - - - - -13 - -plot_depos - - - -5->13 - - - - - -6 - -split_images - - - -6->0 - - - - - -6->9 - - - - - -55 - -plot_split_images -apa: 2 -cmap: seismic -event: 0 -ext: png -plane: U - - - -6->55 - - - - - -56 - -plot_split_images -apa: 2 -cmap: Spectral -event: 0 -ext: png -plane: U - - - -6->56 - - - - - -57 - -plot_split_images -apa: 2 -cmap: terrain -event: 0 -ext: png -plane: U - - - -6->57 - - - - - -58 - -plot_split_images -apa: 2 -cmap: coolwarm -event: 0 -ext: png -plane: U - - - -6->58 - - - - - -59 - -plot_split_images -apa: 2 -cmap: viridis -event: 0 -ext: png -plane: U - - - -6->59 - - - - - -60 - -plot_split_images -apa: 2 -cmap: seismic -event: 0 -ext: pdf -plane: U - - - -6->60 - - - - - -61 - -plot_split_images -apa: 2 -cmap: Spectral -event: 0 -ext: pdf -plane: U - - - -6->61 - - - - - -62 - -plot_split_images -apa: 2 -cmap: terrain -event: 0 -ext: pdf -plane: U - - - -6->62 - - - - - -63 - -plot_split_images -apa: 2 -cmap: coolwarm -event: 0 -ext: pdf -plane: U - - - -6->63 - - - - - -64 - -plot_split_images -apa: 2 -cmap: viridis -event: 0 -ext: pdf -plane: U - - - -6->64 - - - - - -65 - -plot_split_images -apa: 2 -cmap: seismic -event: 0 -ext: svg -plane: U - - - -6->65 - - - - - -66 - -plot_split_images -apa: 2 -cmap: Spectral -event: 0 -ext: svg -plane: U - - - -6->66 - - - - - -67 - -plot_split_images -apa: 2 -cmap: terrain -event: 0 -ext: svg -plane: U - - - -6->67 - - - - - -68 - -plot_split_images -apa: 2 -cmap: coolwarm -event: 0 -ext: svg -plane: U - - - -6->68 - - - - - -69 - -plot_split_images -apa: 2 -cmap: viridis -event: 0 -ext: svg -plane: U - - - -6->69 - - - - - -7->6 - - - - - -7->9 - - + + +10->25 + + 26 - -plot_frames -apa: 0 -ext: png + +plot_frames +apa: 3 +ext: png - - -7->26 - - + + +10->26 + + 27 - -plot_frames -apa: 1 -ext: png + +plot_frames +apa: 4 +ext: png - - -7->27 - - + + +10->27 + + 28 - -plot_frames -apa: 2 -ext: png + +plot_frames +apa: 5 +ext: png - - -7->28 - - + + +10->28 + + 29 - -plot_frames -apa: 3 -ext: png + +plot_frames +apa: 0 +ext: pdf - - -7->29 - - + + +10->29 + + 30 - -plot_frames -apa: 4 -ext: png + +plot_frames +apa: 1 +ext: pdf - - -7->30 - - + + +10->30 + + 31 - -plot_frames -apa: 5 -ext: png + +plot_frames +apa: 2 +ext: pdf - - -7->31 - - + + +10->31 + + 32 - -plot_frames -apa: 0 -ext: pdf + +plot_frames +apa: 3 +ext: pdf - - -7->32 - - + + +10->32 + + 33 - -plot_frames -apa: 1 -ext: pdf + +plot_frames +apa: 4 +ext: pdf - - -7->33 - - + + +10->33 + + 34 - -plot_frames -apa: 2 -ext: pdf - - - -7->34 - - - - - -35 - -plot_frames -apa: 3 -ext: pdf - - - -7->35 - - - - - -36 - -plot_frames -apa: 4 -ext: pdf + +plot_frames +apa: 5 +ext: pdf - - -7->36 - - + + +10->34 + + - - -37 - -plot_frames -apa: 5 -ext: pdf + + +38 + +split_images - + -7->37 - - +10->38 + + - - -8->7 - - + + +11->0 + + - - -8->9 - - + + +12->0 + + - - -11 - -plot_resp -domain: fake + + +13->0 + + - - -8->11 - - + + +14->0 + + - + -10->9 - - +15->0 + + - + -11->9 - - +16->0 + + + + + +17->0 + + + + + +35 + +plot_frames_hidpi - + + +17->35 + + + + -12->9 - - +18->0 + + + + + +19->0 + + - + -13->9 - - +20->0 + + + + + +21->0 + + + + + +22->0 + + - + -14->9 - - +23->0 + + - + -15->9 - - +24->0 + + - + -16->9 - - +25->0 + + - + -17->9 - - +26->0 + + - + -18->9 - - +27->0 + + - + -19->9 - - +28->0 + + - + -20->9 - - +29->0 + + - - -38 - -plot_frames_hidpi + + +36 + +plot_frames_hidpi - - -20->38 - - + + +29->36 + + - + -21->9 - - +30->0 + + - + -22->9 - - +31->0 + + - + -23->9 - - +32->0 + + - + -24->9 - - +33->0 + + - + -25->9 - - +34->0 + + - + -26->9 - - +35->0 + + - + -27->9 - - +36->0 + + - + -28->9 - - +37->0 + + + + + +39 + +plot_split_images +apa: 2 +cmap: seismic +event: 0 +ext: png +plane: U + + + +37->39 + + + + + +40 + +plot_split_images +apa: 2 +cmap: Spectral +event: 0 +ext: png +plane: U + + + +37->40 + + + + + +41 + +plot_split_images +apa: 2 +cmap: terrain +event: 0 +ext: png +plane: U + + + +37->41 + + + + + +42 + +plot_split_images +apa: 2 +cmap: coolwarm +event: 0 +ext: png +plane: U + + + +37->42 + + + + + +43 + +plot_split_images +apa: 2 +cmap: viridis +event: 0 +ext: png +plane: U + + + +37->43 + + + + + +44 + +plot_split_images +apa: 2 +cmap: seismic +event: 0 +ext: pdf +plane: U + + + +37->44 + + + + + +45 + +plot_split_images +apa: 2 +cmap: Spectral +event: 0 +ext: pdf +plane: U + + + +37->45 + + + + + +46 + +plot_split_images +apa: 2 +cmap: terrain +event: 0 +ext: pdf +plane: U + + + +37->46 + + + + + +47 + +plot_split_images +apa: 2 +cmap: coolwarm +event: 0 +ext: pdf +plane: U + + + +37->47 + + + + + +48 + +plot_split_images +apa: 2 +cmap: viridis +event: 0 +ext: pdf +plane: U + + + +37->48 + + + + + +49 + +plot_split_images +apa: 2 +cmap: seismic +event: 0 +ext: svg +plane: U + + + +37->49 + + + + + +50 + +plot_split_images +apa: 2 +cmap: Spectral +event: 0 +ext: svg +plane: U + + + +37->50 + + + + + +51 + +plot_split_images +apa: 2 +cmap: terrain +event: 0 +ext: svg +plane: U + + + +37->51 + + + + + +52 + +plot_split_images +apa: 2 +cmap: coolwarm +event: 0 +ext: svg +plane: U + + + +37->52 + + + + + +53 + +plot_split_images +apa: 2 +cmap: viridis +event: 0 +ext: svg +plane: U + + + +37->53 + + + + + +69 + +just_images + + + +37->69 + + - + -29->9 - - +38->0 + + + + + +54 + +plot_split_images +apa: 2 +cmap: seismic +event: 0 +ext: png +plane: U + + + +38->54 + + + + + +55 + +plot_split_images +apa: 2 +cmap: Spectral +event: 0 +ext: png +plane: U + + + +38->55 + + + + + +56 + +plot_split_images +apa: 2 +cmap: terrain +event: 0 +ext: png +plane: U + + + +38->56 + + + + + +57 + +plot_split_images +apa: 2 +cmap: coolwarm +event: 0 +ext: png +plane: U + + + +38->57 + + + + + +58 + +plot_split_images +apa: 2 +cmap: viridis +event: 0 +ext: png +plane: U + + + +38->58 + + + + + +59 + +plot_split_images +apa: 2 +cmap: seismic +event: 0 +ext: pdf +plane: U + + + +38->59 + + + + + +60 + +plot_split_images +apa: 2 +cmap: Spectral +event: 0 +ext: pdf +plane: U + + + +38->60 + + + + + +61 + +plot_split_images +apa: 2 +cmap: terrain +event: 0 +ext: pdf +plane: U + + + +38->61 + + + + + +62 + +plot_split_images +apa: 2 +cmap: coolwarm +event: 0 +ext: pdf +plane: U + + + +38->62 + + + + + +63 + +plot_split_images +apa: 2 +cmap: viridis +event: 0 +ext: pdf +plane: U + + + +38->63 + + + + + +64 + +plot_split_images +apa: 2 +cmap: seismic +event: 0 +ext: svg +plane: U + + + +38->64 + + + + + +65 + +plot_split_images +apa: 2 +cmap: Spectral +event: 0 +ext: svg +plane: U + + + +38->65 + + + + + +66 + +plot_split_images +apa: 2 +cmap: terrain +event: 0 +ext: svg +plane: U + + + +38->66 + + + + + +67 + +plot_split_images +apa: 2 +cmap: coolwarm +event: 0 +ext: svg +plane: U + + + +38->67 + + + + + +68 + +plot_split_images +apa: 2 +cmap: viridis +event: 0 +ext: svg +plane: U + + + +38->68 + + - + + +38->69 + + + + -30->9 - - +39->0 + + - + -31->9 - - +40->0 + + - + -32->9 - - - - - -39 - -plot_frames_hidpi - - - -32->39 - - +41->0 + + - + -33->9 - - +42->0 + + - + -34->9 - - +43->0 + + - + -35->9 - - +44->0 + + - + -36->9 - - +45->0 + + - + -37->9 - - +46->0 + + - + -38->9 - - +47->0 + + - + -39->9 - - +48->0 + + + + + +49->0 + + + + + +50->0 + + - + -40->9 - - +51->0 + + - + -41->9 - - +52->0 + + - + -42->9 - - +53->0 + + - + -43->9 - - +54->0 + + - + -44->9 - - +55->0 + + - + -45->9 - - +56->0 + + - + -46->9 - - +57->0 + + - + -47->9 - - +58->0 + + - + -48->9 - - +59->0 + + - + -49->9 - - +60->0 + + - + -50->9 - - +61->0 + + - + -51->9 - - +62->0 + + - + -52->9 - - +63->0 + + - + -53->9 - - +64->0 + + - + -54->9 - - +65->0 + + - + -55->9 - - +66->0 + + - + -56->9 - - +67->0 + + - + -57->9 - - - - - -58->9 - - - - - -59->9 - - - - - -60->9 - - - - - -61->9 - - - - - -62->9 - - - - - -63->9 - - - - - -64->9 - - - - - -65->9 - - - - - -66->9 - - - - - -67->9 - - - - - -68->9 - - - - - -69->9 - - +68->0 + + From c5faa98f98718e4c2abe28c32f78fa4081a83a80 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Fri, 23 Jul 2021 13:09:47 -0400 Subject: [PATCH 04/15] Checkpoint --- Snakefile | 43 ++++++++--- cfg/ioutils.jsonnet | 41 +++++++++- cfg/main-depos-sigproc.jsonnet | 137 +++++++++++++++++++-------------- cfg/main-depos-sim.jsonnet | 63 +++++++++++++++ cfg/nf.jsonnet | 99 +----------------------- cfg/pdsp_chndb.jsonnet | 106 +++++++++++++++++++++++++ cfg/sp.jsonnet | 1 - cfg/toyzero.jsonnet | 12 ++- cfg/utils.jsonnet | 12 +++ dot.envrc | 1 + testit.sh | 43 +++++++++++ 11 files changed, 380 insertions(+), 178 deletions(-) create mode 100644 cfg/main-depos-sim.jsonnet create mode 100644 cfg/pdsp_chndb.jsonnet create mode 100644 cfg/utils.jsonnet create mode 100755 testit.sh diff --git a/Snakefile b/Snakefile index 371699b..0fb5631 100644 --- a/Snakefile +++ b/Snakefile @@ -42,6 +42,8 @@ wcdata_ext = "json.bz2" ## to span some set of each. resps = "dune-garfield-1d565" wires = "protodune-wires-larsoft-v4" +# a few places want a list of APA IDs +apa_iota = list(range(6)) # The data domains describe a universe. For toyzero, they are # associated with a particular detector response. @@ -56,7 +58,6 @@ fake_resps = f'{datadir}/resps/fake-resps.{wcdata_ext}' domain_resps = f'{datadir}/resps/{{domain}}-resps.{wcdata_ext}' wires_file = f'{datadir}/wires/wires.{wcdata_ext}' depos_file = f'{datadir}/depos/depos.npz' -domain_frames = f'{datadir}/frames/{{tier}}/{{domain}}-frames.npz' # resp - prepare response files @@ -199,7 +200,7 @@ rule wct_dots: wcsonnet \ -P cfg \ -A input=DEPOS-FILE \ - -A output=FRAMES-FILE \ + --tla-code taps='{{"orig":"frame-orig-.npz","gauss":"frame-gauss.npz"}}' \ -A wires=WIRES-FILE \ -A resps=RESPS-FILE \ {input.config} > {output.json}; @@ -211,6 +212,19 @@ rule all_dots: input: expand(rules.wct_dots.output, tier=tiers) +# this gives the pattern for one per-APA frame file. The %d is +# interpolated by wire-cell configuration. +frames_pattern = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa%d.npz' +frames_wildcard = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa{{apa}}.npz' + +def frame_taps(w): + if w.tier == "noiseless": + tap = "orig" + else: + tap = "gauss" + fp = frames_pattern.format(**dict(w)) + return dict(tap=tap, pat=fp) + rule sim_frames: input: wires = wires_file, @@ -218,14 +232,16 @@ rule sim_frames: depos = depos_file, config = wct_cfg_file output: - frames = temp(domain_frames) + temp([frames_pattern%n for n in apa_iota]) + params: + p = frame_taps shell: ''' - rm -f {output.frames}; + rm -f {output}; wire-cell \ -l stdout -L {config[wcloglvl]} \ -P cfg \ -A input={input.depos} \ - -A output={output.frames} \ + --tla-code 'taps={{"{params.p[tap]}":"{params.p[pat]}"}}' \ -A wires={input.wires} \ -A resps={input.resps} \ -c {input.config} @@ -233,18 +249,23 @@ rule sim_frames: def gen_plot_frames(w): i = int(w.apa) - return dict(chb=f'{i},{i+2560}') + if w.tier == "noiseless": + return dict(chb=f'{i},{i+2560}', tag="") + if w.tier == "signal": + tag = f"gauss{i}" + return dict(chb=f'0,2560', tag=tag) rule plot_frames: input: - domain_frames + frames_wildcard output: f'{plotdir}/frames-{{tier}}-{{domain}}-apa{{apa}}.{{ext}}' params: p = gen_plot_frames shell:''' - wirecell-gen plot-sim {input} {output} -p frames -b {params.p[chb]} + wirecell-gen plot-sim {input} {output} -p frames -b {params.p[chb]} --tag "{params.p[tag]}" ''' + rule plot_frames_hidpi: input: f'{plotdir}/frames-{{tier}}-{{domain}}-apa{{apa}}.pdf' @@ -263,7 +284,7 @@ rule all_frames: tier=tiers), expand(rules.plot_frames.output, domain=["real","fake"], tier=tiers, - ext=["png","pdf"], apa=list(range(6))), + ext=["png","pdf"], apa=apa_iota), expand(rules.plot_frames_hidpi.output, domain=["real","fake"], tier=tiers, apa=[0]) @@ -282,13 +303,13 @@ rule all_frames: split_outer_product = dict( # domain = ["protodune"], event = list(range(nevents)), - apa = list(range(6)), + apa = apa_iota, plane = ["U","V","W"], ) rule split_images: input: - domain_frames + frames_wildcard output: expand(datadir+'/images/{{tier}}/{{domain}}/protodune-orig-{event}-{apa}-{plane}.npz', **split_outer_product) diff --git a/cfg/ioutils.jsonnet b/cfg/ioutils.jsonnet index 0688771..16f0a2b 100644 --- a/cfg/ioutils.jsonnet +++ b/cfg/ioutils.jsonnet @@ -1,8 +1,11 @@ local wc = import "wirecell.jsonnet"; -local g = import 'pgraph.jsonnet'; +local pg = import 'pgraph.jsonnet'; + +local ut = import 'utils.jsonnet'; + { // Return a numpy depo source configuration. - npz_source_depo(name, filename) :: g.pnode({ + npz_source_depo(name, filename) :: pg.pnode({ type: 'NumpyDepoLoader', name: name, data: { @@ -12,7 +15,7 @@ local g = import 'pgraph.jsonnet'; // Return a numpy frame saver configuration. - frame_save_npz(name, filename, digitize=true, tags=[]) :: g.pnode({ + frame_save_npz(name, filename, digitize=true, tags=[]) :: pg.pnode({ type: 'NumpyFrameSaver', name: name, data: { @@ -22,8 +25,38 @@ local g = import 'pgraph.jsonnet'; }}, nin=1, nout=1), // Use to cap off a frame stream with a sink. - frame_sink(name="frame-sink") :: g.pnode({ + frame_sink(name="frame-sink") :: pg.pnode({ type: "DumpFrames", name: name, }, nin=1, nout=0), + + + // Save frames to outfile. + // + // name, outfile and elements of the tags list may have a single + // "%" format code which if it exists will be interpolated again + // the "index". + // + // If outfile has not "%" format the string "-apa%d" will be + // appended to the base file name (prior to .ext). + // + // The "name" is used to give unique objects. + // + // The tags determine which among all available frames/trace tags + // to save. + // + // If digitize is true, frame samples will be truncated to int + // else left as float. + // + // If cap is false, the resulting node acts as a filter. + frame_out(name, index, outfile, tags=["gauss%d"], digitize=false, cap=true) :: { + local nam = if ut.haspct(name) then name%index else name, + local tint = [if ut.haspct(t) then t%index else t for t in tags], + local end = if cap then [$.frame_sink(nam)] else [], + local outf = if ut.haspct(outfile) then outfile%index else ut.basename_append(outfile, "-apa%d"%index), + + ret: pg.pipeline([$.frame_save_npz(nam, outf, digitize=digitize, tags=tint)] + + end) + }.ret, + } diff --git a/cfg/main-depos-sigproc.jsonnet b/cfg/main-depos-sigproc.jsonnet index 847afca..f04e45d 100644 --- a/cfg/main-depos-sigproc.jsonnet +++ b/cfg/main-depos-sigproc.jsonnet @@ -3,20 +3,35 @@ // It implements the chain: // (depos)->sim(signal,noise)->sigproc->(signals) // -// It takes a number of top-level-arguments (TLA). +// It takes a number of top-level-arguments (TLA) which can control +// input configuration and input data and a number of output data +// "taps". You may also control if it runs in single or multi thread +// mode // // Run like // // wire-cell \ -// -A input=depos.npz -A output=frames.npz \ +// -A depos=depos.npz \ +// -A taps={...see below...} \ // -A wires=wires-geometry.json.bz2 \ // -A resps=field-response.json.bz2 \ // -A noisef=/path/to/noise/model/file \ -// -A app=[Pgrapher|TbbFlow] \ +// -A thread=[single|multi] \ // -c main-depos-sim-adc.jsonnet // -// The 'noisef' TLA is optional and specifies a noise model. If not -// given, only signal is simulated. +// The 'noisef' TLA is optional and specifies a noise model file. If +// not given, only signal is simulated. +// +// If "taps" is given it specifies a mapping from a data tier key word +// to a file name. The key is one of the conventional tags: +// +// - orig :: means the ADC-level frames out of the simulation +// - gauss :: means the signal processing with Gaussian filter +// +// The file name may have a "%" formatter which will be interpolated +// against the APA ID number. If omitted, "-apa%d" will be inserted +// at the end of the base file name just prior to the extention +// (likely .npz). local wc = import "wirecell.jsonnet"; local pg = import "pgraph.jsonnet"; @@ -26,59 +41,67 @@ local io = import "ioutils.jsonnet"; local nf = import "nf.jsonnet"; local sp = import "sp.jsonnet"; -function(input, output, wires, resps, noisef=null, app='Pgrapher') -local seeds = [0,1,2,3,4]; // maybe let CLI set? -local depos = io.npz_source_depo("depos", input); - local params = import "pgrapher/experiment/pdsp/simparams.jsonnet"; local spfilt = import "pgrapher/experiment/pdsp/sp-filters.jsonnet"; +local chndb = import "pdsp_chndb.jsonnet"; + +function(input, taps, wires, resps, noisef=null, thread='single') + local app = if thread == 'single' + then 'Pgrapher' + else 'TbbFlow'; + local seeds = [0,1,2,3,4]; // maybe let CLI set? + local depos = io.npz_source_depo("depos", input); + + + local wireobj = tz.wire_file(wires); + local anodes = tz.anodes(wireobj, params.det.volumes); + + local apaids = std.range(0, std.length(anodes)-1); + + local robjs = tz.responses(resps, params.elec, params.daq); + + local random = tz.random(seeds); + + local drifter = tz.drifter(params.det.volumes, params.lar, random); + local bagger = tz.bagger(params.daq); + + local chndb_perfect(n) = + chndb.perfect(anodes[n], robjs.fr, + params.daq.nticks, + params.daq.tick); + + local tap_out(tap, apaid) = + if std.objectHas(taps, tap) + then [io.frame_out(tap+"%d", apaid, taps[tap], tags=[tap+"%d"], cap = tap=="gauss")] + else []; + + local sim(n) = [ + local anode = anodes[n]; + tz.sim(anode, // kitchen + robjs.pirs, // sink + params.daq, + params.adc, + params.lar, + noisef, + 'adc', + random) + ] + tap_out("orig", n); + + local adcpermv = tz.adcpermv(params.adc); + local nfsp(n) = [ + nf(anodes[n], robjs.fr, chndb_perfect(n), + params.daq.nticks, params.daq.tick), + ] + tap_out("raw", n) + [ + sp(anodes[n], robjs.fr, robjs.er, spfilt, adcpermv) + ] + tap_out("gauss", n); + + local oneapa(n) = pg.pipeline(sim(n) + nfsp(n)); + + local pipes = [oneapa(n) for n in apaids]; + + local body = pg.fan.fanout('DepoSetFanout', pipes); + local full = pg.pipeline([depos, drifter, bagger, body]); + + tz.main(full, app) -local wireobj = tz.wire_file(wires); -local anodes = tz.anodes(wireobj, params.det.volumes); - -local apaids = std.range(0, std.length(anodes)-1); - -local robjs = tz.responses(resps, params.elec, params.daq); - -local random = tz.random(seeds); - -local drifter = tz.drifter(params.det.volumes, params.lar, random); -local bagger = tz.bagger(params.daq); - -local sim(n) = - tz.sim(anodes[n], robjs.pirs, - params.daq, params.adc, params.lar, - noisef, 'adc', random); - -local adcpermv = tz.adcpermv(params.adc); -local nfsp(n) = - pg.pipeline([ - //nf(anodes[n], robjs.fr, params.daq.nticks, params.daq.tick), - sp(anodes[n], robjs.fr, robjs.er, spfilt, adcpermv)]); - -local outfile(n) = { - local l = std.split(output,"."), - ret:"%s-apa%d.%s"%[l[0],n,l[1]] -}.ret; -local oneapa(n) = { - local name = "gauss%d"%n, - local outf = outfile(n), - pipe: pg.pipeline([ - sim(n), nfsp(n), - io.frame_save_npz(name, outf, digitize=false, tags=[name]), - io.frame_sink(name)]) -}.pipe; - -local pipes = [oneapa(n) for n in apaids]; - -// local frame_tags = ["gauss%d"%n for n in apaids]; -// local frames = io.frame_save_npz("frames", output, -// digitize = false, tags=frame_tags); -// local dump = io.frame_sink(); -// local body = pg.fan.pipe('DepoSetFanout', pipes, 'FrameFanin', -// outtags=frame_tags); - -local body = pg.fan.fanout('DepoSetFanout', pipes); -local full = pg.pipeline([depos, drifter, bagger, body]); -tz.main(full, app) diff --git a/cfg/main-depos-sim.jsonnet b/cfg/main-depos-sim.jsonnet new file mode 100644 index 0000000..e3a9df4 --- /dev/null +++ b/cfg/main-depos-sim.jsonnet @@ -0,0 +1,63 @@ +// This is a main wire-cell configuration file. +// +// It implements the chain (depos)->[sim[+noise]]->(Voltage|ADC). +// +// It takes a number of top-level-arguments (TLA). +// +// Run like +// +// wire-cell \ +// -A input=depos.npz -A output=frames.npz \ +// -A wires=wires-geometry.json.bz2 \ +// -A resps=field-response.json.bz2 \ +// -A noisef=/path/to/noise/model/file \ +// -A tier=[adc|voltage] \ +// -A app=[Pgrapher|TbbFlow] \ +// -c main-depos-sim-adc.jsonnet +// +// The 'noisef' TLA is optional and specifies a noise model. If not +// given, only signal is simulated. +// +// The 'tier' is optional and default to 'adc'. If 'voltage' is +// given, the output frame will not have the ADC model applied and +// will be in units of Volts instead of ADC counts. + +local wc = import "wirecell.jsonnet"; +local pg = import "pgraph.jsonnet"; + +local tz = import "toyzero.jsonnet"; +local io = import "ioutils.jsonnet"; + +function(input, output, wires, resps, noisef=null, tier='adc', app='Pgrapher') +local seeds = [0,1,2,3,4]; // maybe let CLI set? +local depos = io.npz_source_depo("depos", input); + +local params = tz.protodune_params; + +local wireobj = tz.wire_file(wires); +local anodes = tz.anodes(wireobj, params.det.volumes); + +local apaids = std.range(0, std.length(anodes)-1); + +local pirs = tz.pirs(resps, params.daq, params.elec); + +local random = tz.random(seeds); + +local drifter = tz.drifter(params.det.volumes, params.lar, random); +local bagger = tz.bagger(params.daq); + +local apasim(n) = + tz.apasim(anodes[n], pirs, + params.daq, params.adc, params.lar, + noisef, tier, random); + +local simpipes = [apasim(n) for n in apaids]; + + +local frames = io.frame_save_npz("frames", output, digitize = tier=='adc'); +local dump = io.frame_sink(); + +local simbody = pg.fan.pipe('DepoSetFanout', simpipes, 'FrameFanin'); +local full = pg.pipeline([depos, drifter, bagger, simbody, frames, dump]); +tz.main(full, app) + diff --git a/cfg/nf.jsonnet b/cfg/nf.jsonnet index 7a10551..ce107b8 100644 --- a/cfg/nf.jsonnet +++ b/cfg/nf.jsonnet @@ -6,102 +6,9 @@ local wc = import "wirecell.jsonnet"; local pg = import "pgraph.jsonnet"; -function(anode, fieldresp, nsamples, tick=0.5*wc.us, rms_cuts=[]) { +function(anode, fieldresp, chndb, nsamples, tick=0.5*wc.us, rms_cuts=[]) { local apaid = anode.data.ident, - local chndb = { - type: 'OmniChannelNoiseDB', - name: 'ocndbperfect%d' % apaid, - uses: [anode, fieldresp], - data: { - anode: wc.tn(anode), - field_response: wc.tn(fieldresp), - tick: tick, - - // This sets the number of frequency-domain bins used in the noise - // filtering. It is not necessarily true that the time-domain - // waveforms have the same number of ticks. This must be non-zero. - nsamples: nsamples, - - // Group channels into their domains of coherency - groups: [ - std.range(apaid * 2560 + u * 40, apaid * 2560 + (u + 1) * 40 - 1) - for u in std.range(0, 19) - ] + [ - std.range(apaid * 2560 + 800 + v * 40, apaid * 2560 + 800 + (v + 1) * 40 - 1) - for v in std.range(0, 19) - ] + [ - std.range(apaid * 2560 + 1600 + w * 48, apaid * 2560 + 1600 + (w + 1) * 48 - 1) - for w in std.range(0, 19) - ], - - - // Overide defaults for specific channels. If an info is - // mentioned for a particular channel in multiple objects in this - // list then last mention wins. - channel_info: [ - // First entry provides default channel info across ALL - // channels. Subsequent entries override a subset of channels - // with a subset of these entries. There's no reason to - // repeat values found here in subsequent entries unless you - // wish to change them. - { - channels: std.range(apaid * 2560, (apaid + 1) * 2560 - 1), - nominal_baseline: 2048.0, // adc count - gain_correction: 1.0, // unitless - response_offset: 0.0, // ticks? - pad_window_front: 10, // ticks? - pad_window_back: 10, // ticks? - decon_limit: 0.02, - decon_limit1: 0.09, - adc_limit: 15, - roi_min_max_ratio: 0.8, // default 0.8 - min_rms_cut: 1.0, // units??? - max_rms_cut: 30.0, // units??? - - // parameter used to make "rcrc" spectrum - rcrc: 1.1 * wc.millisecond, // 1.1 for collection, 3.3 for induction - rc_layers: 1, // default 2 - - // parameters used to make "config" spectrum - reconfig: {}, - - // list to make "noise" spectrum mask - freqmasks: [], - - // field response waveform to make "response" spectrum. - response: {}, - }, - { - //channels: { wpid: wc.WirePlaneId(wc.Ulayer) }, - channels: std.range(apaid * 2560, apaid * 2560 + 800- 1), - response: { wpid: wc.WirePlaneId(wc.Ulayer) }, - response_offset: 120, // offset of the negative peak - pad_window_front: 20, - decon_limit: 0.02, - decon_limit1: 0.07, - roi_min_max_ratio: 3.0, - }, - { - //channels: { wpid: wc.WirePlaneId(wc.Vlayer) }, - channels: std.range(apaid * 2560 + 800, apaid * 2560 + 1600- 1), - response: { wpid: wc.WirePlaneId(wc.Vlayer) }, - response_offset: 124, - decon_limit: 0.01, - decon_limit1: 0.08, - roi_min_max_ratio: 1.5, - }, - { - //channels: { wpid: wc.WirePlaneId(wc.Wlayer) }, - channels: std.range(apaid * 2560 + 1600, apaid * 2560 + 2560- 1), - nominal_baseline: 400.0, - decon_limit: 0.05, - decon_limit1: 0.08, - }, - ] + rms_cuts, - }, // data - }, // chndb - local single = { type: 'pdOneChannelNoise', name: 'ocn%d'%apaid, @@ -109,9 +16,6 @@ function(anode, fieldresp, nsamples, tick=0.5*wc.us, rms_cuts=[]) { noisedb: wc.tn(chndb), anode: wc.tn(anode), resmp: [ - {channels: std.range(2128, 2175), sample_from: 5996}, - {channels: std.range(1520, 1559), sample_from: 5996}, - {channels: std.range( 440, 479), sample_from: 5996}, ], }, }, @@ -126,7 +30,6 @@ function(anode, fieldresp, nsamples, tick=0.5*wc.us, rms_cuts=[]) { local obnf = pg.pnode({ - type: 'OmnibusNoiseFilter', name: 'nf%d'%apaid, data: { diff --git a/cfg/pdsp_chndb.jsonnet b/cfg/pdsp_chndb.jsonnet new file mode 100644 index 0000000..5a3bda8 --- /dev/null +++ b/cfg/pdsp_chndb.jsonnet @@ -0,0 +1,106 @@ +// This defines channel noise databases for noise filtering for pdsp. + +local wc = import "wirecell.jsonnet"; + +{ + // The "perfect noise" database is one that is free of any + // "special" considerations such as per channel variability. The + // "official" perfect chndb depends on the official "chndb-base" + // and that seems to be adulterated with specific settings. We + // try to start fresh here. + perfect(anode, fr, nsamples, tick=0.5*wc.us) :: { + local apaid = anode.data.ident, + type:'OmniChannelNoiseDB', + name: 'ocndbperfect-%s' % anode.name, + uses: [anode, fr], + data: { + anode: wc.tn(anode), + field_response: wc.tn(fr), + tick: tick, + nsamples: nsamples, + + groups: [ + std.range(apaid * 2560 + u * 40, apaid * 2560 + (u + 1) * 40 - 1) + for u in std.range(0, 19) + ] + [ + std.range(apaid * 2560 + 800 + v * 40, apaid * 2560 + 800 + (v + 1) * 40 - 1) + for v in std.range(0, 19) + ] + [ + std.range(apaid * 2560 + 1600 + w * 48, apaid * 2560 + 1600 + (w + 1) * 48 - 1) + for w in std.range(0, 19) + ], + + // last match wins + channel_info: [ + + // First entry provides default channel info across ALL + // channels. Subsequent entries override a subset of channels + // with a subset of these entries. There's no reason to + // repeat values found here in subsequent entries unless you + // wish to change them. + { + channels: std.range(apaid * 2560, (apaid + 1) * 2560 - 1), + nominal_baseline: 2350.0, // adc count + gain_correction: 1.0, // unitless + response_offset: 0.0, // ticks? + pad_window_front: 10, // ticks? + pad_window_back: 10, // ticks? + decon_limit: 0.02, + decon_limit1: 0.09, + adc_limit: 15, + roi_min_max_ratio: 0.8, // default 0.8 + min_rms_cut: 1.0, // units??? + max_rms_cut: 30.0, // units??? + + // parameter used to make "rcrc" spectrum + rcrc: 1.1 * wc.millisecond, // 1.1 for collection, 3.3 for induction + rc_layers: 1, // default 2 + + // parameters used to make "config" spectrum + reconfig: {}, + + // list to make "noise" spectrum mask + freqmasks: [], + + // field response waveform to make "response" spectrum. + response: {}, + + }, + + { + //channels: { wpid: wc.WirePlaneId(wc.Ulayer) }, + channels: std.range(apaid * 2560, apaid * 2560 + 800- 1), + /// this will use an average calculated from the anode + // response: { wpid: wc.WirePlaneId(wc.Ulayer) }, + /// this uses hard-coded waveform. + response_offset: 120, // offset of the negative peak + pad_window_front: 20, + decon_limit: 0.02, + decon_limit1: 0.07, + roi_min_max_ratio: 3.0, + }, + + { + //channels: { wpid: wc.WirePlaneId(wc.Vlayer) }, + channels: std.range(apaid * 2560 + 800, apaid * 2560 + 1600- 1), + /// this will use an average calculated from the anode + // response: { wpid: wc.WirePlaneId(wc.Vlayer) }, + /// this uses hard-coded waveform. + decon_limit: 0.01, + decon_limit1: 0.08, + roi_min_max_ratio: 1.5, + }, + + { + //channels: { wpid: wc.WirePlaneId(wc.Wlayer) }, + channels: std.range(apaid * 2560 + 1600, apaid * 2560 + 2560- 1), + nominal_baseline: 900.0, + decon_limit: 0.05, + decon_limit1: 0.08, + }, + + + ], + } // data + } // perfect() +} diff --git a/cfg/sp.jsonnet b/cfg/sp.jsonnet index a5928e4..1c8d9c5 100644 --- a/cfg/sp.jsonnet +++ b/cfg/sp.jsonnet @@ -75,7 +75,6 @@ function(anode, fieldresp, elecresp, spfilt, adcpermv, perchan=null) { mp2_roi_tag: 'mp2_roi%d' % apaid, isWrapped: false, - sparse: false, // process_planes: [0, 2], } }, nin=1, nout=1, uses=[anode, fieldresp, elecresp] + pcuses + spfilt), diff --git a/cfg/toyzero.jsonnet b/cfg/toyzero.jsonnet index 4445cd1..21493fa 100644 --- a/cfg/toyzero.jsonnet +++ b/cfg/toyzero.jsonnet @@ -116,7 +116,7 @@ local pg = import "pgraph.jsonnet"; // A per anode configure node for simulating noise. - noisesim(anode, noisef, daq, csdb=null, rnd=$.random()) : { + noisesim(anode, noisef, daq, chstat=null, rnd=$.random()) : { local apaid = anode.data.ident, local noise_model = { @@ -124,13 +124,13 @@ local pg = import "pgraph.jsonnet"; name: "emperical-noise-model-%d" % apaid, data: { anode: wc.tn(anode), - chanstat: if std.type(csdb) == "null" then "" else wc.tn(csdb), + chanstat: if std.type(chstat) == "null" then "" else wc.tn(chstat), spectra_file: noisef, nsamples: daq.nticks, period: daq.tick, wire_length_scale: 1.0*wc.cm, // optimization binning }, - uses: [anode] + if std.type(csdb) == "null" then [] else [csdb], + uses: [anode] + if std.type(chstat) == "null" then [] else [chstat], }, ret: pg.pnode({ type: "AddNoise", @@ -204,21 +204,19 @@ local pg = import "pgraph.jsonnet"; // The tier can be 'adc' or something else if no digitizer. sim(anode, pirs, daq, adc, lar, noisef=null, tier='adc', rnd=$.random()) : { - // fixme: make configurable - local csdb = null, + local apaid = anode.data.ident, local beg = if std.type(lar) == "null" || std.type(pirs) == "null" then [] else [ $.sigsim(anode, pirs, daq, lar, rnd)], local mid = if std.type(noisef) == "null" then [] else [ - $.noisesim(anode, noisef, daq, rnd, csdb)], + $.noisesim(anode, noisef, daq, rnd=rnd)], local end = if tier == 'adc' then [$.digisim(anode, adc)] else [], pipeline: pg.pipeline(beg + mid + end), }.pipeline, - local plugins = [ "WireCellSio", "WireCellAux", "WireCellGen", "WireCellSigProc", diff --git a/cfg/utils.jsonnet b/cfg/utils.jsonnet new file mode 100644 index 0000000..4441e63 --- /dev/null +++ b/cfg/utils.jsonnet @@ -0,0 +1,12 @@ +// fixme: many/all of these probably could get lifted to wct/cfg/ + +{ + // Return true if string s has at least one % symbol. + haspct(s, pct="%"):: std.length(std.findSubstr(pct, s))>0, + + // append a suffix to the base name of a file name, prior to .ext + basename_append(filename, suffix) :: { + local l = std.split(filename, "."), + ret:"%s%s.%s"%[l[0], suffix, l[1]] + }.ret, +} diff --git a/dot.envrc b/dot.envrc index 361e981..63d2db9 100644 --- a/dot.envrc +++ b/dot.envrc @@ -1,5 +1,6 @@ load_prefix $HOME/dev/ls4gan/toyzero/wire-cell-toolkit/install path_add WIRECELL_PATH $HOME/dev/ls4gan/toyzero/wire-cell-toolkit/cfg +path_add WIRECELL_PATH $HOME/dev/ls4gan/toyzero/wire-cell-data load_prefix $HOME/opt/jsonnet layout python3 diff --git a/testit.sh b/testit.sh new file mode 100755 index 0000000..58df2a9 --- /dev/null +++ b/testit.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -x + +tiers=(raw orig gauss threshold) + +taps="" +for tier in ${tiers[*]} +do + taps+="{\"$tier\":\"testit/fake-frames-${tier}.npz\"}" +done + +if [ -f testit/fake-frames-orig-apa0.npz ] ; then + echo "wire-cell has run already, rm testit if you want" +else + mkdir -p testit + wire-cell \ + -l stdout -L debug -P cfg\ + -A input=data/depos/depos.npz \ + --tla-code taps=$taps \ + -A wires=data/wires/wires.json.bz2 \ + -A resps=data/resps/fake-resps.json.bz2 \ + -A thread=multi \ + -A noisef=protodune-noise-spectra-v1.json.bz2 \ + -c cfg/main-depos-sigproc.jsonnet || exit +fi + + +for tier in raw orig gauss +do + for apaid in 0 1 2 3 4 5 + do + tag=${tier}${apaid} + wirecell-gen \ + plot-sim \ + testit/fake-frames-${tier}-apa${apaid}.npz \ + -p frames -b 0,2560 --tag ${tier}${apaid} \ + testit/fake-frames-${tier}-apa${apaid}.png + done +done + + + From aad34192571869939c4bfff71af9b2b116dccbe3 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Fri, 23 Jul 2021 16:35:26 -0400 Subject: [PATCH 05/15] Checkpoint --- Snakefile | 36 ++++++++++++++++++++---------------- testit.sh | 23 ++++++++++++++++++----- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/Snakefile b/Snakefile index 0fb5631..6a87de3 100644 --- a/Snakefile +++ b/Snakefile @@ -128,24 +128,28 @@ rule all_wires: def gen_depos_cfg(w): 'Dig out the bounding box of the detector' - params_cmd = 'wcsonnet pgrapher/experiment/pdsp/simparams.jsonnet' - jtext = subprocess.check_output(params_cmd, shell=True) - jdat = json.loads(jtext) - bb = jdat['det']['bounds'] - - p1 = bb['tail'] - p2 = bb['head'] - corn = list() - diag = list() - for l in "xyz": - c = p1[l] - dc = p2[l]-p1[l] - # warning, pretend we know WCT's SoU here.... - corn.append(f'{c:.1f}*mm') - diag.append(f'{dc:.1f}*mm') + # params_cmd = 'wcsonnet pgrapher/experiment/pdsp/simparams.jsonnet' + # jtext = subprocess.check_output(params_cmd, shell=True) + # jdat = json.loads(jtext) + # bb = jdat['det']['bounds'] + # p1 = bb['tail'] + # p2 = bb['head'] + # corn = list() + # diag = list() + # for l in "xyz": + # c = p1[l] + # dc = p2[l]-p1[l] + # # warning, pretend we know WCT's SoU here.... + # corn.append(f'{c:.1f}*mm') + # diag.append(f'{dc:.1f}*mm') + + # found by running once and looking at AnodePlane log msgs + diag = '16000.0*mm,6100.0*mm,7000.0*mm' + corn = '-8000.0*mm,0.0*mm,0.0*mm' return dict(tracks = ntracks, sets = nevents, seed = seed, - corn = ','.join(corn), diag = ','.join(diag)) + corn = corn, diag = diag) + #corn = ','.join(corn), diag = ','.join(diag)) rule gen_depos: input: diff --git a/testit.sh b/testit.sh index 58df2a9..fb8b398 100755 --- a/testit.sh +++ b/testit.sh @@ -2,7 +2,9 @@ set -x -tiers=(raw orig gauss threshold) +mkdir -p testit + +tiers=(orig gauss) taps="" for tier in ${tiers[*]} @@ -10,13 +12,24 @@ do taps+="{\"$tier\":\"testit/fake-frames-${tier}.npz\"}" done +if [ -f testit/depos.npz ] ; then + echo "depos already generated" +else + wirecell-gen depo-lines \ + --seed 1234 \ + --tracks 100 \ + --sets 1 \ + --diagonal '16000.0*mm,6100.0*mm, 7000.0*mm' \ + --corner '-8000.0*mm,0.0*mm,0.0*mm' \ + --output testit/depos.npz || exit +fi + if [ -f testit/fake-frames-orig-apa0.npz ] ; then echo "wire-cell has run already, rm testit if you want" else - mkdir -p testit wire-cell \ -l stdout -L debug -P cfg\ - -A input=data/depos/depos.npz \ + -A input=depos.npz \ --tla-code taps=$taps \ -A wires=data/wires/wires.json.bz2 \ -A resps=data/resps/fake-resps.json.bz2 \ @@ -26,7 +39,7 @@ else fi -for tier in raw orig gauss +for tier in orig gauss do for apaid in 0 1 2 3 4 5 do @@ -35,7 +48,7 @@ do plot-sim \ testit/fake-frames-${tier}-apa${apaid}.npz \ -p frames -b 0,2560 --tag ${tier}${apaid} \ - testit/fake-frames-${tier}-apa${apaid}.png + testit/fake-frames-${tier}-apa${apaid}.png || exit done done From f5d3b093bbf9ff5c80d33fdeb809ef134f7146fb Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Mon, 26 Jul 2021 17:55:25 -0400 Subject: [PATCH 06/15] Almost there to making full chain --- Snakefile | 168 ++++++++++++++++++++++++++---------------------------- testit.sh | 4 +- 2 files changed, 83 insertions(+), 89 deletions(-) diff --git a/Snakefile b/Snakefile index 6a87de3..aa9acaa 100644 --- a/Snakefile +++ b/Snakefile @@ -23,12 +23,10 @@ ntracks = config.get("ntracks", 10) nevents = config.get("nevents", 10) wcloglvl = config.get("wcloglvl", "info") -tiers = config.get("tiers", ["noiseless"]) - -# print(f"OUTDIR:{outdir}") -# print(f"NEVENTS:{nevents}") -# print(f"WCLOGLVL:{wcloglvl}") -print(f"TIERS:{tiers}, {type(tiers)}") +# limit number of threads per wire-cell job +threads = config.get("threads", 1) +# single threaded uses Pgrapher, multi uses TbbFlow +threading = "single" if threads == 1 else "multi" # The rest are hard wired for now wcdata_url = "https://github.com/WireCell/wire-cell-data/raw/master" @@ -49,8 +47,10 @@ apa_iota = list(range(6)) # associated with a particular detector response. DOMAINS = ["fake", "real"] -# The data tiers for frame or image -TIERS = ["noiseless", "signal"] +# The data tiers for frame or image. We make all tiers in one job. +# The 'orig' is the output of the simulation and 'gauss' is the output +# of signal processing using the charge-preserving filters. +TIERS = ["orig", "gauss"] # some important file names real_resps = f'{datadir}/resps/real-resps.{wcdata_ext}' @@ -58,6 +58,7 @@ fake_resps = f'{datadir}/resps/fake-resps.{wcdata_ext}' domain_resps = f'{datadir}/resps/{{domain}}-resps.{wcdata_ext}' wires_file = f'{datadir}/wires/wires.{wcdata_ext}' depos_file = f'{datadir}/depos/depos.npz' +noise_file = f'{datadir}/noise/protodune-noise-spectra-v1.{wcdata_ext}' # resp - prepare response files @@ -79,6 +80,15 @@ rule gen_resp_fake: wirecell-sigproc frzero -n 0 -o {output} {input} ''' +rule get_noisef: + input: + HTTP.remote(f'{wcdata_url}/protodune-noise-spectra-v1.{wcdata_ext}', keep_local=True) + output: + temp(noise_file) + run: + shell("mkdir -p {datadir}") + shell("cp {input} {output}") + rule plot_resp: input: domain_resps @@ -125,44 +135,19 @@ rule all_wires: # depos - generate ionization point depositions -def gen_depos_cfg(w): - 'Dig out the bounding box of the detector' - - # params_cmd = 'wcsonnet pgrapher/experiment/pdsp/simparams.jsonnet' - # jtext = subprocess.check_output(params_cmd, shell=True) - # jdat = json.loads(jtext) - # bb = jdat['det']['bounds'] - # p1 = bb['tail'] - # p2 = bb['head'] - # corn = list() - # diag = list() - # for l in "xyz": - # c = p1[l] - # dc = p2[l]-p1[l] - # # warning, pretend we know WCT's SoU here.... - # corn.append(f'{c:.1f}*mm') - # diag.append(f'{dc:.1f}*mm') - - # found by running once and looking at AnodePlane log msgs - diag = '16000.0*mm,6100.0*mm,7000.0*mm' - corn = '-8000.0*mm,0.0*mm,0.0*mm' - - return dict(tracks = ntracks, sets = nevents, seed = seed, - corn = corn, diag = diag) - #corn = ','.join(corn), diag = ','.join(diag)) - rule gen_depos: input: wires_file params: - p = gen_depos_cfg + diag = '16000.0*mm,6100.0*mm,7000.0*mm', + corn = '-8000.0*mm,0.0*mm,0.0*mm' output: temp(depos_file) shell: ''' wirecell-gen depo-lines \ - --seed {params.p[seed]} \ - --tracks {params.p[tracks]} --sets {params.p[sets]} \ - --diagonal '{params.p[diag]}' --corner '{params.p[corn]}' \ + --seed {seed} \ + --tracks {tracks} --sets {sets} \ + --diagonal '{diag}' --corner '{corn}' \ --output {output} ''' @@ -183,24 +168,20 @@ rule all_depos: # frames -def wct_cfg_file(w): - slugs = dict(signal="depos-sigproc", - noiseless="depos-sim-adc") - slug = slugs[w.tier] - return f'cfg/main-{slug}.jsonnet' +wct_cfg_file = 'cfg/main-sigproc.jsonnet' - -# note, passes bogus TLAs so don't use the genearted json! +# note, we pass bogus TLAs so don't use the generated json for +# anything real! rule wct_dots: input: config = wct_cfg_file output: - json = temp(f'{plotdir}/dots/{{tier}}/cfg.json'), - dot = temp(f'{plotdir}/dots/{{tier}}/dag.dot'), - png = f'{plotdir}/dots/{{tier}}/dag.png', - pdf = f'{plotdir}/dots/{{tier}}/dag.pdf' + json = temp(f'{plotdir}/dots/cfg.json'), + dot = temp(f'{plotdir}/dots/dag.dot'), + png = f'{plotdir}/dots/dag.png', + pdf = f'{plotdir}/dots/dag.pdf' shell: ''' - mkdir -p {plotdir}/dots/{wildcards.tier}; + mkdir -p {plotdir}/dots; wcsonnet \ -P cfg \ -A input=DEPOS-FILE \ @@ -214,50 +195,59 @@ rule wct_dots: ''' rule all_dots: input: - expand(rules.wct_dots.output, tier=tiers) + rules.wct_dots.output # this gives the pattern for one per-APA frame file. The %d is # interpolated by wire-cell configuration. -frames_pattern = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa%d.npz' + frames_wildcard = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa{{apa}}.npz' def frame_taps(w): - if w.tier == "noiseless": - tap = "orig" - else: - tap = "gauss" - fp = frames_pattern.format(**dict(w)) - return dict(tap=tap, pat=fp) + frames_pattern = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa%d.npz' + taps = list() + for tier in ('orig', 'gauss'): + d = dict(w) + d["tier"] = tier + taps.append('"%s":"%s"' % (tier, frames_pattern.format(**d))) + + taps = ",".join(taps) + return '{%s}'%taps + +def frame_files(): + frames_pattern = datadir + '/frames/{tier}/{{domain}}-frames-apa{apaid}.npz' + return expand(frames_pattern, tier=TIERS, apaid = apa_iota) rule sim_frames: input: wires = wires_file, resps = domain_resps, depos = depos_file, - config = wct_cfg_file + config = wct_cfg_file, + noise = noise_file output: - temp([frames_pattern%n for n in apa_iota]) + frame_files() params: - p = frame_taps + taps = frame_taps shell: ''' rm -f {output}; + mkdir -p {datadir}/frames/orig {datadir}/frames/gauss wire-cell \ + --threads 4 \ + -A thread={threading} \ -l stdout -L {config[wcloglvl]} \ -P cfg \ -A input={input.depos} \ - --tla-code 'taps={{"{params.p[tap]}":"{params.p[pat]}"}}' \ + --tla-code 'taps={params.taps}' \ -A wires={input.wires} \ -A resps={input.resps} \ + -A noisef={input.noise} \ -c {input.config} ''' def gen_plot_frames(w): i = int(w.apa) - if w.tier == "noiseless": - return dict(chb=f'{i},{i+2560}', tag="") - if w.tier == "signal": - tag = f"gauss{i}" - return dict(chb=f'0,2560', tag=tag) + tag = f"{w.tier}{i}" + return dict(chb=f'0,2560', tag=tag) rule plot_frames: input: @@ -284,13 +274,15 @@ rule plot_frames_hidpi: rule all_frames: input: - expand(rules.sim_frames.output, domain=["real","fake"], - tier=tiers), + rules.all_resp.input, + rules.all_wires.input, + rules.all_depos.input, + expand(rules.sim_frames.output, domain=["real","fake"]), expand(rules.plot_frames.output, domain=["real","fake"], - tier=tiers, + tier=TIERS, ext=["png","pdf"], apa=apa_iota), expand(rules.plot_frames_hidpi.output, domain=["real","fake"], - tier=tiers, apa=[0]) + tier=TIERS, apa=[0]) @@ -305,21 +297,21 @@ rule all_frames: ## static data (ie, defined right here). So, that is what we do. # split_outer_product = dict( -# domain = ["protodune"], event = list(range(nevents)), - apa = apa_iota, plane = ["U","V","W"], ) +# frames_wildcard = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa{{apa}}.npz' + rule split_images: input: frames_wildcard output: - expand(datadir+'/images/{{tier}}/{{domain}}/protodune-orig-{event}-{apa}-{plane}.npz', + expand(datadir+'/images/{{tier}}/{{domain}}/protodune-{{tier}}{{apa}}-{event}-{plane}.npz', **split_outer_product) shell: ''' wirecell-util frame-split \ - -f {datadir}/images/{wildcards.tier}/{wildcards.domain}/{{detector}}-{{tag}}-{{index}}-{{anodeid}}-{{planeletter}}.npz \ + -f {datadir}/images/{wildcards.tier}/{wildcards.domain}/{{detector}}-{{tag}}-{{index}}-{{planeletter}}.npz \ {input} ''' @@ -335,9 +327,9 @@ def gen_title(w): ## Above is 1->N, here is 1->1. rule plot_split_images: input: - datadir+'/images/{tier}/{domain}/protodune-orig-{event}-{apa}-{plane}.npz', + datadir+'/images/{tier}/{domain}/protodune-{tier}{apa}-{event}-{plane}.npz', output: - plotdir+'/images/{tier}/{domain}/{cmap}/protodune-orig-{event}-{apa}-{plane}.{ext}' + plotdir+'/images/{tier}/{domain}/{cmap}/protodune-{tier}{apa}-{event}-{plane}.{ext}' params: title = gen_title shell: ''' @@ -349,25 +341,27 @@ rule plot_split_images: --zoom 300:500,0:1000 --mask 0 --vmin -50 --vmax 50 \ --dpi 600 --baseline=median -o {output} {input} ''' + +rule just_images: + input: + rules.all_frames.input, + expand(rules.split_images.output, + domain=["real","fake"], + tier=TIERS, apa=apa_iota) + ## note, list-of-list for the split_images rule rule all_images: input: - expand(rules.split_images.output, - domain=["real","fake"], tier=tiers), + rules.just_images.input, expand( rules.plot_split_images.output, domain = ["real","fake"], - tier = tiers, - event = [0], apa=[2], plane=["U"], + tier = TIERS, + event = [0], apa=[0], plane=["U","V","W"], ext = ["png", "pdf", "svg"], - cmap = ["seismic", "Spectral", "terrain", "coolwarm", "viridis"], + cmap = ["seismic", "viridis"], ) -rule just_images: - input: - expand(rules.split_images.output, - domain=["real","fake"], - tier=tiers) rule all: input: diff --git a/testit.sh b/testit.sh index fb8b398..e4ff9fe 100755 --- a/testit.sh +++ b/testit.sh @@ -19,7 +19,7 @@ else --seed 1234 \ --tracks 100 \ --sets 1 \ - --diagonal '16000.0*mm,6100.0*mm, 7000.0*mm' \ + --diagonal '16000.0*mm,6100.0*mm,7000.0*mm' \ --corner '-8000.0*mm,0.0*mm,0.0*mm' \ --output testit/depos.npz || exit fi @@ -29,7 +29,7 @@ if [ -f testit/fake-frames-orig-apa0.npz ] ; then else wire-cell \ -l stdout -L debug -P cfg\ - -A input=depos.npz \ + -A input=testit/depos.npz \ --tla-code taps=$taps \ -A wires=data/wires/wires.json.bz2 \ -A resps=data/resps/fake-resps.json.bz2 \ From 7e1606e8cabe5c7d1f09858b7c06b3e87464ca23 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Tue, 27 Jul 2021 13:08:31 -0400 Subject: [PATCH 07/15] Variable name typo fixes --- Snakefile | 6 +++--- dot.envrc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Snakefile b/Snakefile index aa9acaa..b9785f4 100644 --- a/Snakefile +++ b/Snakefile @@ -146,8 +146,8 @@ rule gen_depos: shell: ''' wirecell-gen depo-lines \ --seed {seed} \ - --tracks {tracks} --sets {sets} \ - --diagonal '{diag}' --corner '{corn}' \ + --tracks {ntracks} --sets {nevents} \ + --diagonal '{params.diag}' --corner '{params.corn}' \ --output {output} ''' @@ -168,7 +168,7 @@ rule all_depos: # frames -wct_cfg_file = 'cfg/main-sigproc.jsonnet' +wct_cfg_file = 'cfg/main-depos-sigproc.jsonnet' # note, we pass bogus TLAs so don't use the generated json for # anything real! diff --git a/dot.envrc b/dot.envrc index 63d2db9..1798950 100644 --- a/dot.envrc +++ b/dot.envrc @@ -1,6 +1,6 @@ -load_prefix $HOME/dev/ls4gan/toyzero/wire-cell-toolkit/install -path_add WIRECELL_PATH $HOME/dev/ls4gan/toyzero/wire-cell-toolkit/cfg -path_add WIRECELL_PATH $HOME/dev/ls4gan/toyzero/wire-cell-data +load_prefix $HOME/wrk/ls4gan/toyzero/wire-cell-toolkit/install +path_add WIRECELL_PATH $HOME/wrk/ls4gan/toyzero/wire-cell-toolkit/cfg +path_add WIRECELL_PATH $HOME/wrk/ls4gan/toyzero/wire-cell-data load_prefix $HOME/opt/jsonnet layout python3 From 890dfc55f83a3e9a8f48115db4eb9675361c270c Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Wed, 28 Jul 2021 18:13:53 -0400 Subject: [PATCH 08/15] Allow different FRs for sim and sigproc --- Snakefile | 28 +++++++---- cfg/main-depos-sigproc.jsonnet | 15 +++--- cfg/toyzero.jsonnet | 3 +- cfg/utils.jsonnet | 11 +++++ testit.sh | 85 ++++++++++++++++++---------------- 5 files changed, 85 insertions(+), 57 deletions(-) diff --git a/Snakefile b/Snakefile index b9785f4..a2e7881 100644 --- a/Snakefile +++ b/Snakefile @@ -185,9 +185,11 @@ rule wct_dots: wcsonnet \ -P cfg \ -A input=DEPOS-FILE \ - --tla-code taps='{{"orig":"frame-orig-.npz","gauss":"frame-gauss.npz"}}' \ + --tla-code taps='{{"orig":"frame-orig.npz","gauss":"frame-gauss.npz"}}' \ -A wires=WIRES-FILE \ - -A resps=RESPS-FILE \ + -A resps_sim=RESPS-SIM-FILE \ + -A resps_sigproc=RESPS-SIGPROC-FILE \ + -A noisef=NOISE-FILE \ {input.config} > {output.json}; wirecell-pgraph dotify --no-params --jpath=-1 {output.json} {output.dot} ; dot -Tpng -o {output.png} {output.dot} ; @@ -220,17 +222,19 @@ def frame_files(): rule sim_frames: input: wires = wires_file, - resps = domain_resps, + resps_sim = domain_resps, + resps_sigproc = fake_resps, depos = depos_file, config = wct_cfg_file, noise = noise_file output: - frame_files() + temp(frame_files()) params: taps = frame_taps shell: ''' rm -f {output}; - mkdir -p {datadir}/frames/orig {datadir}/frames/gauss + mkdir -p {datadir}/frames/orig; + mkdir -p {datadir}/frames/gauss; wire-cell \ --threads 4 \ -A thread={threading} \ @@ -239,7 +243,8 @@ rule sim_frames: -A input={input.depos} \ --tla-code 'taps={params.taps}' \ -A wires={input.wires} \ - -A resps={input.resps} \ + -A resps_sim={input.resps_sim} \ + -A resps_sigproc={input.resps_sigproc} \ -A noisef={input.noise} \ -c {input.config} ''' @@ -272,12 +277,17 @@ rule plot_frames_hidpi: ''' +rule just_frames: + input: + expand(rules.sim_frames.output, domain=["real","fake"]), + + rule all_frames: input: rules.all_resp.input, rules.all_wires.input, rules.all_depos.input, - expand(rules.sim_frames.output, domain=["real","fake"]), + rules.just_frames.input, expand(rules.plot_frames.output, domain=["real","fake"], tier=TIERS, ext=["png","pdf"], apa=apa_iota), @@ -344,10 +354,10 @@ rule plot_split_images: rule just_images: input: - rules.all_frames.input, + rules.just_frames.input, expand(rules.split_images.output, domain=["real","fake"], - tier=TIERS, apa=apa_iota) + tier=["gauss"], apa=apa_iota) ## note, list-of-list for the split_images rule rule all_images: diff --git a/cfg/main-depos-sigproc.jsonnet b/cfg/main-depos-sigproc.jsonnet index f04e45d..78b9262 100644 --- a/cfg/main-depos-sigproc.jsonnet +++ b/cfg/main-depos-sigproc.jsonnet @@ -15,6 +15,8 @@ // -A taps={...see below...} \ // -A wires=wires-geometry.json.bz2 \ // -A resps=field-response.json.bz2 \ +// -A resps_sim=field-response-for-sim.json.bz2 \ +// -A resps_sigproc=field-response-for-sigproc.json.bz2 \ // -A noisef=/path/to/noise/model/file \ // -A thread=[single|multi] \ // -c main-depos-sim-adc.jsonnet @@ -45,7 +47,7 @@ local params = import "pgrapher/experiment/pdsp/simparams.jsonnet"; local spfilt = import "pgrapher/experiment/pdsp/sp-filters.jsonnet"; local chndb = import "pdsp_chndb.jsonnet"; -function(input, taps, wires, resps, noisef=null, thread='single') +function(input, taps, wires, resps_sim, resps_sigproc, noisef=null, thread='single') local app = if thread == 'single' then 'Pgrapher' else 'TbbFlow'; @@ -58,7 +60,8 @@ function(input, taps, wires, resps, noisef=null, thread='single') local apaids = std.range(0, std.length(anodes)-1); - local robjs = tz.responses(resps, params.elec, params.daq); + local robjs_sim = tz.responses(resps_sim, params.elec, params.daq); + local robjs_sigproc = tz.responses(resps_sigproc, params.elec, params.daq); local random = tz.random(seeds); @@ -66,7 +69,7 @@ function(input, taps, wires, resps, noisef=null, thread='single') local bagger = tz.bagger(params.daq); local chndb_perfect(n) = - chndb.perfect(anodes[n], robjs.fr, + chndb.perfect(anodes[n], robjs_sim.fr, params.daq.nticks, params.daq.tick); @@ -78,7 +81,7 @@ function(input, taps, wires, resps, noisef=null, thread='single') local sim(n) = [ local anode = anodes[n]; tz.sim(anode, // kitchen - robjs.pirs, // sink + robjs_sim.pirs, // sink params.daq, params.adc, params.lar, @@ -89,10 +92,10 @@ function(input, taps, wires, resps, noisef=null, thread='single') local adcpermv = tz.adcpermv(params.adc); local nfsp(n) = [ - nf(anodes[n], robjs.fr, chndb_perfect(n), + nf(anodes[n], robjs_sigproc.fr, chndb_perfect(n), params.daq.nticks, params.daq.tick), ] + tap_out("raw", n) + [ - sp(anodes[n], robjs.fr, robjs.er, spfilt, adcpermv) + sp(anodes[n], robjs_sigproc.fr, robjs_sigproc.er, spfilt, adcpermv) ] + tap_out("gauss", n); local oneapa(n) = pg.pipeline(sim(n) + nfsp(n)); diff --git a/cfg/toyzero.jsonnet b/cfg/toyzero.jsonnet index 21493fa..35b2c88 100644 --- a/cfg/toyzero.jsonnet +++ b/cfg/toyzero.jsonnet @@ -1,6 +1,6 @@ local wc = import "wirecell.jsonnet"; local pg = import "pgraph.jsonnet"; - +local ut = import "utils.jsonnet"; // define general object for toyzero @@ -26,6 +26,7 @@ local pg = import "pgraph.jsonnet"; field_response(filename) :: { type: "FieldResponse", + name: filename, data: { filename: filename } }, diff --git a/cfg/utils.jsonnet b/cfg/utils.jsonnet index 4441e63..e715b2c 100644 --- a/cfg/utils.jsonnet +++ b/cfg/utils.jsonnet @@ -4,6 +4,17 @@ // Return true if string s has at least one % symbol. haspct(s, pct="%"):: std.length(std.findSubstr(pct, s))>0, + stripext(fn, n=1) :: { + local parts = std.split(fn, "."), + local siz = std.length(parts), + ret: std.join(".", parts[:siz+1-n]) + }.ret, + + basename(fn) :: { + local parts = std.split(fn, "/"), + ret: parts[std.length(parts)-1] + }.ret, + // append a suffix to the base name of a file name, prior to .ext basename_append(filename, suffix) :: { local l = std.split(filename, "."), diff --git a/testit.sh b/testit.sh index e4ff9fe..abdda7e 100755 --- a/testit.sh +++ b/testit.sh @@ -5,50 +5,53 @@ set -x mkdir -p testit tiers=(orig gauss) - -taps="" -for tier in ${tiers[*]} +for domain in real fake do - taps+="{\"$tier\":\"testit/fake-frames-${tier}.npz\"}" -done + taps="" + for tier in ${tiers[*]} + do + taps+="{\"$tier\":\"testit/${domain}-frames-${tier}.npz\"}" + done -if [ -f testit/depos.npz ] ; then - echo "depos already generated" -else - wirecell-gen depo-lines \ - --seed 1234 \ - --tracks 100 \ - --sets 1 \ - --diagonal '16000.0*mm,6100.0*mm,7000.0*mm' \ - --corner '-8000.0*mm,0.0*mm,0.0*mm' \ - --output testit/depos.npz || exit -fi - -if [ -f testit/fake-frames-orig-apa0.npz ] ; then - echo "wire-cell has run already, rm testit if you want" -else - wire-cell \ - -l stdout -L debug -P cfg\ - -A input=testit/depos.npz \ - --tla-code taps=$taps \ - -A wires=data/wires/wires.json.bz2 \ - -A resps=data/resps/fake-resps.json.bz2 \ - -A thread=multi \ - -A noisef=protodune-noise-spectra-v1.json.bz2 \ - -c cfg/main-depos-sigproc.jsonnet || exit -fi - - -for tier in orig gauss -do - for apaid in 0 1 2 3 4 5 + if [ -f testit/depos.npz ] ; then + echo "depos already generated" + else + wirecell-gen depo-lines \ + --seed 1234 \ + --tracks 100 \ + --sets 1 \ + --diagonal '16000.0*mm,6100.0*mm,7000.0*mm' \ + --corner '-8000.0*mm,0.0*mm,0.0*mm' \ + --output testit/depos.npz || exit + fi + + if [ -f testit/${domain}-frames-orig-apa0.npz ] ; then + echo "wire-cell has run already for $domain" + else + wire-cell \ + -l stdout -L debug -P cfg\ + -A input=testit/depos.npz \ + --tla-code taps=$taps \ + -A wires=data/wires/wires.json.bz2 \ + -A resps_sim=data/resps/${domain}-resps.json.bz2 \ + -A resps_sigproc=data/resps/fake-resps.json.bz2 \ + -A thread=multi \ + -A noisef=protodune-noise-spectra-v1.json.bz2 \ + -c cfg/main-depos-sigproc.jsonnet || exit + fi + + + for tier in orig gauss do - tag=${tier}${apaid} - wirecell-gen \ - plot-sim \ - testit/fake-frames-${tier}-apa${apaid}.npz \ - -p frames -b 0,2560 --tag ${tier}${apaid} \ - testit/fake-frames-${tier}-apa${apaid}.png || exit + for apaid in 0 1 2 3 4 5 + do + tag=${tier}${apaid} + wirecell-gen \ + plot-sim \ + testit/${domain}-frames-${tier}-apa${apaid}.npz \ + -p frames -b 0,2560 --tag ${tier}${apaid} \ + testit/${domain}-frames-${tier}-apa${apaid}.png || exit + done done done From 68cc1bd27b7d49a93ab6cb67a1d96ad06e1bcbfc Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Thu, 29 Jul 2021 15:43:22 -0400 Subject: [PATCH 09/15] Remove mkdir now that WCT handles it and fix thread option pass through --- Snakefile | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Snakefile b/Snakefile index a2e7881..bb49813 100644 --- a/Snakefile +++ b/Snakefile @@ -19,14 +19,15 @@ datadir = os.path.abspath(os.path.join(outdir, datadir)) plotdir = os.path.abspath(os.path.join(outdir, plotdir)) seed = config.get("seed", "1,2,3,4") -ntracks = config.get("ntracks", 10) -nevents = config.get("nevents", 10) +ntracks = int(config.get("ntracks", 10)) +nevents = int(config.get("nevents", 10)) wcloglvl = config.get("wcloglvl", "info") # limit number of threads per wire-cell job -threads = config.get("threads", 1) +wct_threads = int(config.get("threads", 1)) # single threaded uses Pgrapher, multi uses TbbFlow -threading = "single" if threads == 1 else "multi" +wct_threading = "single" if wct_threads == 1 else "multi" +print(f'WCT THREADS {wct_threads} ({wct_threading})') # The rest are hard wired for now wcdata_url = "https://github.com/WireCell/wire-cell-data/raw/master" @@ -233,11 +234,9 @@ rule sim_frames: taps = frame_taps shell: ''' rm -f {output}; - mkdir -p {datadir}/frames/orig; - mkdir -p {datadir}/frames/gauss; wire-cell \ - --threads 4 \ - -A thread={threading} \ + --threads {wct_threads} \ + -A thread={wct_threading} \ -l stdout -L {config[wcloglvl]} \ -P cfg \ -A input={input.depos} \ @@ -249,6 +248,10 @@ rule sim_frames: -c {input.config} ''' + ## remove to check WCT makes output directories + # mkdir -p {datadir}/frames/orig; + # mkdir -p {datadir}/frames/gauss; + def gen_plot_frames(w): i = int(w.apa) tag = f"{w.tier}{i}" From c6d5f1b10e14a3a8f5d7f6f3621291aa44bc00dd Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Thu, 29 Jul 2021 18:06:19 -0400 Subject: [PATCH 10/15] Tweak plots --- Snakefile | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/Snakefile b/Snakefile index bb49813..514a442 100644 --- a/Snakefile +++ b/Snakefile @@ -328,12 +328,38 @@ rule split_images: {input} ''' -def gen_title(w): +def plot_split_params(w): if w.domain == 'real': dim='2D' else: dim='q1D' - return f'"{w.tier} {w.domain}/{dim}, event {w.event}, APA {w.apa}, {w.plane} plane"', + + if w.tier == "orig": + ztitle='ADC (baseline subtracted)' + vmin=-50 + vmax=50 + baseline="median" + else: + ztitle='Signal (ionization electrons)' + vmin=0 + vmax=5000 + baseline="0" + + blerg="0" + if int(w.zoomlevel) == 2: + blerg="" + + if w.plane == "U": + zoom=f'0:80{blerg},0:400{blerg}' + elif w.plane == "V": + zoom=f'0:80{blerg},0:400{blerg}' + elif w.plane == "W": + zoom=f'0:96{blerg},0:400{blerg}' + + title=f'{w.tier} {w.domain}/{dim}, event {w.event}, APA {w.apa}, {w.plane} plane' + return locals() + + ## Note, we must match the input here by hand to the output above ## because the domain is not included in the expand above but is here. @@ -342,17 +368,19 @@ rule plot_split_images: input: datadir+'/images/{tier}/{domain}/protodune-{tier}{apa}-{event}-{plane}.npz', output: - plotdir+'/images/{tier}/{domain}/{cmap}/protodune-{tier}{apa}-{event}-{plane}.{ext}' + plotdir+'/images/{tier}/{domain}/{cmap}/protodune-{tier}{apa}-{event}-{plane}-zoom{zoomlevel}.{ext}' params: - title = gen_title + p = plot_split_params shell: ''' wirecell-util npz-to-img --cmap {wildcards.cmap} \ - --title {params.title} \ + --title '{params.p[title]}' \ --xtitle 'Relative ticks number' \ --ytitle 'Relative channel number' \ - --ztitle 'ADC (baseline subtracted)' \ - --zoom 300:500,0:1000 --mask 0 --vmin -50 --vmax 50 \ - --dpi 600 --baseline=median -o {output} {input} + --ztitle '{params.p[ztitle]}' \ + --zoom '{params.p[zoom]}' \ + --vmin '{params.p[vmin]}' --vmax '{params.p[vmax]}' \ + --mask 0 \ + --dpi 600 --baseline='{params.p[baseline]}' -o {output} {input} ''' rule just_images: @@ -371,8 +399,9 @@ rule all_images: domain = ["real","fake"], tier = TIERS, event = [0], apa=[0], plane=["U","V","W"], - ext = ["png", "pdf", "svg"], + ext = ["png"], # , "pdf", "svg"], cmap = ["seismic", "viridis"], + zoomlevel=[1, 2], ) From a516c6586b1a8a07af6259e7253b9dac60ae14cf Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Tue, 3 Aug 2021 17:18:38 -0400 Subject: [PATCH 11/15] Add depo splat job as well as 2x2 outer product of (fake,real)x(sim,sigproc) --- Snakefile | 183 ++++++++++++++++++++++++--------- cfg/main-depos-sigproc.jsonnet | 3 +- cfg/main-depos-splat.jsonnet | 76 ++++++++++++++ cfg/toyzero.jsonnet | 22 ++++ 4 files changed, 231 insertions(+), 53 deletions(-) create mode 100644 cfg/main-depos-splat.jsonnet diff --git a/Snakefile b/Snakefile index 514a442..0a4aabc 100644 --- a/Snakefile +++ b/Snakefile @@ -1,7 +1,7 @@ #!/usr/bin/env snakemake -import json import os +import json from snakemake.remote.HTTP import RemoteProvider as HTTPRemoteProvider HTTP = HTTPRemoteProvider() @@ -51,7 +51,8 @@ DOMAINS = ["fake", "real"] # The data tiers for frame or image. We make all tiers in one job. # The 'orig' is the output of the simulation and 'gauss' is the output # of signal processing using the charge-preserving filters. -TIERS = ["orig", "gauss"] +SIM_TIERS = ["orig", "gauss"] +TIERS = SIM_TIERS + ["splat"] # some important file names real_resps = f'{datadir}/resps/real-resps.{wcdata_ext}' @@ -169,13 +170,15 @@ rule all_depos: # frames -wct_cfg_file = 'cfg/main-depos-sigproc.jsonnet' + +wct_sigproc_cfg = 'cfg/main-depos-sigproc.jsonnet' +wct_splat_cfg = 'cfg/main-depos-splat.jsonnet' # note, we pass bogus TLAs so don't use the generated json for # anything real! rule wct_dots: input: - config = wct_cfg_file + config = wct_sigproc_cfg output: json = temp(f'{plotdir}/dots/cfg.json'), dot = temp(f'{plotdir}/dots/dag.dot'), @@ -203,10 +206,10 @@ rule all_dots: # this gives the pattern for one per-APA frame file. The %d is # interpolated by wire-cell configuration. -frames_wildcard = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa{{apa}}.npz' +frames_wildcard = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-frames-apa{{apa}}.npz' def frame_taps(w): - frames_pattern = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa%d.npz' + frames_pattern = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-frames-apa%d.npz' taps = list() for tier in ('orig', 'gauss'): d = dict(w) @@ -216,20 +219,20 @@ def frame_taps(w): taps = ",".join(taps) return '{%s}'%taps -def frame_files(): - frames_pattern = datadir + '/frames/{tier}/{{domain}}-frames-apa{apaid}.npz' - return expand(frames_pattern, tier=TIERS, apaid = apa_iota) +def sim_frame_files(): + frames_pattern = datadir + '/frames/{tier}/{{sim_domain}}-{{sigproc_domain}}-frames-apa{apaid}.npz' + return expand(frames_pattern, tier=SIM_TIERS, apaid = apa_iota) rule sim_frames: input: wires = wires_file, - resps_sim = domain_resps, - resps_sigproc = fake_resps, + resps_sim = f'{datadir}/resps/{{sim_domain}}-resps.{wcdata_ext}', + resps_sigproc = f'{datadir}/resps/{{sigproc_domain}}-resps.{wcdata_ext}', depos = depos_file, - config = wct_cfg_file, + config = wct_sigproc_cfg, noise = noise_file output: - temp(frame_files()) + temp(sim_frame_files()) params: taps = frame_taps shell: ''' @@ -248,9 +251,30 @@ rule sim_frames: -c {input.config} ''' - ## remove to check WCT makes output directories - # mkdir -p {datadir}/frames/orig; - # mkdir -p {datadir}/frames/gauss; + +# like sim_frames but we use DepoSplat instead of sim+sigproc. +rule splat_frames: + input: + wires = wires_file, + depos = depos_file, + config = wct_splat_cfg + output: + temp([f'{datadir}/frames/splat/splat-frames-apa{apa}.npz' for apa in apa_iota]) + params: + taps = f'{{"splat":"{datadir}/frames/splat/splat-frames-apa%d.npz"}}' + shell: ''' + rm -f {output}; + wire-cell \ + --threads {wct_threads} \ + -A thread={wct_threading} \ + -l stdout -L {config[wcloglvl]} \ + -P cfg \ + -A input={input.depos} \ + --tla-code 'taps={params.taps}' \ + -A wires={input.wires} \ + -c {input.config} + ''' + def gen_plot_frames(w): i = int(w.apa) @@ -261,28 +285,50 @@ rule plot_frames: input: frames_wildcard output: - f'{plotdir}/frames-{{tier}}-{{domain}}-apa{{apa}}.{{ext}}' + f'{plotdir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-apa{{apa}}.{{ext}}' params: p = gen_plot_frames shell:''' wirecell-gen plot-sim {input} {output} -p frames -b {params.p[chb]} --tag "{params.p[tag]}" ''' +rule plot_splat_frames: + input: + f'{datadir}/frames/splat/splat-frames-apa{{apa}}.npz' + output: + f'{plotdir}/frames/splat/splat-apa{{apa}}.{{ext}}' + shell:''' + wirecell-gen plot-sim {input} {output} -p frames -b 0,2560 --tag splat{wildcards.apa}" + ''' + rule plot_frames_hidpi: input: - f'{plotdir}/frames-{{tier}}-{{domain}}-apa{{apa}}.pdf' + f'{plotdir}/frames-{{tier}}-{{sim_domain}}-{{sigproc_domain}}-apa{{apa}}.pdf' output: - f'{plotdir}/hidpi/frames-{{tier}}-{{domain}}-apa{{apa}}.png' - params: + f'{plotdir}/hidpi/frames-{{tier}}-{{sim_domain}}-{{sigproc_domain}}-apa{{apa}}.png' shell:''' pdftoppm -rx 600 -ry 600 {input} | pnmtopng > {output} ''' +rule just_splat_frames: + input: + rules.splat_frames.output + +rule all_splat_frames: + input: + rules.just_splat_frames.input, + expand(rules.plot_splat_frames.output, + ext=["png","pdf"], apa=apa_iota) + + rule just_frames: input: - expand(rules.sim_frames.output, domain=["real","fake"]), + expand(rules.sim_frames.output, + sim_domain=["real","fake"], + sigproc_domain=["real","fake"], + ), rule all_frames: @@ -291,11 +337,16 @@ rule all_frames: rules.all_wires.input, rules.all_depos.input, rules.just_frames.input, - expand(rules.plot_frames.output, domain=["real","fake"], - tier=TIERS, + expand(rules.plot_frames.output, + sim_domain=["real","fake"], + sigproc_domain=["real","fake"], + tier=SIM_TIERS, ext=["png","pdf"], apa=apa_iota), - expand(rules.plot_frames_hidpi.output, domain=["real","fake"], - tier=TIERS, apa=[0]) + + # expand(rules.plot_frames_hidpi.output, + # sim_domain=["real","fake"], + # sigproc_domain=["real","fake"], + # tier=TIERS, apa=[0]) @@ -316,23 +367,49 @@ split_outer_product = dict( # frames_wildcard = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa{{apa}}.npz' +# this pattern is formatted inside wirecell-util frame-split. +split_array_pattern = '{detector}-{tag}-{index}-{planeletter}' + +sigproc_rebin = 4 +def rebin_number(w): + if w.tier == "orig": + return 0; + return sigproc_rebin + rule split_images: input: frames_wildcard output: - expand(datadir+'/images/{{tier}}/{{domain}}/protodune-{{tier}}{{apa}}-{event}-{plane}.npz', + expand(datadir+'/images/{{tier}}/{{sim_domain}}-{{sigproc_domain}}/protodune-{{tier}}{{apa}}-{event}-{plane}.npz', **split_outer_product) - shell: ''' - wirecell-util frame-split \ - -f {datadir}/images/{wildcards.tier}/{wildcards.domain}/{{detector}}-{{tag}}-{{index}}-{{planeletter}}.npz \ - {input} - ''' + params: + outpath = datadir+'/images/{tier}/{sim_domain}-{sigproc_domain}', + mdpath = 'metadata-{apa}.json', + rebin = rebin_number + run: + if not os.path.exists(params.outpath): + os.makedirs(params.outpath) + mdpath = os.path.join(params.outpath, params.mdpath) + with open(mdpath, "w") as fp: + fp.write(json.dumps(dict(wildcards), indent=4)) + shell('wirecell-util frame-split -r {params.rebin} -m {mdpath} -a {params.outpath}/{split_array_pattern} {input}') + def plot_split_params(w): - if w.domain == 'real': - dim='2D' + if w.tier == 'splat': + dim="splat" + elif w.sim_domain == 'real': + dim='sim:2D' else: - dim='q1D' + dim='sim:q1D' + + if w.tier == "gauss": + # only one response is relevant to ADC tier + # signals may have a different response used in decon. + if w.sigproc_domain == 'real': + dim+='/SP:2D' + else: + dim+='/SP:q1D' if w.tier == "orig": ztitle='ADC (baseline subtracted)' @@ -342,21 +419,23 @@ def plot_split_params(w): else: ztitle='Signal (ionization electrons)' vmin=0 - vmax=5000 + vmax=20000 baseline="0" - blerg="0" - if int(w.zoomlevel) == 2: - blerg="" - - if w.plane == "U": - zoom=f'0:80{blerg},0:400{blerg}' - elif w.plane == "V": - zoom=f'0:80{blerg},0:400{blerg}' - elif w.plane == "W": - zoom=f'0:96{blerg},0:400{blerg}' + # we make 2 zoom levels. 1 is full, 2 is something less. + factor = 2 + if int(w.zoomlevel) == 1: + factor = 10 + if w.plane == "W": + chanmax=96*factor + else: + chanmax=80*factor + tickmax=400*factor + if w.tier != "orig": + tickmax = tickmax // sigproc_rebin + zoom=f'0:{chanmax},0:{tickmax}' - title=f'{w.tier} {w.domain}/{dim}, event {w.event}, APA {w.apa}, {w.plane} plane' + title=f'{w.tier} {dim}, event {w.event}, APA {w.apa}, {w.plane} plane' return locals() @@ -366,9 +445,9 @@ def plot_split_params(w): ## Above is 1->N, here is 1->1. rule plot_split_images: input: - datadir+'/images/{tier}/{domain}/protodune-{tier}{apa}-{event}-{plane}.npz', + datadir+'/images/{tier}/{sim_domain}-{sigproc_domain}/protodune-{tier}{apa}-{event}-{plane}.npz', output: - plotdir+'/images/{tier}/{domain}/{cmap}/protodune-{tier}{apa}-{event}-{plane}-zoom{zoomlevel}.{ext}' + plotdir+'/images/{tier}/{sim_domain}-{sigproc_domain}/{cmap}/protodune-{tier}{apa}-{event}-{plane}-zoom{zoomlevel}.{ext}' params: p = plot_split_params shell: ''' @@ -387,7 +466,8 @@ rule just_images: input: rules.just_frames.input, expand(rules.split_images.output, - domain=["real","fake"], + sim_domain=["real","fake"], + sigproc_domain=["real","fake"], tier=["gauss"], apa=apa_iota) ## note, list-of-list for the split_images rule @@ -396,8 +476,9 @@ rule all_images: rules.just_images.input, expand( rules.plot_split_images.output, - domain = ["real","fake"], - tier = TIERS, + sim_domain = ["real","fake"], + sigproc_domain = ["real","fake"], + tier = SIM_TIERS, event = [0], apa=[0], plane=["U","V","W"], ext = ["png"], # , "pdf", "svg"], cmap = ["seismic", "viridis"], diff --git a/cfg/main-depos-sigproc.jsonnet b/cfg/main-depos-sigproc.jsonnet index 78b9262..2fc5cc3 100644 --- a/cfg/main-depos-sigproc.jsonnet +++ b/cfg/main-depos-sigproc.jsonnet @@ -14,12 +14,11 @@ // -A depos=depos.npz \ // -A taps={...see below...} \ // -A wires=wires-geometry.json.bz2 \ -// -A resps=field-response.json.bz2 \ // -A resps_sim=field-response-for-sim.json.bz2 \ // -A resps_sigproc=field-response-for-sigproc.json.bz2 \ // -A noisef=/path/to/noise/model/file \ // -A thread=[single|multi] \ -// -c main-depos-sim-adc.jsonnet +// -c main-depos-sigproc.jsonnet // // The 'noisef' TLA is optional and specifies a noise model file. If // not given, only signal is simulated. diff --git a/cfg/main-depos-splat.jsonnet b/cfg/main-depos-splat.jsonnet new file mode 100644 index 0000000..fb888ff --- /dev/null +++ b/cfg/main-depos-splat.jsonnet @@ -0,0 +1,76 @@ +// This is a main wire-cell configuration file. +// +// It implements the chain: +// (depos)->splat->(signals) +// +// It takes a number of top-level-arguments (TLA) which can control +// input configuration and input data and a number of output data +// "taps". You may also control if it runs in single or multi thread +// mode +// +// Run like +// +// wire-cell \ +// -A depos=depos.npz \ +// -A taps={...see below...} \ +// -A wires=wires-geometry.json.bz2 \ +// -A thread=[single|multi] \ +// -c main-depos-splat.jsonnet +// +// If "taps" is given it specifies a mapping from a data tier key word +// to a file name. The key is one of the conventional tags: +// +// - splat :: the output of DepoSplat. +// +// The file name may have a "%" formatter which will be interpolated +// against the APA ID number. If omitted, "-apa%d" will be inserted +// at the end of the base file name just prior to the extention +// (likely .npz). + +local wc = import "wirecell.jsonnet"; +local pg = import "pgraph.jsonnet"; + +local tz = import "toyzero.jsonnet"; +local io = import "ioutils.jsonnet"; + +local params = import "pgrapher/experiment/pdsp/simparams.jsonnet"; +local spfilt = import "pgrapher/experiment/pdsp/sp-filters.jsonnet"; +local chndb = import "pdsp_chndb.jsonnet"; + +function(input, taps, wires, thread='single') + local app = if thread == 'single' + then 'Pgrapher' + else 'TbbFlow'; + local seeds = [0,1,2,3,4]; // maybe let CLI set? + local depos = io.npz_source_depo("depos", input); + + + local wireobj = tz.wire_file(wires); + local anodes = tz.anodes(wireobj, params.det.volumes); + + local apaids = std.range(0, std.length(anodes)-1); + + local random = tz.random(seeds); + + local drifter = tz.drifter(params.det.volumes, params.lar, random); + + local tap_out(tap, apaid) = + if std.objectHas(taps, tap) + then [io.frame_out(tap+"%d", apaid, taps[tap], tags=[tap+"%d"], cap = tap=="gauss")] + else []; + + local splat(n, ) = [ + local anode = anodes[n]; + tz.splat(anode, params.daq, params.lar) + ] + tap_out("splat", n); + + local oneapa(n) = pg.pipeline(splat(n)); + + local pipes = [oneapa(n) for n in apaids]; + + local body = pg.fan.fanout('DepoFanout', pipes); + local full = pg.pipeline([depos, drifter, body]); + + tz.main(full, app) + + diff --git a/cfg/toyzero.jsonnet b/cfg/toyzero.jsonnet index 35b2c88..62c76f2 100644 --- a/cfg/toyzero.jsonnet +++ b/cfg/toyzero.jsonnet @@ -218,6 +218,28 @@ local ut = import "utils.jsonnet"; pipeline: pg.pipeline(beg + mid + end), }.pipeline, + + // Return a DepoSplat node + splat(anode, daq, lar) :: pg.pnode({ + type: "DepoSplat", + name: "splat%d"%anode.data.ident, + data: { + tag: "splat", + anode: wc.tn(anode), + continuous: false, + fixed: true, + drift_speed: lar.drift_speed, + readout_time: daq.nticks*daq.tick, + start_time: 0, + tick: daq.tick, + nsigma: 3, + } + }, nin=1, nout=1, uses=[anode]), + + + + // top-level stuff + local plugins = [ "WireCellSio", "WireCellAux", "WireCellGen", "WireCellSigProc", From 01547d18f3815f3c805c429a4dce082c1be5cb12 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Thu, 5 Aug 2021 14:48:32 -0400 Subject: [PATCH 12/15] Fix depo times, add 'splat' sim+sigproc --- Snakefile | 147 +++++++++++++++++++++++++---------- cfg/main-depos-splat.jsonnet | 2 +- cfg/toyzero.jsonnet | 60 ++++++++++---- 3 files changed, 153 insertions(+), 56 deletions(-) diff --git a/Snakefile b/Snakefile index 0a4aabc..b1bd10a 100644 --- a/Snakefile +++ b/Snakefile @@ -21,6 +21,7 @@ plotdir = os.path.abspath(os.path.join(outdir, plotdir)) seed = config.get("seed", "1,2,3,4") ntracks = int(config.get("ntracks", 10)) nevents = int(config.get("nevents", 10)) +depotimes = config.get("depotimes", "-3*ms,6*ms") wcloglvl = config.get("wcloglvl", "info") # limit number of threads per wire-cell job @@ -147,6 +148,7 @@ rule gen_depos: temp(depos_file) shell: ''' wirecell-gen depo-lines \ + --time {depotimes} \ --seed {seed} \ --tracks {ntracks} --sets {nevents} \ --diagonal '{params.diag}' --corner '{params.corn}' \ @@ -298,17 +300,7 @@ rule plot_splat_frames: output: f'{plotdir}/frames/splat/splat-apa{{apa}}.{{ext}}' shell:''' - wirecell-gen plot-sim {input} {output} -p frames -b 0,2560 --tag splat{wildcards.apa}" - ''' - -rule plot_frames_hidpi: - input: - f'{plotdir}/frames-{{tier}}-{{sim_domain}}-{{sigproc_domain}}-apa{{apa}}.pdf' - output: - f'{plotdir}/hidpi/frames-{{tier}}-{{sim_domain}}-{{sigproc_domain}}-apa{{apa}}.png' - - shell:''' - pdftoppm -rx 600 -ry 600 {input} | pnmtopng > {output} + wirecell-gen plot-sim {input} {output} -p frames -b 0,2560 --tag splat{wildcards.apa} ''' @@ -320,7 +312,7 @@ rule all_splat_frames: input: rules.just_splat_frames.input, expand(rules.plot_splat_frames.output, - ext=["png","pdf"], apa=apa_iota) + ext=["png"], apa=apa_iota) rule just_frames: @@ -341,7 +333,7 @@ rule all_frames: sim_domain=["real","fake"], sigproc_domain=["real","fake"], tier=SIM_TIERS, - ext=["png","pdf"], apa=apa_iota), + ext=["png"], apa=apa_iota), # expand(rules.plot_frames_hidpi.output, # sim_domain=["real","fake"], @@ -375,6 +367,12 @@ def rebin_number(w): if w.tier == "orig": return 0; return sigproc_rebin +def tick_offset(w): + if w.tier == 'splat': + # approx, chosen by comparing gauss W to splat W plots + # this is pre-rebin ticks, 0.5us. + return 125+8 + return 0 rule split_images: input: @@ -385,14 +383,34 @@ rule split_images: params: outpath = datadir+'/images/{tier}/{sim_domain}-{sigproc_domain}', mdpath = 'metadata-{apa}.json', - rebin = rebin_number + rebin = rebin_number, + tickoff = tick_offset + run: + if not os.path.exists(params.outpath): + os.makedirs(params.outpath) + mdpath = os.path.join(params.outpath, params.mdpath) + with open(mdpath, "w") as fp: + fp.write(json.dumps(dict(wildcards), indent=4)) + shell('wirecell-util frame-split -t {params.tickoff} -r {params.rebin} -m {mdpath} -a {params.outpath}/{split_array_pattern} {input}') + +rule split_splat_images: + input: + f'{datadir}/frames/splat/splat-frames-apa{{apa}}.npz' + output: + expand(datadir+'/images/{{tier}}/splat/protodune-{{tier}}{{apa}}-{event}-{plane}.npz', + **split_outer_product) + params: + outpath = datadir+'/images/{tier}/splat', + mdpath = 'metadata-{apa}.json', + rebin = rebin_number, + tickoff = tick_offset run: if not os.path.exists(params.outpath): os.makedirs(params.outpath) mdpath = os.path.join(params.outpath, params.mdpath) with open(mdpath, "w") as fp: fp.write(json.dumps(dict(wildcards), indent=4)) - shell('wirecell-util frame-split -r {params.rebin} -m {mdpath} -a {params.outpath}/{split_array_pattern} {input}') + shell('wirecell-util frame-split -t {params.tickoff} -r {params.rebin} -m {mdpath} -a {params.outpath}/{split_array_pattern} {input}') def plot_split_params(w): @@ -422,20 +440,26 @@ def plot_split_params(w): vmax=20000 baseline="0" - # we make 2 zoom levels. 1 is full, 2 is something less. - factor = 2 - if int(w.zoomlevel) == 1: - factor = 10 - if w.plane == "W": - chanmax=96*factor - else: - chanmax=80*factor - tickmax=400*factor + # we make 2 zoom levels. 1 is full, 2 is something zoomed in. + + chan0 = 0 + nchans = 960 if w.plane == "W" else 800 + tick0 = 0 + nticks = 6000 + + # pick a region with good activity in all 3 views + if int(w.zoomlevel) == 2: + nchans = int(nchans / 5) + tick0 = 3600 + nticks = 800 + if w.tier != "orig": - tickmax = tickmax // sigproc_rebin - zoom=f'0:{chanmax},0:{tickmax}' + tick0 = int(tick0/sigproc_rebin) + nticks = int(nticks/sigproc_rebin) + + zoom = f'{chan0}:{chan0+nchans},{tick0}:{tick0+nticks}' + title = f'{w.tier} {dim}, event {w.event}, APA {w.apa}, {w.plane} plane' - title=f'{w.tier} {dim}, event {w.event}, APA {w.apa}, {w.plane} plane' return locals() @@ -443,14 +467,7 @@ def plot_split_params(w): ## Note, we must match the input here by hand to the output above ## because the domain is not included in the expand above but is here. ## Above is 1->N, here is 1->1. -rule plot_split_images: - input: - datadir+'/images/{tier}/{sim_domain}-{sigproc_domain}/protodune-{tier}{apa}-{event}-{plane}.npz', - output: - plotdir+'/images/{tier}/{sim_domain}-{sigproc_domain}/{cmap}/protodune-{tier}{apa}-{event}-{plane}-zoom{zoomlevel}.{ext}' - params: - p = plot_split_params - shell: ''' +plot_split_shell = ''' wirecell-util npz-to-img --cmap {wildcards.cmap} \ --title '{params.p[title]}' \ --xtitle 'Relative ticks number' \ @@ -461,6 +478,23 @@ rule plot_split_images: --mask 0 \ --dpi 600 --baseline='{params.p[baseline]}' -o {output} {input} ''' +rule plot_split_images: + input: + datadir+'/images/{tier}/{sim_domain}-{sigproc_domain}/protodune-{tier}{apa}-{event}-{plane}.npz', + output: + plotdir+'/images/{tier}/{sim_domain}-{sigproc_domain}/{cmap}/protodune-{tier}{apa}-{event}-{plane}-zoom{zoomlevel}.{ext}' + params: + p = plot_split_params + shell: plot_split_shell + +rule plot_split_splat_images: + input: + datadir+'/images/{tier}/splat/protodune-{tier}{apa}-{event}-{plane}.npz', + output: + plotdir+'/images/{tier}/splat/{cmap}/protodune-{tier}{apa}-{event}-{plane}-zoom{zoomlevel}.{ext}' + params: + p = plot_split_params + shell: plot_split_shell rule just_images: input: @@ -470,18 +504,53 @@ rule just_images: sigproc_domain=["real","fake"], tier=["gauss"], apa=apa_iota) +rule just_splat_images: + input: + rules.just_splat_frames.input, + expand(rules.split_splat_images.output, + tier=["splat"], apa=apa_iota) + ## note, list-of-list for the split_images rule rule all_images: input: rules.just_images.input, + rules.just_splat_images.input, + expand( + rules.plot_split_images.output, + sim_domain = ["fake"], + sigproc_domain = ["fake"], + tier = ["orig"], + event = [0], apa=apa_iota, plane=["U","V","W"], + ext = ["png"], + cmap = ["seismic"], + zoomlevel=[1, 2], + ), + expand( + rules.plot_split_images.output, + sim_domain = ["real"], + sigproc_domain = ["real"], + tier = ["orig"], + event = [0], apa=apa_iota, plane=["U","V","W"], + ext = ["png"], + cmap = ["seismic"], + zoomlevel=[1, 2], + ), expand( rules.plot_split_images.output, sim_domain = ["real","fake"], sigproc_domain = ["real","fake"], - tier = SIM_TIERS, - event = [0], apa=[0], plane=["U","V","W"], - ext = ["png"], # , "pdf", "svg"], - cmap = ["seismic", "viridis"], + tier = ["gauss"], + event = [0], apa=apa_iota, plane=["U","V","W"], + ext = ["png"], + cmap = ["viridis"], + zoomlevel=[1, 2], + ), + expand( + rules.plot_split_splat_images.output, + tier = ["splat"], + event = [0], apa=apa_iota, plane=["U","V","W"], + ext = ["png"], + cmap = ["viridis"], zoomlevel=[1, 2], ) diff --git a/cfg/main-depos-splat.jsonnet b/cfg/main-depos-splat.jsonnet index fb888ff..5aca79f 100644 --- a/cfg/main-depos-splat.jsonnet +++ b/cfg/main-depos-splat.jsonnet @@ -61,7 +61,7 @@ function(input, taps, wires, thread='single') local splat(n, ) = [ local anode = anodes[n]; - tz.splat(anode, params.daq, params.lar) + tz.splat(anode, params.daq, params.lar, random) ] + tap_out("splat", n); local oneapa(n) = pg.pipeline(splat(n)); diff --git a/cfg/toyzero.jsonnet b/cfg/toyzero.jsonnet index 62c76f2..4fda8a0 100644 --- a/cfg/toyzero.jsonnet +++ b/cfg/toyzero.jsonnet @@ -188,7 +188,7 @@ local ut = import "utils.jsonnet"; toffset: 0, nticks: daq.nticks, }, - }, nin=1, nout=1), + }, nin=1, nout=1, uses=[anode]), ret: pg.pipeline([ductor, reframer]), }.ret, @@ -220,22 +220,50 @@ local ut = import "utils.jsonnet"; // Return a DepoSplat node - splat(anode, daq, lar) :: pg.pnode({ - type: "DepoSplat", - name: "splat%d"%anode.data.ident, - data: { - tag: "splat", - anode: wc.tn(anode), - continuous: false, - fixed: true, - drift_speed: lar.drift_speed, - readout_time: daq.nticks*daq.tick, - start_time: 0, - tick: daq.tick, - nsigma: 3, - } - }, nin=1, nout=1, uses=[anode]), + splat(anode, daq, lar, rnd=null) :: { + + local apaid = anode.data.ident, + local frame_tag = "splat%d" % apaid, + + local rextra = if std.type(rnd) == "null" + then { data:{}, uses:[] } + else { + data: { fluctuate:true, rng: wc.tn(rnd) }, + uses: [rnd] + }, + + local splat = pg.pnode({ + type: "DepoSplat", + name: "splat%d"%apaid, + data: { + frame_tag: frame_tag, + anode: wc.tn(anode), + continuous: false, + fixed: true, + drift_speed: lar.drift_speed, + readout_time: daq.nticks*daq.tick, + start_time: 0, + tick: daq.tick, + nsigma: 3, + } + rextra.data + }, nin=1, nout=1, uses=[anode]+rextra.uses), + + local reframer = pg.pnode({ + type: 'Reframer', + name: 'Reframer%d' % apaid, + data: { + anode: wc.tn(anode), + tags: [], + frame_tag: frame_tag, + fill: 0.0, + tbin: 0, + toffset: 0, + nticks: daq.nticks, + }, + }, nin=1, nout=1, uses=[anode]), + ret: pg.pipeline([splat, reframer]), + }.ret, // top-level stuff From 56a03470c452a9576713cc3daa56bf18bd3f8903 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Tue, 17 Aug 2021 13:09:39 -0400 Subject: [PATCH 13/15] Get working with WCT new support for compressed tar streams --- Snakefile | 51 ++++++++++++++++++++-------------- cfg/ioutils.jsonnet | 28 +++++++++++++++++-- cfg/main-depos-sigproc.jsonnet | 22 ++++++++------- cfg/main-depos-splat.jsonnet | 20 +++++++------ 4 files changed, 79 insertions(+), 42 deletions(-) diff --git a/Snakefile b/Snakefile index b1bd10a..9d86c6a 100644 --- a/Snakefile +++ b/Snakefile @@ -13,10 +13,12 @@ configfile: "toyzero.yaml" ## $ snakemake --config ntracks=100 [...] datadir = config.get("datadir", "data") plotdir = config.get("plotdir", "plots") +logdir = config.get("logdir", "logs") outdir = config.get("outdir", os.environ.get("TOYZERO_OUTDIR", ".")) datadir = os.path.abspath(os.path.join(outdir, datadir)) plotdir = os.path.abspath(os.path.join(outdir, plotdir)) +logdir = os.path.abspath(os.path.join(outdir, logdir)) seed = config.get("seed", "1,2,3,4") ntracks = int(config.get("ntracks", 10)) @@ -176,42 +178,49 @@ rule all_depos: wct_sigproc_cfg = 'cfg/main-depos-sigproc.jsonnet' wct_splat_cfg = 'cfg/main-depos-splat.jsonnet' +def wct_dots_params(w): + if w.verb == "brief": + return "--no-params" + return "" # note, we pass bogus TLAs so don't use the generated json for # anything real! rule wct_dots: input: config = wct_sigproc_cfg output: - json = temp(f'{plotdir}/dots/cfg.json'), - dot = temp(f'{plotdir}/dots/dag.dot'), - png = f'{plotdir}/dots/dag.png', - pdf = f'{plotdir}/dots/dag.pdf' + json = temp(f'{plotdir}/dots/cfg-{{verb}}.json'), + dot = temp(f'{plotdir}/dots/dag-{{verb}}.dot'), + png = f'{plotdir}/dots/dag-{{verb}}.png', + pdf = f'{plotdir}/dots/dag-{{verb}}.pdf' + params: + wct_dots_params shell: ''' mkdir -p {plotdir}/dots; wcsonnet \ -P cfg \ -A input=DEPOS-FILE \ - --tla-code taps='{{"orig":"frame-orig.npz","gauss":"frame-gauss.npz"}}' \ + --tla-code taps='{{"orig":"frame-orig-apa%d.tar.bz2","gauss":"frame-gauss-apa%d.tar.bz2"}}' \ -A wires=WIRES-FILE \ -A resps_sim=RESPS-SIM-FILE \ -A resps_sigproc=RESPS-SIGPROC-FILE \ -A noisef=NOISE-FILE \ {input.config} > {output.json}; - wirecell-pgraph dotify --no-params --jpath=-1 {output.json} {output.dot} ; + wirecell-pgraph dotify {params} --jpath=-1 {output.json} {output.dot} ; dot -Tpng -o {output.png} {output.dot} ; dot -Tpdf -o {output.pdf} {output.dot} ''' rule all_dots: input: - rules.wct_dots.output + expand(rules.wct_dots.output, verb=["full", "brief"]) # this gives the pattern for one per-APA frame file. The %d is # interpolated by wire-cell configuration. -frames_wildcard = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-frames-apa{{apa}}.npz' +frames_ext = "tar.bz2" +frames_wildcard = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-frames-apa{{apa}}.{frames_ext}' def frame_taps(w): - frames_pattern = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-frames-apa%d.npz' + frames_pattern = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-frames-apa%d.{frames_ext}' taps = list() for tier in ('orig', 'gauss'): d = dict(w) @@ -222,7 +231,7 @@ def frame_taps(w): return '{%s}'%taps def sim_frame_files(): - frames_pattern = datadir + '/frames/{tier}/{{sim_domain}}-{{sigproc_domain}}-frames-apa{apaid}.npz' + frames_pattern = datadir + '/frames/{tier}/{{sim_domain}}-{{sigproc_domain}}-frames-apa{apaid}.' + frames_ext return expand(frames_pattern, tier=SIM_TIERS, apaid = apa_iota) rule sim_frames: @@ -237,6 +246,8 @@ rule sim_frames: temp(sim_frame_files()) params: taps = frame_taps + benchmark: + f'{logdir}/sim-frames-{{sim_domain}}-{{sigproc_domain}}.tsv' shell: ''' rm -f {output}; wire-cell \ @@ -261,9 +272,9 @@ rule splat_frames: depos = depos_file, config = wct_splat_cfg output: - temp([f'{datadir}/frames/splat/splat-frames-apa{apa}.npz' for apa in apa_iota]) + temp([f'{datadir}/frames/splat/splat-frames-apa{apa}.{frames_ext}' for apa in apa_iota]) params: - taps = f'{{"splat":"{datadir}/frames/splat/splat-frames-apa%d.npz"}}' + taps = f'{{"splat":"{datadir}/frames/splat/splat-frames-apa%d.{frames_ext}"}}' shell: ''' rm -f {output}; wire-cell \ @@ -296,7 +307,7 @@ rule plot_frames: rule plot_splat_frames: input: - f'{datadir}/frames/splat/splat-frames-apa{{apa}}.npz' + f'{datadir}/frames/splat/splat-frames-apa{{apa}}.tar.gz' output: f'{plotdir}/frames/splat/splat-apa{{apa}}.{{ext}}' shell:''' @@ -357,7 +368,7 @@ split_outer_product = dict( plane = ["U","V","W"], ) -# frames_wildcard = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa{{apa}}.npz' +# frames_wildcard = f'{datadir}/frames/{{tier}}/{{domain}}-frames-apa{{apa}}.{frames_ext}' # this pattern is formatted inside wirecell-util frame-split. split_array_pattern = '{detector}-{tag}-{index}-{planeletter}' @@ -385,6 +396,8 @@ rule split_images: mdpath = 'metadata-{apa}.json', rebin = rebin_number, tickoff = tick_offset + benchmark: + f'{logdir}/split-images-{{tier}}-{{sim_domain}}-{{sigproc_domain}}-apa{{apa}}.tsv' run: if not os.path.exists(params.outpath): os.makedirs(params.outpath) @@ -395,7 +408,7 @@ rule split_images: rule split_splat_images: input: - f'{datadir}/frames/splat/splat-frames-apa{{apa}}.npz' + f'{datadir}/frames/splat/splat-frames-apa{{apa}}.{frames_ext}' output: expand(datadir+'/images/{{tier}}/splat/protodune-{{tier}}{{apa}}-{event}-{plane}.npz', **split_outer_product) @@ -499,14 +512,11 @@ rule plot_split_splat_images: rule just_images: input: rules.just_frames.input, + rules.just_splat_frames.input, expand(rules.split_images.output, sim_domain=["real","fake"], sigproc_domain=["real","fake"], - tier=["gauss"], apa=apa_iota) - -rule just_splat_images: - input: - rules.just_splat_frames.input, + tier=["gauss"], apa=apa_iota), expand(rules.split_splat_images.output, tier=["splat"], apa=apa_iota) @@ -514,7 +524,6 @@ rule just_splat_images: rule all_images: input: rules.just_images.input, - rules.just_splat_images.input, expand( rules.plot_split_images.output, sim_domain = ["fake"], diff --git a/cfg/ioutils.jsonnet b/cfg/ioutils.jsonnet index 16f0a2b..43abb84 100644 --- a/cfg/ioutils.jsonnet +++ b/cfg/ioutils.jsonnet @@ -25,12 +25,36 @@ local ut = import 'utils.jsonnet'; }}, nin=1, nout=1), // Use to cap off a frame stream with a sink. - frame_sink(name="frame-sink") :: pg.pnode({ + frame_cap(name="frame-cap") :: pg.pnode({ type: "DumpFrames", name: name, }, nin=1, nout=0), + // Sink a frame to a tar stream. Filename extension can be .tar, + // .tar.bz2 or .tar.gz. There's no monkey business with a %d in + // the file name. Pass in a unique, literal file name. Same goes + // for tags. + frame_sink(name, outfile, tags=[], digitize=false) :: + pg.pnode({ + type: "FrameFileSink", + name: name, + data: { + outname: outfile, + tags: tags, + digitize: digitize, + }, + }, nin=1, nout=0), + + frame_tap(name, sink, tag, cap=false) :: + if cap + then sink + else pg.fan.tap('FrameFanout', sink, name, + tag_rules=[ // one for each port! + {frame:{'.*':tag}}, + {frame:{'.*':tag}}, + ]), + // Save frames to outfile. // // name, outfile and elements of the tags list may have a single @@ -52,7 +76,7 @@ local ut = import 'utils.jsonnet'; frame_out(name, index, outfile, tags=["gauss%d"], digitize=false, cap=true) :: { local nam = if ut.haspct(name) then name%index else name, local tint = [if ut.haspct(t) then t%index else t for t in tags], - local end = if cap then [$.frame_sink(nam)] else [], + local end = if cap then [$.frame_cap(nam)] else [], local outf = if ut.haspct(outfile) then outfile%index else ut.basename_append(outfile, "-apa%d"%index), ret: pg.pipeline([$.frame_save_npz(nam, outf, digitize=digitize, tags=tint)] diff --git a/cfg/main-depos-sigproc.jsonnet b/cfg/main-depos-sigproc.jsonnet index 2fc5cc3..1241cac 100644 --- a/cfg/main-depos-sigproc.jsonnet +++ b/cfg/main-depos-sigproc.jsonnet @@ -24,15 +24,14 @@ // not given, only signal is simulated. // // If "taps" is given it specifies a mapping from a data tier key word -// to a file name. The key is one of the conventional tags: +// to a file pattern. The key is one of the conventional tags: // // - orig :: means the ADC-level frames out of the simulation // - gauss :: means the signal processing with Gaussian filter // -// The file name may have a "%" formatter which will be interpolated -// against the APA ID number. If omitted, "-apa%d" will be inserted -// at the end of the base file name just prior to the extention -// (likely .npz). +// The file pattern MUST have a %d format marker which will be +// interpolated on the APA ID. + local wc = import "wirecell.jsonnet"; local pg = import "pgraph.jsonnet"; @@ -72,10 +71,13 @@ function(input, taps, wires, resps_sim, resps_sigproc, noisef=null, thread='sing params.daq.nticks, params.daq.tick); - local tap_out(tap, apaid) = - if std.objectHas(taps, tap) - then [io.frame_out(tap+"%d", apaid, taps[tap], tags=[tap+"%d"], cap = tap=="gauss")] - else []; + local tap_out(tap, apaid, cap=false) = { + local name = "%s%d"%[tap,apaid], + local digi = tap == "raw" || tap == "orig", + res: if std.objectHas(taps, tap) + then [io.frame_tap(name, io.frame_sink(name, taps[tap]%apaid, tags=[name], digitize=digi), name, cap)] + else [] + }.res; local sim(n) = [ local anode = anodes[n]; @@ -95,7 +97,7 @@ function(input, taps, wires, resps_sim, resps_sigproc, noisef=null, thread='sing params.daq.nticks, params.daq.tick), ] + tap_out("raw", n) + [ sp(anodes[n], robjs_sigproc.fr, robjs_sigproc.er, spfilt, adcpermv) - ] + tap_out("gauss", n); + ] + tap_out("gauss", n, true); local oneapa(n) = pg.pipeline(sim(n) + nfsp(n)); diff --git a/cfg/main-depos-splat.jsonnet b/cfg/main-depos-splat.jsonnet index 5aca79f..9264ac3 100644 --- a/cfg/main-depos-splat.jsonnet +++ b/cfg/main-depos-splat.jsonnet @@ -22,10 +22,9 @@ // // - splat :: the output of DepoSplat. // -// The file name may have a "%" formatter which will be interpolated -// against the APA ID number. If omitted, "-apa%d" will be inserted -// at the end of the base file name just prior to the extention -// (likely .npz). +// The file name must have a "%d" formatter which will be interpolated +// against the APA ID number. Tap files likely one of .tar, .tar.gz +// or .tar.bz2. local wc = import "wirecell.jsonnet"; local pg = import "pgraph.jsonnet"; @@ -54,12 +53,15 @@ function(input, taps, wires, thread='single') local drifter = tz.drifter(params.det.volumes, params.lar, random); - local tap_out(tap, apaid) = - if std.objectHas(taps, tap) - then [io.frame_out(tap+"%d", apaid, taps[tap], tags=[tap+"%d"], cap = tap=="gauss")] - else []; + local tap_out(tap, apaid, cap=true) = { + local name = "%s%d"%[tap,apaid], + local digi = tap == "raw" || tap == "orig", + res: if std.objectHas(taps, tap) + then [io.frame_tap(name, io.frame_sink(name, taps[tap]%apaid, tags=[name], digitize=digi), name, cap)] + else [] + }.res; - local splat(n, ) = [ + local splat(n) = [ local anode = anodes[n]; tz.splat(anode, params.daq, params.lar, random) ] + tap_out("splat", n); From 535aeff83f1fa7e4a13ada8ace9c48902cf4ebab Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Thu, 19 Aug 2021 12:12:54 -0400 Subject: [PATCH 14/15] Make configurable saving orig frame --- Snakefile | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Snakefile b/Snakefile index 9d86c6a..24f2c1b 100644 --- a/Snakefile +++ b/Snakefile @@ -26,8 +26,16 @@ nevents = int(config.get("nevents", 10)) depotimes = config.get("depotimes", "-3*ms,6*ms") wcloglvl = config.get("wcloglvl", "info") -# limit number of threads per wire-cell job +# Which intermediate data tier(s) to save from the full simulation +# chain. It may include "orig". The "gauss" tier will always be included. +frame_tap_tiers = list(config.get("frame_tap_tiers", ('orig', ))) +if "gauss" not in frame_tap_tiers: + frame_tap_tiers += ["gauss"] + + +# limit number of threads given to a wire-cell job wct_threads = int(config.get("threads", 1)) + # single threaded uses Pgrapher, multi uses TbbFlow wct_threading = "single" if wct_threads == 1 else "multi" print(f'WCT THREADS {wct_threads} ({wct_threading})') @@ -222,7 +230,7 @@ frames_wildcard = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}- def frame_taps(w): frames_pattern = f'{datadir}/frames/{{tier}}/{{sim_domain}}-{{sigproc_domain}}-frames-apa%d.{frames_ext}' taps = list() - for tier in ('orig', 'gauss'): + for tier in frame_tap_tiers: d = dict(w) d["tier"] = tier taps.append('"%s":"%s"' % (tier, frames_pattern.format(**d))) From 95b87a515f247e1e3b75e957a03eb2476d533021 Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Thu, 19 Aug 2021 15:48:38 -0400 Subject: [PATCH 15/15] More cleanups and tweaks --- README.org | 45 ++++++++++++++++++++++ Snakefile | 105 +++++++++++++++++++++++++++++++++++++-------------- toyzero.yaml | 13 ++++--- 3 files changed, 130 insertions(+), 33 deletions(-) diff --git a/README.org b/README.org index 228ea67..87afbd5 100644 --- a/README.org +++ b/README.org @@ -573,3 +573,48 @@ made available. If the changes are generally useful, please consider making a PR! +* Production + +We run from containers. Eg, as a mongo ~docker~ command that runs +~snakemake~ in the ~toyzero~ container. + +#+begin_example + + $ docker run \ + --user user \ + --volume (pwd):/data \ + -ti ls4gan/toyzero:0.3.0 \ + "cd toyzero && \ + snakemake just_tar --notemp -j1 -p \ + --config seed=1234 outdir=/data ntracks=100 nevents=10 wcloglvl=debug threads=8" + +#+end_example + +Or, one layer down in the ~wirecell~ container, assuming you have +~toyzero~ checked out locally: + +#+begin_example + + $ cd .. # parent holding local toyzero/ + $ mkdir run + $ cd run/ + $ cp -a ../toyzero/{Snakefile,cfg,toyzero.yaml} . + $ docker run \ + --user user \ + --volume (pwd):/data \ + -ti ls4gan/wirecell:0.16.0 \ + "cd /data && \ + snakemake just_tar -j1 -p \ + --config seed=1234 ntracks=100 nevents=10 wcloglvl=debug threads=8" + $ ls -l toyzero-100-10-1234.tar + +#+end_example + +Note: will likely want to set ~threads=1~ for batch and *MUST* set +~seed=XYZ~ uniquely for each submission. The ~outdir~ setting may be used +if output should not go to the CWD. The tar file base name can be +controlled with ~outname~. Intermediate files that eventually go in to +the tar file land in ~{outdir}/seed-{seed}/~. The ~./.snakemake/~ +directory receives snakemake control and log files. If run with +~--notemp~ then (ironically, should be named ~--yestemp~) temporary files +marked with ~temp()~ in the Snakefile are *kept*. diff --git a/Snakefile b/Snakefile index 24f2c1b..790e99a 100644 --- a/Snakefile +++ b/Snakefile @@ -16,9 +16,6 @@ plotdir = config.get("plotdir", "plots") logdir = config.get("logdir", "logs") outdir = config.get("outdir", os.environ.get("TOYZERO_OUTDIR", ".")) -datadir = os.path.abspath(os.path.join(outdir, datadir)) -plotdir = os.path.abspath(os.path.join(outdir, plotdir)) -logdir = os.path.abspath(os.path.join(outdir, logdir)) seed = config.get("seed", "1,2,3,4") ntracks = int(config.get("ntracks", 10)) @@ -26,6 +23,15 @@ nevents = int(config.get("nevents", 10)) depotimes = config.get("depotimes", "-3*ms,6*ms") wcloglvl = config.get("wcloglvl", "info") +seedlst_ = str(seed).replace(",","-") +seeddir = f'seed-{seedlst_}' +outuniq = os.path.abspath(os.path.join(outdir, seeddir)) +datadir = os.path.abspath(os.path.join(outuniq, datadir)) +plotdir = os.path.abspath(os.path.join(outuniq, plotdir)) +logdir = os.path.abspath(os.path.join(outuniq, logdir)) + +outname = config.get("outname", f'{outdir}/toyzero-{ntracks}-{nevents}-{seed}') + # Which intermediate data tier(s) to save from the full simulation # chain. It may include "orig". The "gauss" tier will always be included. frame_tap_tiers = list(config.get("frame_tap_tiers", ('orig', ))) @@ -38,7 +44,7 @@ wct_threads = int(config.get("threads", 1)) # single threaded uses Pgrapher, multi uses TbbFlow wct_threading = "single" if wct_threads == 1 else "multi" -print(f'WCT THREADS {wct_threads} ({wct_threading})') +# print(f'WCT THREADS {wct_threads} ({wct_threading})') # The rest are hard wired for now wcdata_url = "https://github.com/WireCell/wire-cell-data/raw/master" @@ -112,10 +118,14 @@ rule plot_resp: wirecell-sigproc plot-response {input} {output} ''' -rule all_resp: +rule just_resp: input: rules.get_resp_real.output, rules.gen_resp_fake.output, + +rule all_resp: + input: + rules.just_resp.input, expand(rules.plot_resp.output, domain=["real","fake"]) # wires - get wires file @@ -140,9 +150,13 @@ rule plot_wires: wirecell-util plot-wires {input} {output} ''' -rule all_wires: +rule just_wires: input: rules.get_wires.output, + +rule all_wires: + input: + rules.just_wires.input, rules.plot_wires.output @@ -174,9 +188,14 @@ rule plot_depos: wirecell-gen plot-sim {input} {output} -p depo ''' +rule just_depos: + input: + expand(rules.gen_depos.output, wire=wires) + + rule all_depos: input: - expand(rules.gen_depos.output, wire=wires), + rules.just_depos.input, expand(rules.plot_depos.output, wire=wires) @@ -254,6 +273,8 @@ rule sim_frames: temp(sim_frame_files()) params: taps = frame_taps + log: + f'{logdir}/sim-frames-{{sim_domain}}-{{sigproc_domain}}.log' benchmark: f'{logdir}/sim-frames-{{sim_domain}}-{{sigproc_domain}}.tsv' shell: ''' @@ -261,7 +282,7 @@ rule sim_frames: wire-cell \ --threads {wct_threads} \ -A thread={wct_threading} \ - -l stdout -L {config[wcloglvl]} \ + -l {log} -L {config[wcloglvl]} \ -P cfg \ -A input={input.depos} \ --tla-code 'taps={params.taps}' \ @@ -283,12 +304,16 @@ rule splat_frames: temp([f'{datadir}/frames/splat/splat-frames-apa{apa}.{frames_ext}' for apa in apa_iota]) params: taps = f'{{"splat":"{datadir}/frames/splat/splat-frames-apa%d.{frames_ext}"}}' + log: + f'{logdir}/splat-frames.log' + benchmark: + f'{logdir}/splat-frames.tsv' shell: ''' rm -f {output}; wire-cell \ --threads {wct_threads} \ -A thread={wct_threading} \ - -l stdout -L {config[wcloglvl]} \ + -l {log} -L {config[wcloglvl]} \ -P cfg \ -A input={input.depos} \ --tla-code 'taps={params.taps}' \ @@ -337,9 +362,13 @@ rule all_splat_frames: rule just_frames: input: expand(rules.sim_frames.output, - sim_domain=["real","fake"], - sigproc_domain=["real","fake"], + sim_domain=["real"], + sigproc_domain=["fake"], ), + expand(rules.sim_frames.output, + sim_domain=["fake"], + sigproc_domain=["fake"], + ) rule all_frames: @@ -520,11 +549,18 @@ rule plot_split_splat_images: rule just_images: input: rules.just_frames.input, - rules.just_splat_frames.input, expand(rules.split_images.output, - sim_domain=["real","fake"], - sigproc_domain=["real","fake"], + sim_domain=["real"], + sigproc_domain=["fake"], tier=["gauss"], apa=apa_iota), + expand(rules.split_images.output, + sim_domain=["fake"], + sigproc_domain=["fake"], + tier=["gauss"], apa=apa_iota) + +rule just_splat_images: + input: + rules.just_splat_frames.input, expand(rules.split_splat_images.output, tier=["splat"], apa=apa_iota) @@ -536,32 +572,26 @@ rule all_images: rules.plot_split_images.output, sim_domain = ["fake"], sigproc_domain = ["fake"], - tier = ["orig"], + tier = ["gauss"], event = [0], apa=apa_iota, plane=["U","V","W"], ext = ["png"], - cmap = ["seismic"], + cmap = ["viridis"], zoomlevel=[1, 2], ), expand( rules.plot_split_images.output, sim_domain = ["real"], - sigproc_domain = ["real"], - tier = ["orig"], - event = [0], apa=apa_iota, plane=["U","V","W"], - ext = ["png"], - cmap = ["seismic"], - zoomlevel=[1, 2], - ), - expand( - rules.plot_split_images.output, - sim_domain = ["real","fake"], - sigproc_domain = ["real","fake"], + sigproc_domain = ["fake"], tier = ["gauss"], event = [0], apa=apa_iota, plane=["U","V","W"], ext = ["png"], cmap = ["viridis"], zoomlevel=[1, 2], - ), + ) + +rule all_splat: + input: + rules.just_splat_images.input, expand( rules.plot_split_splat_images.output, tier = ["splat"], @@ -572,6 +602,11 @@ rule all_images: ) +rule just: + input: + rules.just_frames.input, + rules.just_images.input + rule all: input: rules.all_resp.input, @@ -580,3 +615,17 @@ rule all: rules.all_frames.input, rules.all_images.input +rule just_tar: + input: + rules.just.input, + output: + f'{outname}.tar' + shell: ''' + tar --exclude data/depos \ + --exclude data/noise \ + --exclude data/wires \ + --exclude data/resps \ + --exclude data/frames/orig \ + -cf {output} {outuniq} + ''' + diff --git a/toyzero.yaml b/toyzero.yaml index 761206a..6da7d11 100644 --- a/toyzero.yaml +++ b/toyzero.yaml @@ -3,11 +3,14 @@ # This is the default toyzero config file. # Use like: # snakemake --configfile mycfg.yaml -jall all +# Or further, like +# snakemake --configfile mycfg.yaml -jall all --config outdir=other +# See top of snakefile for what can be set here. outdir: "." -datadir: "data" -plotdir: "plots" +# best to set something unique on the CLI!!! +# seed will be used as an intermediate under outdir seed: "1,2,3,4" ntracks: 10 -nevents: 10 -# how noisy wire-cell should be. "debug" is more, "error" is less -wcloglvl: info +nevents: 1 +# how noisy wire-cell should be. "debug" is more, "info" is less +wcloglvl: debug