Repository navigation
3D Matrix Analysis and Web Viewer
The cmat3d_webviewer.py server and companion cmat3d_webviewer.html client provide an ultra-responsive, browser-based spectroscopy environment for navigating 3D matrix cubes in the .cmat format (e.g.
The viewer couples pure Python/NumPy memory-mapped slice decompression with a high-performance HTML5 Canvas frontend featuring real-time multi-plane orthoslicing, 3D multi-gate coincidence cuts, 2D graphical Banana ROI gates, 1D/2D photopeak fitting, and publication-quality vector PDF export.
Live ROOT TH3 support: The 3D viewer can also display live TH3 histograms from ROOT
THttpServer/ c4Root (or saved JSON snapshots), with orthogonal-plane gating, double gates, polygon cuts, and time-spectra half-life fits. See ROOT Files & Live Histograms.
Development status: The large symmetric 3D
.cmatviewing workflow is preliminary and under active development. Sparse slicing, synchronized navigation, fitting workflows, and performance should be treated as experimental while validation and optimization continue.
Run the 3D viewer from your terminal, pointing to a 3D .cmat file:
# Open a 3D matrix cube
python cmat3d_webviewer.py /path/to/matrix3d.cmat
# Open multiple 3D matrices for rapid cycling
python cmat3d_webviewer.py cube1.cmat cube2.cmatThe server binds to http://0.0.0.0:8080 (or the configured host/port) and automatically opens your default web browser.
+-----------------------------------------------------------------------------------+
| cmat3d_webviewer.py |
| |
| +---------------------------+ +---------------------------------------------+ |
| | CMAT3DReader (cmat3d.py) | | ThreadingHTTPServer | |
| | - IVF Slice Decompressor | | - Multi-threaded concurrent request handler | |
| | - .cmat3d_cache/ (memmap) | | - /api/tile (Subregion Slicing: ~1.6 ms) | |
| | - 3D Coincidence Slicing | | - /api/projection_region (~2.1 ms) | |
| | - 2D Banana Polygon ROI | | - /api/gate_1d (Coincidence Cuts) | |
| +---------------------------+ +---------------------------------------------+ |
+------------------------------------------^----------------------------------------+
| HTTP / REST (JSON + Binary ArrayBuffers)
+------------------------------------------v----------------------------------------+
| cmat3d_webviewer.html |
| |
| +---------------------------+ +---------------------------------------------+ |
| | 2D Density Heatmap Canvas | | Triple 1D Spectroscopic Displays | |
| | - Turbo/Viridis LUT Engine| | - Det 1 (Axis 0) Stepped Histogram | |
| | - Multi-Plane: 0-1, 0-2, 1-2 | - Det 2 (Axis 1) Stepped Histogram | |
| | - 2D Banana ROI (a/b) | | - Rings / dt (Axis 2) Stepped Histogram | |
| | - AbortController Sync | | - 60 FPS Client-Side Instant Channel Zoom | |
| +---------------------------+ +---------------------------------------------+ |
+-----------------------------------------------------------------------------------+
3D .cmat files vary from moderately-sized asymmetric cubes to massive symmetric volumes:
-
Dense Memory-Mapped Volume Engine (
.dat): For moderately sized cubes (e.g.$4096 \times 4096 \times 128 = 2.14 \times 10^9$ channels,$\approx 8.5\text{ GB}$ uncompressed),CMAT3DReaderdecompresses slices on first access into a binary cache file (.cmat3d_cache/<filename>.dat) accessed as anumpy.memmap(int32), providing instant subsequent startup ($<50\text{ ms}$ ). -
Sparse On-Demand Indexing Engine (
.idx): For massive symmetric 3-fold cubes (e.g.$8192 \times 8192 \times 8192 = 5.5 \times 10^{11}$ channels, which would require$\approx 2.2\text{ TB}$ uncompressed), building a full dense file is impossible.python-cmatautomatically enables a sparse on-demand reader:- Generates a compact index file (
.cmat3d_cache/<filename>.idx) in$<0.5\text{ seconds}$ without uncompressing unread data. - Slices, orthogonal planes, and coincidence cuts are extracted on-the-fly directly from the compressed IVF segment streams in
$<10\text{ ms}$ .
- Generates a compact index file (
In folded 3D symmetric matrices (matmode == 1,
- Block coordinates are folded into the lower tetrahedron
$s_1 \le s_2 \le s_3$ . - In addition, sub-block channels along diagonal planes are folded internally:
- Plane
$s_1 = s_2 < s_3$ : internal channels folded into$dx \le dy$ . - Plane
$s_1 < s_2 = s_3$ : internal channels folded into$dy \le dz$ . - Main diagonal
$s_1 = s_2 = s_3$ : internal channels folded into$dx \le dy \le dz$ (1/6th sub-tetrahedron).
- Plane
-
CMAT3DReaderperforms vectorized sub-tetrahedral unfolding across all 6 permutations, eliminating dark triangular artifacts along diagonal boundaries and guaranteeing exact spatial symmetry ($|M - M^T| = 0$ ).
The viewer allows real-time orthogonal slicing across any pair of dimensions:
-
Plane 0-1 (
Det 1 vs Det 2): Standard$4096 \times 4096$ $\gamma$ -$\gamma$ coincidence matrix, sliced over the visible/gated 3rd axis. -
Plane 0-2 (
Det 1 vs Rings):$4096 \times 128$ matrix showing$\gamma$ -ray energy on Det 1 as a function of detector ring or timing. -
Plane 1-2 (
Det 2 vs Rings):$4096 \times 128$ matrix showing$\gamma$ -ray energy on Det 2 as a function of detector ring or timing.
Switch planes instantly using the header toolbar buttons (0-1, 0-2, 1-2) or dropdown menu.
-
Pre-Sum Slicing: When zooming into a 2D region
$[x_0..x_1, y_0..y_1]$ , the backend extracts only the required subvolume from the 3D memory map before summing over the 3rd axis, accelerating tile generation from$76.5\text{ ms}$ to$1.6\text{ ms}$ (~48× speedup). -
Multi-Threaded Server:
ThreadingHTTPServerprocesses simultaneous tile requests, 1D projections, and peak fits on separate background threads without blocking. -
Debounced 2D Synchronization: 1D wheel zooming and channel panning update the stepped histograms client-side at 60 FPS (
$<1\text{ ms}$ ), while heavy 2D tile rendering is debounced (45 ms) withAbortControllercancellation to prevent network congestion.
Set coincidence gates directly on any 1D spectrum:
- Hover cursor over a 1D spectrum panel (Det 1, Det 2, or Rings).
- Press
Wat the lower gate limit, move cursor to the upper limit, and pressWagain to confirm. - (Optional) Press
XorBtwice to define background subtraction windows. - The remaining spectra and 2D projections immediately update to display the background-subtracted coincidence cut:
- Press
Zor click Clear Gate to reset active coincidence gates.
For particle-gamma identification, ring discrimination, or kinematic curve gating:
-
Draw Peak Banana (W): Press
awith the cursor over the active 2D plane to start drawing and add the first Peak ROI vertex (rendered in gold#ffd600); each furtherapress adds a vertex at the cursor position. The polygon auto-closes (the last vertex connects back to the first) — pressEnterto apply the gate. Mouse click-drag zoom/pan keeps working while drawing. -
Draw Background Banana (B): Press
bwith the cursor over the 2D plane to add Background ROI vertices (rendered in magenta#ff4081) the same way; pressEnterto apply. -
Area-Normalized Subtraction: The viewer computes the continuous geometric surface area (in
$\text{ch}^2$ via the Shoelace algorithm) and discrete rasterized pixel count ($\text{px}$ ) for both polygons. It normalizes background subtraction by the ratio of their surface areas:$$\text{scale} = \frac{\text{Area}{\text{peak}}}{\text{Area}{\text{bg}}}$$ $$\text{Net Spec}(i) = \text{Spec}{\text{peak}}(i) - \text{scale} \times \text{Spec}{\text{bg}}(i)$$
-
Terminal Reporting: Applying a banana gate prints a detailed diagnostic report in the terminal listing surface areas (in pixels and
$\text{ch}^2$ ), count totals inside each banana, the scale factor, and net counts:[2D Banana Gate Cut] matrix.cmat -> Plane 0-1 => Axis 3 (Z): • Peak Banana (W): 400 px (area: 400.0 ch², 4 vertices) | Counts: 228,000 cts • Bg Banana (B): 200 px (area: 200.0 ch², 4 vertices) | Counts: 64,000 cts (Scale factor: 2.0000) • Net Area Counts: 100,000.0 counts -
Clear Gate: Click Clear Bananas or press
Zto remove active banana gates. -
Banana Save Files (
.ban): Banana gates are saved to the server working directory only on demand: click Save Banana in the sidebar or presswwith the cursor over the 2D plane. Files are namedp<E1>-<E2>.ban(peak) /b<E1>-<E2>.ban(background), whereE1/E2are the calibrated centroid coordinates of the polygon on the plane's two axes as integers (channels if uncalibrated). Each file lists onex yvertex pair per line in channel coordinates. To keep peak/background pairs associated, a background file reuses the coordinates of the most recently saved peak: the first background saved after a peak becomesb<E1>-<E2>.ban, and each additional one gets an incrementing index (b<E1>-<E2>_2.ban,_3.ban, …) until a new peak is saved. If no peak has been saved yet, the background file uses its own centroid coordinates. -
Result Logging: When fit results logging is enabled (
fit_log on [filename]), the gross peak area and the net background-subtracted area (± error) of every applied banana gate are appended to the results.txtfile, tagged with the active plane. -
Matrix Cycling: Banana gates are re-applied automatically when the active matrix is switched (
[/]) with unchanged dimensions: the diagnostic report is re-printed for the new matrix and the 1D gated spectrum is refreshed. If the new matrix has different dimensions, the bananas are cleared with a notification. -
Per-Plane Memory: Each plane (
0-1,0-2,1-2) remembers its own banana gates and in-progress drawings. Switching planes (Tabor the plane buttons) restores that plane's banana and re-applies it automatically;Zclears only the active plane's banana. -
Manual Save / Load: The sidebar Save Banana button saves the drawn polygon(s) as
p<E1>-<E2>.ban/b<E1>-<E2>.ban(same format as the automatic save) in the server working directory. The Load Banana button opens a server-side file browser (starting in the server working directory) to pick a.banfile from the server filesystem: the type is auto-recognized from the header (Peak/Background) and the polygon is placed on the active plane and applied immediately.
The 3D viewer incorporates the complete spectroscopy peak fitting engine:
-
Gaussian: Standard symmetric photopeak with linear/quadratic continuum baseline.
-
RadWare / SAMPO: Photopeak with an exponential low-energy tail (
$A, \eta$ ) for high-energy HPGe detectors. -
Hypermet EMG: Convolved Gaussian exponential tail plus complementary error function (
$\text{erfc}$ ) Compton step. -
Multiplet Fits: Place multiple candidate markers with
Por click fit region withR, then pressVto perform multi-component non-linear least-squares fitting. -
2D Peak Fit & Gamba 3D Slicing (
Ctrl+ClickorVin 2D):Ctrl+Clickon any 2D orthogonal plane fits the 2D coincidence peak and decomposes the local region into the Gamba 4-component model. In 3D matrices, this automatically computes and displays the net 3rd-axis coincidence spectrum:$$\text{Spec}{\text{net}}(z) = \text{Spec}{P|P}(z) - \frac{N_{P|BG}}{N_{BG|BG \text{ (1)}}} \text{Spec}{BG|P}(z) - \frac{N{BG|P}}{N_{BG|BG \text{ (2)}}} \text{Spec}{P|BG}(z) + \frac{N{BG|BG \text{ (corr)}}}{N_{BG|BG \text{ (all)}}} \text{Spec}_{BG|BG}(z)$$
This opens an interactive modal window displaying the net 3rd-axis projection (e.g. lifetime distribution or ring spectrum) with peak areas, background breakdown, 1D fitting tools, and vector PDF export.
The 3D viewer automatically reads and writes configuration files in the working directory:
[General]
port = 8080
colormap = turbo
scale_mode_2d = log
scroll_sensitivity = 0.04
active_plane = 0-1
proj_range_mode = synced
[Axes]
axis0_name = Det 1
axis1_name = Det 2
axis2_name = Rings
[Calibration]
axis0_cal = 0.0, 0.5, 0.0
axis1_cal = 0.0, 0.5, 0.0
axis2_cal = 0.0, 1.0, 0.0
[PeakFit]
fit_type = gaussian
fwhm_est = 4.0
fwhm_mult = 4.0You can also use cmat3d.py programmatically in Python scripts and Jupyter notebooks:
from cmat3d import CMAT3DReader
# 1. Open 3D .cmat file
reader = CMAT3DReader("GeE-Rings3D.cmat")
print(f"Matrix shape: {reader.shape}") # e.g. (4096, 4096, 128)
print(f"Total counts: {reader.total_counts:,}")
# 2. Access memory-mapped 3D volume
mmap_vol = reader.to_memmap() # Shape: (4096, 4096, 128), dtype: int32
# 3. Extract 2D orthogonal plane projection
# Plane options: '0-1' (Det 1 vs 2), '0-2' (Det 1 vs Rings), '1-2' (Det 2 vs Rings)
plane_01 = reader.get_2d_plane("0-1")
plane_gated = reader.get_2d_plane("0-1", gate_3rd=[[10, 12]]) # Gated on Ring 10-12
# 4. Extract 1D total projection
proj_det1 = reader.get_projection_1d(axis=0)
# 5. Coincidence Gating
net_spec, bg_spec = reader.get_gate_1d(
target_axis=0,
w_gates={2: [[11, 11]]}, # Gate on Ring 11
b_gates={2: [[1, 2], [20, 21]]} # Background rings
)
# 6. 2D Banana Gate Polygon (Peak & Background Subtraction)
poly_peak = [[1000, 10], [1500, 12], [1480, 15], [980, 13]] # [[x, y], ...]
poly_bg = [[900, 10], [950, 12], [940, 15], [890, 13]]
banana_res = reader.get_banana_gate(
plane="0-2",
polygon_peak=poly_peak,
polygon_bg=poly_bg
)
print(f"Peak: {banana_res['pixel_count_peak']} px, Bg: {banana_res['pixel_count_bg']} px (scale: {banana_res['scale']:.4f})")
print(f"Net Gated Counts: {banana_res['total_gated_counts']:,}")python-cmat © 2026. Released under the MIT License. Return to GitHub Repository
Interactive 2D & 3D Matrix Analysis & Spectroscopy
- Home
- Interactive Web Viewer
- 3D Matrix Analysis & Web Viewer
- Keyboard Shortcuts & Controls
- ROOT Files & Live Histograms
- ENSDF Isotope Identification
- Spectroscopy Commands & Macros
- Configuration Guide
- Python API & CLI Tools