Skip to content

Recent developments for the pixelated Anode#1

Open
fanrado wants to merge 68 commits into
WireCell:masterfrom
fanrado:wogrid
Open

Recent developments for the pixelated Anode#1
fanrado wants to merge 68 commits into
WireCell:masterfrom
fanrado:wogrid

Conversation

@fanrado
Copy link
Copy Markdown

@fanrado fanrado commented May 3, 2026

Summary

This PR consolidates recent work on the pixelated-anode field-response pipeline
(test/test-full-3d-pixel.sh). Hard-coded geometry parameters and debug scaffolding have been
replaced by configuration-driven, reusable code, and a dedicated README documents the full
workflow.

What changed

Geometry & generators

  • Pixel grid generalized to arbitrary Npixels (validated at 9×9).
  • Rounded-corner radius is now driven by a chamfer_r field in the JSON config; trimCorner
    carves a true quarter-disk of that radius instead of a hard-coded L-stencil. Applies to both
    gen_pcb_drift_pixel_with_grid.py and gen_pcb_pixel_with_grid.py.
  • trimCorner and trimCorner_pcb unified into a single function with a val argument;
    documented.
  • Pixel-grid path-shifting logic extracted from induce_pixel into a standalone,
    neighbourhood-size-aware helper.
  • Starting points computed from z_depth, npoints, and pitch instead of hard-coded values.

Pipeline & tooling

  • POCHOIR_DRIFT_SHAPE / POCHOIR_WEIGHT_SHAPE hoisted to the top of test-full-3d-pixel.sh, with
    env-var overrides preserved.
  • Updated example JSON configs (example_gen_pcb_drift_pixel_with_grid.json,
    example_gen_pixel_with_grid.json) to reflect the new geometry fields.

Docs

  • New README_for_pixelatedReadout.md describing requirements, install, the JSON configs, and
    how to run test-full-3d-pixel.sh.

How to try it

cd test
POCHOIR_DRIFT_SHAPE=44,44,1500 \
POCHOIR_WEIGHT_SHAPE=396,396,1500 \
bash test-full-3d-pixel.sh store

See README_for_pixelatedReadout.md for full details.

rennney and others added 30 commits October 24, 2025 13:06
enabling mode=="yes" in starts, allowing to not hard-coding the argument points
…ift_domain where drift_domain is the domain for the drift field calculation.

Included new lines to calculated the positions of the starting points
… These variables are needed to save checkpoints
fanrado and others added 30 commits April 14, 2026 13:55
…strings

### pochoir/gen_pcb_drift_pixel_with_grid.py

**Unify trimCorner and trimCorner_pcb into a single function**
- The two corner-stencil functions were identical except for the fill value
  (0 for pixel-plane holes, 1 for PCB-shield corners). They are now merged
  into a single trimCorner(arr, x, y, z1, z2, corner, val=0) that accepts a
  'val' keyword argument. Default val=0 preserves the original pixel-plane
  behaviour; callers that previously used trimCorner_pcb now pass val=1.
- The redundant trimCorner_pcb function has been removed.
- Each corner branch uses elif instead of if to make the dispatch mutually
  exclusive and avoid redundant comparisons. The self-assignment
  arr[x, y, z1:z2] = val (centre cell) that was present in the old code is
  also removed since the arm assignments already cover that cell.

