-
Notifications
You must be signed in to change notification settings - Fork 1
fix: Zarr v3 push API improvements and 6D mode fixes #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- 6D mode: array is directly at acquisition.zarr, not at /0 subpath - Add store readiness check for 6D regions mode (retry if zarr.json doesn't exist yet during early acquisition) Ported from Squid repo testing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR makes follow‑up fixes to the zarr v3 push API, specifically for 6D regions mode, to align behavior with Squid integration testing and improve robustness during live acquisition.
Changes:
- Adjust 6D regions mode to open the TensorStore array at the zarr store root (
array_path="") instead of under the"0"subdirectory. - Add a readiness check for 6D regions mode that verifies the presence of
zarr.jsonfor the region store before building the dask array, mirroring the existing per‑FOV readiness logic.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # 6D mode: array is directly at acquisition.zarr, not at /0 | ||
| ts_arr = open_zarr_tensorstore(region_path, array_path="") | ||
| if ts_arr is None: | ||
| logger.debug( |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing 6D regions mode to use array_path="" means open_zarr_tensorstore will now open the TensorStore at the region root (e.g. .../acquisition.zarr), but simulate_zarr_acquisition.py currently creates the 6D arrays under a "0" subdirectory (see simulate_zarr_acquisition.py:314-332). As a result, the 6D simulation path (--structure 6d) will no longer line up with the viewer’s read path and the viewer won’t see the data written by the simulator. If the goal is to support Squid’s "array at root" layout, consider either (a) updating the simulator to create the 6D array at the store root instead of .../0, or (b) teaching the reader to detect whether the array lives at the root vs "0" and handle both layouts transparently.
| # 6D mode: array is directly at acquisition.zarr, not at /0 | |
| ts_arr = open_zarr_tensorstore(region_path, array_path="") | |
| if ts_arr is None: | |
| logger.debug( | |
| # 6D mode: prefer array at acquisition.zarr root, but fall back to /0 for | |
| # older/simulated layouts that store the array under a "0" subdirectory. | |
| ts_arr = open_zarr_tensorstore(region_path, array_path="") | |
| if ts_arr is None: | |
| logger.debug( | |
| f"Root-level zarr array not found for region {region_idx}, " | |
| f"trying '0' subdirectory layout" | |
| ) | |
| ts_arr = open_zarr_tensorstore(region_path, array_path="0") | |
| if ts_arr is None: | |
| logger.debug( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Claude Code] Fixed in commit 9a346b0 - updated simulator to create 6D array at zarr root instead of /0 subdirectory.
| # Check if store is ready for 6D regions mode | ||
| if self._zarr_6d_regions_mode and self._zarr_region_paths: | ||
| region_idx, _ = self._global_to_region_fov(fov_idx) | ||
| if region_idx < len(self._zarr_region_paths): | ||
| zarr_json = self._zarr_region_paths[region_idx] / "zarr.json" | ||
| if not zarr_json.exists(): | ||
| # Store not ready yet, retry later | ||
| if self._zarr_acquisition_active: | ||
| QTimer.singleShot(500, self._load_current_zarr_fov) | ||
| return |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description’s test plan suggests running simulate_zarr_acquisition.py --structure 6d_regions, but the simulator’s CLI only accepts --structure values "single", "6d", "per_fov", and "hcs" (simulate_zarr_acquisition.py:591-595). This discrepancy will make it hard to follow the documented test plan; either the CLI needs a 6d_regions option or the PR description/test instructions should be updated to refer to --structure 6d with the appropriate multi-region flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Claude Code] Fixed - updated PR description to use --structure 6d instead of --structure 6d_regions.
…ions The single-store mode used incorrect axis order (T, FOV, C, Z, Y, X) instead of the OME-NGFF standard (FOV, T, C, Z, Y, X). Rather than fix it, remove the mode entirely since it can be replaced by: - per_fov with --n-fov 1 for single FOV cases - 6d_regions for multi-FOV cases Changes: - Remove _zarr_acquisition_path and _zarr_acquisition_store variables - Make fov_paths required in start_zarr_acquisition() (was optional) - Remove single-store handling in _load_zarr_plane() - Update simulation script to remove "single" structure option - Default structure is now "per_fov" instead of "single" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Validates that fov_paths is not empty in start_zarr_acquisition(), matching the validation pattern in start_zarr_acquisition_6d(). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The viewer reads 6D arrays from the zarr root (array_path=""), so the simulator must also create arrays there to match. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # 6D mode: array is directly at acquisition.zarr, not at /0 | ||
| ts_arr = open_zarr_tensorstore(region_path, array_path="") |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching 6D regions mode to open the array at the store root (array_path="") fixes the mismatch with Squid-written datasets, but the simulator currently creates 6D zarrs with the array under a "0" subdirectory, and the static 6D loader _load_zarr_v3_6d() still opens array_path="0". This inconsistency means simulated 6D acquisitions are unlikely to display correctly in live mode, and real 6D datasets with root-level arrays will still fail when opened via the file loader. To avoid hard-to-debug behavior, align all 6D code paths on a single layout (either move simulator writes and _load_zarr_v3_6d to use root-level arrays, or keep using the "0" subpath everywhere and revert this call).
| # 6D mode: array is directly at acquisition.zarr, not at /0 | |
| ts_arr = open_zarr_tensorstore(region_path, array_path="") | |
| # 6D mode: array is stored under the '0' subdirectory | |
| ts_arr = open_zarr_tensorstore(region_path, array_path="0") |
| ap.add_argument( | ||
| "--structure", | ||
| choices=["single", "6d", "per_fov", "hcs"], | ||
| default="single", | ||
| help="Zarr structure type (default: single). " | ||
| choices=["6d", "per_fov", "hcs"], | ||
| default="per_fov", |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CLI no longer allows the "single" structure and the PR description says single-store mode is removed, but ZarrAcquisitionSimulator._setup_fov_structure() still has an else: # single branch that configures fov_paths for that mode. Since this branch is now unreachable and _create_zarr_stores() has dropped support for structure == "single", it effectively defines a broken, dead mode and can confuse future readers; consider deleting the leftover branch (and its comments) to keep the simulator aligned with the supported structures.
When the 6D array is created at the zarr root (not /0 subdirectory), tensorstore creates an array zarr.json. Previously, _write_zarr_metadata would overwrite this with a group zarr.json, destroying the array info. Now for 6D mode, we merge the OME-NGFF attributes into the existing array zarr.json instead of overwriting it. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The OME-NGFF metadata `datasets[0].path` should be: - "." when array is at root (6D mode) - "0" when array is at /0 subdirectory (per_fov/hcs mode) Previously it was always "0", which was incorrect for 6D mode. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…rocesses During live acquisition, the writer process and viewer run in separate processes. Without these options, TensorStore may return cached data instead of seeing fresh writes from the writer process. Adding recheck_cached_metadata and recheck_cached_data ensures TensorStore validates data freshness on each read, which is much cheaper than reopening stores for every read. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
recheck_cached_data defaults to True in TensorStore, so explicitly setting it has no effect. Only recheck_cached_metadata needs to be set since its default is "open" (recheck only at open time). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update _load_zarr_plane() docstring to document both 6D regions and
per-FOV modes, and clarify that zeros are returned when no zarr mode
is configured
- Add unit tests for _write_zarr_metadata() merge_into_existing parameter:
- test_merge_into_existing_preserves_array_structure: verifies 6D mode
preserves array zarr.json and uses path "."
- test_non_merge_creates_group_structure: verifies per_fov mode creates
group zarr.json and uses path "0"
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove reference to _zarr_acquisition_path which was removed from the implementation. Add test case for fov_paths mode detection. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add error type to warning log messages (e.g., "OSError: ...") to help distinguish between different failure modes - Add TestZarrApiValidation class with tests for: - Empty fov_paths validation raises ValueError - Mismatched fov_paths/fov_labels truncation behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
Follow-up fixes for #25 discovered during Squid integration testing.
Bug Fixes
array_path=""instead of"0"for 6D mode (array is at root, not/0subpath)"."for 6D mode (array at root) vs"0"for per_fov moderecheck_cached_metadata: Trueto see fresh writes from writer process during live acquisitionAPI Changes
start_zarr_acquisition()now requiresfov_pathsas first parameter (breaking change - Squid updated)fov_pathsraisesValueErrorRefactoring
per_fovwith--n-fov 1for single FOV cases6dfor multi-FOV 6D casessingletoper_fovImprovements
{type(e).__name__}: {e}_load_zarr_plane()documenting both modesTest plan
python simulate_zarr_acquisition.py --structure per_fov --n-fov 4python simulate_zarr_acquisition.py --structure 6d --n-regions 2 --fovs-per-region 3 3python simulate_zarr_acquisition.py --structure 6d --n-regions 3 --fovs-per-region 2 4 3python simulate_zarr_acquisition.py --structure hcs --wells A1 A2 B1 B2 --fov-per-well 2🤖 Generated with Claude Code