@@ -512,6 +551,9 @@ These keys live under `files:` and are read by `config_parser.cpp` exactly as li
| `deconvolution.threshold` | float | `0.001` | `0.0005`| Stop when peak residual drops below this value [Jy/beam]. |
| `deconvolution.gain` | float | `0.1` | `0.1` | Loop gain: fraction of peak flux removed per iteration (0.05–0.2). |
| `deconvolution.positive_only` | bool | `false` | `false` | Restrict cleaning to positive components (opt-in; keep false for negative sidelobes and Stokes Q/U/V). |
| `deconvolution.restoring_beam.bmaj`| float/`auto` | `auto` | `8.5` | Restoring beam major-axis FWHM [arcsec]; `auto` (or absent) = fit an elliptical Gaussian to the PSF main lobe. |
| `deconvolution.restoring_beam.bmin`| float/`auto` | `auto` | `7.0` | Restoring beam minor-axis FWHM [arcsec]; `auto` = fit from PSF. Override requires BOTH bmaj and bmin numeric. |
| `deconvolution.restoring_beam.bpa` | float/`auto` | `auto` | `25.0`| Restoring beam position angle [deg, east of north]; only consulted with a numeric bmaj/bmin override. |
> **PSF is generated automatically.** The dirty beam is computed internally
> from the visibility data (second gridding pass with unit amplitudes and the
@@ -625,21 +667,39 @@ The loop terminates when $A_k < \tau$ (threshold) or $k = N_{\text{max}}$ (maxim
@@ -9,7 +9,6 @@ RICK is a distributed radio interferometry imaging code (C++/MPI+OpenMP) doing W
**Decomposition model:**
- 1D mode: full X (x_start=0, xaxis=grid_size_x), Y strip per rank (Gaussian-weighted strip sizes possible).
- 2D mode: sub-rectangle of both axes; requires ≥4 ranks; "2d" or "2d_gaussian" modes via config.
- "relaxed" mode (added 2026): data-driven point-based UV decomposition. Builds per-axis histograms over the *normalised* [0,1] coordinate domain (NOT the data-extent envelope — this would silently rescale the data range to span the full pixel grid and put slab boundaries in the wrong pixels), then iteratively refines slab boundaries via pair-optimal V-shape binary search. Histogram-cell slab boundaries are mapped onto pixel boundaries via `pixel = floor(slab/Ngrid * grid_size)`. Output `(start_x, start_y, size_x, size_y, rank_2d_x, rank_2d_y, npx, npy)` matches the 2D contract so the rest of the pipeline (bucket-sort, gridding, wstack, FFT, phase, MPI-IO subarray) is unchanged. Requires ≥4 ranks. Conceptually deferred: relaxed_decomposition runs AFTER reading uu/vv local coordinates because it's data-dependent; the 1D/2D paths run up-front.
- Visibilities distributed via ghost-region bucket sort: any vis whose kernel footprint (±KernelLen cells) overlaps a rank's domain is sent to that rank. Each rank then clips kernel to its local domain in `wstack.cpp`.
**Why:** Allows each rank to grid disjoint pieces of overlapping kernel footprints — no double-counting and no halo exchange needed. The grid is *output*-decomposed, not input-decomposed.
@@ -22,13 +21,9 @@ RICK is a distributed radio interferometry imaging code (C++/MPI+OpenMP) doing W
- Halo-exchange utilities (`exchange_halos_x/y/2d` in `src/communication/communication.cpp`) have stride bugs (write at `grid + y*xaxis + size_x` which assumes a halo-extended row that isn't allocated). Treat these as broken — use the ghost-region clipping design instead.
- MPI-IO write in `phase_correction` uses `starts[2] = {y_start, 0}` — correct for 1D, broken for 2D (should be `{y_start, x_start}`).
- FITSIO write uses `rank * yaxis` — only valid when all ranks have equal yaxis (not true under Gaussian-weighted decomposition).
-**`io_write` (src/io/io_functions.cpp) is NOT slab-aware:** it hardcodes `MPI_File_write_at(File, 0, data, Ndata, MPI_BYTE)` — every rank writes at byte offset 0, no `MPI_File_set_view`, no global offset/subarray params. Only correct for whole-buffer single-region writes. When N ranks call it on one filename each writes its local slab at offset 0 (race) and the file ends up one-slab-sized. The deconvolution output block in test_clib.cpp (component/residual/restored _real/_imag) used `io_write` and therefore produced only one rank's 2048×2048 slab instead of the full 8192×8192 image. CORRECT pattern for distributed image writes lives in phase_correction_library.cpp (~lines 533-549) and fft_library.cpp/gridding_data.cpp: `MPI_Type_create_subarray(2, {grid_size_y,grid_size_x}, {yaxis,xaxis}, {y_start,x_start}, MPI_ORDER_C, MPI_DOUBLE)` + `MPI_File_set_view(fh,0,...)` + `MPI_File_write_all(fh, buf, xaxis*yaxis, MPI_DOUBLE)`. CleanSolver itself is dimensionally correct (sized xaxis*yaxis, converts local↔global peaks via x_start_/y_start_) — only the write was broken.
- Bucket-sort `v_norm = vvt[i] / grid_size_y` while `vvt` is already in `[0,1]` (normalized by `create_binMS.py`) — dimensional mismatch with `min_vals[r]` that's also in `[0,1]`. Pre-existing in `test_clib.cpp`.
- Kernel index `kKer = increaseprecision * fabs(v_dist + KernelLen)` relies on kernel symmetry; correct for Gauss/KaiserBessel but would be wrong for asymmetric kernels.
-**Layout-mismatch class** (e.g., the 2026 `weight_grid` work): a buffer is allocated as a flat real array `[iw*xaxis*yaxis + i]` at the call site, but written with the interleaved complex stride `2*(j+k*xaxis+grid_w*xaxis*yaxis)` inside `wstack.cpp`. Anything sized like the visibility grid must use either the interleaved index everywhere or the flat index everywhere — never mix.
-**Histogram-domain mismatch class** (relaxed decomposition): if a histogram is built over a data-driven `[u_min,u_max]` window and then slab-to-pixel mapped via `floor(slab/Ngrid * grid_size)`, the mapping silently treats the data range as if it spans the entire pixel grid. The histogram domain MUST match the mapping domain ([0,1] in RICK's normalised UV space) or you must apply an affine transform `pix = floor((hmin + slab/Ngrid * hrange) * grid_size)`.
-**Unsigned-int passing across function boundaries:** large per-rank measurement counts (`Nmeasures`) routinely exceed 2^31 on fat nodes. Any decomposition or gridding helper that takes its trip count as `int` will silently truncate. Use `unsigned long long` (or `myull`) for all visibility counts crossing function boundaries.
-**`enforce_min1` / boundary-tightening lambdas:** when iterating boundary arrays after rounded mapping, naive two-pass forward/backward walks can leave the first slab with size 0 if the backward walk pushes b[1] above 0 and then the front is restored to 0. Always use a two-sided clamp `[lo_i, hi_i]` where `lo_i = b[i-1]+1` and `hi_i = axis_total - (N-i)` so each slab keeps ≥1 pixel after the pass.
- New `weight_grid` plumbed through `gridding → wstack → fftw_data → phase_correction(weight_grid_fft)` to divide image by per-pixel Σweights (summed over w-planes).