**Add _apply_rounded_corners helper**
- New private function _apply_rounded_corners(barr, p_size, p_gap, z1, z2, val)
  encodes the four inner-corner positions and their quadrant indices once and
  then calls trimCorner for each. Both draw_pixel_plane and
  draw_pcb_plane_rounded_sq_drift delegate to this helper so that the
  coordinate arithmetic (half = p_size // 2, corner offsets by p_gap) lives
  in exactly one place.

**Refactor draw_pixel_plane (replace repeated trimCorner calls)**
- The four explicit trimCorner(...) calls (one per corner, each written out
  separately with explicit coordinate arithmetic) are replaced by a single
  _apply_rounded_corners call with val=0.
- The four barr slice assignments that fill the corner quadrants are rewritten
  using the local variable half = p_size // 2 and z1, z2 range variables,
  making the intent explicit and consistent with draw_pcb_plane_rounded_sq_drift.

**Refactor draw_pcb_plane_rounded_sq_drift**
- The four explicit trimCorner_pcb calls are replaced by a single
  _apply_rounded_corners call with val=1 (fill corners back into the solid
  after cutting square holes).
- Local variable z = pp_loweredge + pcb_width is introduced to avoid
  repeating the expression, and z1, z2 = z, z+1 gives the z-slice range for
  _apply_rounded_corners.
- barr and arr slice assignments use half = p_size // 2 and the z variable
  for clarity.

**Add gridHoleShape parameter to generator**
- generator() now accepts an optional gridHoleShape argument ('circular' or
  'rectangular', default 'rectangular'). When 'circular' is selected the
  original draw_pcb_plane (quarter-circle holes) is called; when 'rectangular'
  draw_pcb_plane_rounded_sq_drift (rounded-square holes) is called. A TODO
  comment notes that this argument should eventually be read from the config
  file rather than passed as a function argument.

**Add docstrings to trimCorner, draw_pcb_plane_rounded_sq_drift, and draw_pixel_plane**
- trimCorner: documents all parameters, the corner-numbering convention, the
  modification history (fusion of the two old functions), and shows usage
  examples for both the carve (val=0) and fill (val=1) cases.
- draw_pcb_plane_rounded_sq_drift: documents all parameters, explains the
  solid-then-cut approach (complement of draw_pixel_plane's void-then-fill
  approach), and describes the removal of trimCorner_pcb.
- draw_pixel_plane: documents all parameters and explains the fill-then-carve
  approach as the counterpart of draw_pcb_plane_rounded_sq_drift.
Examines fanrado/pochoir wogrid branch: algorithm (two-step mixed-precision
Poisson solver), potential bugs (12 items incl. err=None crash, numpy.bool
removal, hard-coded 3-D assumption), GPU efficiency (torch.compile disabled,
kernel launch count), and memory footprint vs master branch.

Docs written to docs/examinations/wogrid/. No source code modified.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add GPU field-response code examination for wogrid branch
…urhood size

Replace the hardcoded 3×3 asymmetric loop (driven by fixed row_counts/col_levels
lists and a secondary sp2 sub-sampling level) with a symmetric double loop over
range(-half_n, half_n+1) in both X and Y. The new `half_n` parameter controls
the half-width of the neighbourhood: half_n=1 → 3×3, half_n=4 → 9×9, etc.

For each pixel offset (ix, iy) all sp×sp prototype paths are translated by
(ix*pitch, iy*pitch) relative to the central collection pixel, giving
(2*half_n+1)² × n_paths output paths in total.

Wire half_n through to the induce_pixel call site so that the CLI --npixels
argument is actually respected instead of being silently ignored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- `_shift_paths_pixel_grid`: refactored signature (npaths, npixels) so
  the grid size is no longer hard-coded to 5×5; added docstring.
  `induce_pixel` updated to call it with npixels=9.
- `induce_pixel`: save shifted and original paths to store/ for
  debugging; remove stale commented-out print statements.
- `starts`: switch active start-point depth from 148 mm to 305 mm.
- `fdm_torch.solve`: re-enable `@torch.compile` on `_compiled_step`;
  comment out Poisson source-term block (barr_pad, iarr_pad_source,
  nabla_phi computation) that is not yet ready.
- Test configs (example_gen_*_pixel_with_grid.json): Npixels 5→9,
  GridHoleShape square→None, CathodePotential -7000→-15000.
- test-full-3d-pixel.sh: expand drift/weight domains to match 9×9
  pixel geometry (3100 z-steps, 396×396 weight domain).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Save current state of pochoir modifications, pixel-with-grid generators,
new test scripts/notebooks, and toy explorations so this point can be
returned to if later changes regress behavior.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Adds test/{OUTPUT,bkup_store,plots,toy,review}/, test/log.log,
test/pochoir_stencil_vs_stencil_poisson_torch.log, toy/*.npy and env/
to .gitignore so per-machine pipeline outputs (≈14 GB) don't pollute
the working tree.
numpy.gradient no longer accepts a single 1-D array as 'one scalar per
axis' — passing a length-N sequence is now interpreted as coordinates
for axis 0. Callers (test_dipole, test_velo) pass dom.spacing as a
single arg, so unpack a length-N sequence into N positional scalars
before forwarding. Also forward *spacing in the torch branch (was
positional 'spacing' bug). Drops a stray debug print.
Figure.gca() no longer accepts kwargs in matplotlib 3.5+; the
documented replacement for gca(projection='3d') is
add_subplot(projection='3d').
…il()

- Remove commented time.time() / info_msg() probes from edge_condition,
  stencil and stencil_poisson.
- Delete the leftover first definition of stencil() (the file had two
  back-to-back defs; only the second was reachable). Behaviour is
  unchanged.
- Tighten stencil_poisson docstring to clarify that 'source' is the
  reduced (interior) shape, matching how fdm_torch.solve constructs it,
  and document the ∇²φ = -f sign convention inline rather than in a
  trailing comment.

Adds test_stencil_poisson covering the kept implementation.
Drops commented debug print/sys.exit blocks left over from earlier
development around draw_pixel_plane (z-slice dumps, pp_loweredge/barr
shape probes, kdlsjfew probe). Also removes the 'TO DO: include
Chamfer radius' comment on trimCorner — _apply_rounded_corners already
covers the rounded-corner case used by the pixel pipeline.
- Replace top-level debug print and 'Saved 3D barr plot' print with a
  module logger (log.debug / log.info) so default-level pipeline runs
  stay quiet.
- Remove the large commented IDW initialisation block and its
  InverseDistanceWeight_torch import. The referenced module was never
  committed, so re-enabling was impossible; the block was dead code.
- Drop the now-unused 'import torch'.

Adds test_grid_init_method covering the chosen (zeros-based) init.
- induce-pixel: remove the unconditional sys.exit() that aborted the
  command after printing dom.linspaces. The CLI now runs to completion
  and writes the induced-current output expected by the pipeline.
- induce-pixel: load pixel geometry (pixelSize, pixelGap, Npixels) from
  one or more --config JSON files via _load_pixel_geometry() instead of
  the hardcoded npixels=9, pitch=4.4, gap=0.6, size=3.8 call. Missing
  --config or missing keys raise a clear error. test-full-3d-pixel.sh
  now passes --config example_gen_pixel_with_grid.json.
- velo / starts / drift: replace ad-hoc print() debug output with
  debug_msg() so default-level runs stay quiet.
- starts / drift / induce-pixel: gate the inline matplotlib plotting
  (starting_points.png, drift_paths_3d.png, charge.png) behind a new
  --plot/--no-plot flag, defaulting off, and ensure store/ exists
  before writing.
- Drop the stale 'FIXME: most of this verbiage belongs in the manual'
  header and the 'we say we don't allow numpy in main' aside.

Tests added:
- test_induce_pixel_no_sys_exit
- test_no_stray_prints
- test_pixel_geometry_loaded_from_config
- test_plot_flag
- Delete test/for_pixel/debug_py.ipynb (debug-only, not part of the
  validation workflow).
- Clear cell outputs on the kept validation notebooks
  (compare_with_without_grid, plot_weighting_potential, validate_FR,
  velocity_vs_err) so the repo stays small and diffs review cleanly.
- Add test_notebooks_have_no_outputs to guard the kept set.
Walks the modules exercised by test-full-3d-pixel.sh and fails if any
TO DO / FIXME marker reappears in pipeline code paths, so the §1 TODO
sweep stays swept.
- test_make_pixel_start_points: 1x1, 5x5, 9x9. Verifies count, fixed
  z-depth, cell-centred offset (spacing/2), uniform spacing, no
  duplicates, and that an explicit spacing overrides pitch/ngridpoints.
- test_shift_paths_pixel_grid: parameterised output count for
  npixels in {3,5,9}; z coordinates pass through unchanged; with
  origin-only input the first replicated path lands at the expected
  central-pixel centre (npix*pitch + gap/2 + size/2); successive
  iy_pix groups differ by exactly pixel_pitch.
- Makefile target runs pytest and test-full-3d-pixel.sh, redirecting
  stdout/stderr into a freshly-created test/review/<date>_<slug>/ folder
  with pytest.log, pipeline.log, outputs/, and a NOTES.md stub matching
  the plan template.
- POCHOIR_DRIFT_SHAPE / POCHOIR_WEIGHT_SHAPE default to the iteration
  sizes (44,44,300 / 220,220,300). Override on the command line for
  release validation:

    make test-pixel SLUG=release-validation \
         POCHOIR_DRIFT_SHAPE=44,44,500 POCHOIR_WEIGHT_SHAPE=220,220,500

- test-full-3d-pixel.sh now reads those env vars (defaults preserved at
  the production sizes 44,44,500 / 220,220,500), so direct invocation
  is unchanged but the Makefile can supply iteration sizes.
Replace the hardcoded L-shaped 4-cell stencil with a quarter-disk carve
of radius chamfer_r (in grid-index units). The chamfer box is r x r,
anchored at the inner corner, and cells outside a quarter-circle
centered at the inward corner are written with val. chamfer_r <= 0 is a
no-op (sharp 90 corner).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirror the drift-side change in gen_pcb_pixel_with_grid: replace the
hardcoded L-shaped stencil with a quarter-disk carve of radius
chamfer_r, and thread chamfer_r through every trimCorner call in
draw_pixel_plane (previously they used the hardcoded default).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Switch the drift-field example geometry from a 5x5 array of 3.8 mm /
0.6 mm pixels to a 9x9 array of 3.5 mm / 0.9 mm pixels, drop the
cathode potential to -7000 V, and introduce the new chamfer_r field
(0.7 mm) consumed by trimCorner.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Match the drift config: 9x9 pixel array with 3.5 mm pitch and 0.9 mm
gap, plus chamfer_r=0.7 mm so the weighting-potential geometry uses
the same rounded-corner radius as the drift geometry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Define both domain shapes once near the top of test-full-3d-pixel.sh
(with env-var override preserved via :- defaults) instead of inlining
the defaults at each do_domain call site. Makes the shapes easy to
edit in one place while keeping the Makefile env-var overrides
working.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Refresh cached cell outputs and minor edits in
plot_weighting_potential.ipynb to reflect the updated 9x9 pixel
weighting-potential geometry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Refresh cached cell outputs and minor edits in validate_FR.ipynb to
reflect the updated 9x9 pixel field-response geometry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Companion to README.org focused on the pixelated-readout pipeline
driven by test/test-full-3d-pixel.sh: describes the 7-stage chain
(drift domain/fdm, weighting domain/fdm, velocity, starts/paths,
induced currents), system and Python requirements, the two driving
JSON configs and their geometry fields, direct invocation with
POCHOIR_DRIFT_SHAPE / POCHOIR_WEIGHT_SHAPE overrides, expected store
outputs, and how chamfer_r controls the rounded-corner radius via
trimCorner.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Fix half-pixel boundary calculation: use (p_size+1)//2 instead of
  p_size//2 to restore symmetry in quarter-pixel corners/rounded edges
- Reduce z_depth from 148 to 28 for start points in debug mode
- Lower pixelPlaneLowEdgePosition from 10 to 2 in both test configs
- Reduce CathodePotential from -7000 to -1400 in pcb_drift config
- Switch FDM boundary to fix,fix,fix; add --plot to starts command

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _load_start_point_config helper that reads driftZDepth, nGridPoints,
and optional gridSpacing from JSON config files, deriving pitch from the
existing pixelSize+pixelGap keys. Wire it into the starts CLI command via
a new -c/--config option (same pattern as induce-pixel), replacing the
hardcoded parameter values. Update pixel JSON configs with the new keys
and add 7 pytest tests covering all loading scenarios.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extend driftZDepth from 28.0 to 148.0 in both example JSON configs
  (example_gen_pixel_with_grid.json and example_gen_pcb_drift_pixel_with_grid.json)
- Update CathodePotential from -1400 to -7500 in PCB drift pixel config
  to match the longer drift distance
- Scale domain Z dimension from 300 to 1500 in test-full-3d-pixel.sh
  for both drift (44,44,1500) and weighting field (396,396,1500) shapes
- Mark issue pochoir-xks closed: 7 tests written and passing in
  test/test_start_point_config.py covering all JSON-driven start-point
  config scenarios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants