|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Identity |
| 6 | + |
| 7 | +MeasureIt is a QCoDeS-based measurement software for condensed matter physics experiments. It is published on PyPI as `qmeasure` but imported as `measureit`. The repository name is `MeasureIt`. |
| 8 | + |
| 9 | +## Build & Development Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Install (editable with all dev deps) |
| 13 | +pip install -e ".[dev,docs,jupyter]" |
| 14 | + |
| 15 | +# Run all tests with coverage |
| 16 | +pytest |
| 17 | + |
| 18 | +# Run specific test categories |
| 19 | +pytest tests/unit -v |
| 20 | +pytest tests/integration -v |
| 21 | +pytest tests/e2e -v |
| 22 | +pytest -m "not slow" |
| 23 | + |
| 24 | +# Run a single test file |
| 25 | +pytest tests/unit/test_sweep1d.py -v |
| 26 | + |
| 27 | +# Lint and format |
| 28 | +make format # ruff format + ruff check --fix |
| 29 | +make lint # ruff format --check + ruff check + mypy |
| 30 | + |
| 31 | +# Build docs |
| 32 | +make docs # outputs to docs/source/_build/html/ |
| 33 | +``` |
| 34 | + |
| 35 | +Tests use `QT_QPA_PLATFORM=offscreen` (set in `tests/conftest.py`). Qt/pytest-qt integration tests are skipped in CI but should be run locally. Each test gets an isolated `MEASUREIT_HOME` via the `temp_measureit_home` fixture. |
| 36 | + |
| 37 | +## Architecture |
| 38 | + |
| 39 | +### Sweep Hierarchy |
| 40 | + |
| 41 | +`BaseSweep` (in `src/measureit/sweep/base_sweep.py`) is the central class. It owns: |
| 42 | +- A `RunnerThread` (QThread in `_internal/runner_thread.py`) for data acquisition |
| 43 | +- A `Plotter` (QObject in `_internal/plotter_thread.py`) for real-time visualization |
| 44 | +- `ProgressState` / `SweepState` for status tracking |
| 45 | +- Parameter following, QCoDeS `Measurement` creation, and save/plot lifecycle |
| 46 | + |
| 47 | +Concrete sweep types: `Sweep0D` (time-based), `Sweep1D` (single param), `Sweep2D` (dual param, composes an inner `Sweep1D`), `SimulSweep`, `SweepIPS`, `GateLeakage`, `Sweep1D_listening`. |
| 48 | + |
| 49 | +### Concurrency Guard |
| 50 | + |
| 51 | +A module-level `WeakSet` (`_ACTIVE_SWEEPS`) enforces that only one non-queued sweep runs at a time. Inner sweeps and sweeps sharing a parent chain are considered "related" and allowed. `SweepQueue` bypasses this guard. `start_force()` kills unrelated active sweeps before starting. |
| 52 | + |
| 53 | +### Threading Model |
| 54 | + |
| 55 | +All threading uses PyQt5 `QThread` + signals/slots, **not** Python `threading.Thread`. This is required even in headless/Jupyter usage because the sweep loop (`RunnerThread.run()`) communicates data points back via `pyqtSignal`. The `conftest.py` provides a session-scoped `qapp` fixture and per-test `cleanup_qt_threads` to prevent segfaults. |
| 56 | + |
| 57 | +### Package Layout |
| 58 | + |
| 59 | +``` |
| 60 | +src/measureit/ |
| 61 | + sweep/ # Sweep classes (base_sweep, sweep0d/1d/2d, simul_sweep, etc.) |
| 62 | + _internal/ # RunnerThread, Plotter (not public API) |
| 63 | + tools/ # sweep_queue, util (init_database, safe_get/set), safe_ramp, tracking |
| 64 | + visualization/ # heatmap_thread, helper (pyqtgraph-based) |
| 65 | + legacy/ # Old matplotlib-based plotter/heatmap threads |
| 66 | + Drivers/ # QCoDeS instrument drivers for lab hardware |
| 67 | + config.py # Data directory resolution (MEASUREIT_HOME / platformdirs) |
| 68 | + logging_utils.py # Sweep file logging + Jupyter notebook log handler |
| 69 | + _deprecation.py # FutureWarning shims for old import paths |
| 70 | +``` |
| 71 | + |
| 72 | +Top-level shim modules (`base_sweep.py`, `sweep0d.py`, etc. at `src/measureit/`) re-export from `sweep/` with deprecation warnings. New code should import from `measureit.sweep.*` or the top-level `measureit` namespace. |
| 73 | + |
| 74 | +### Data Directory |
| 75 | + |
| 76 | +Priority: `set_data_dir()` > `MEASUREIT_HOME` env > legacy `MeasureItHome` env > `platformdirs` default. Subdirectories (`Databases`, `logs`, `cfg`, `Origin Files`) are created lazily on first access via `get_path()`. |
| 77 | + |
| 78 | +### Sweep Timing Constraints |
| 79 | + |
| 80 | +`inter_delay` minimum: 0.01s (10ms). `outer_delay` minimum: 0.1s (100ms). Values below these raise `ValueError`. |
| 81 | + |
| 82 | +## Testing Conventions |
| 83 | + |
| 84 | +- Mock instruments use `qcodes.parameters.Parameter` subclasses (see `conftest.py::mock_parameter`) |
| 85 | +- `fast_sweep_kwargs` fixture provides `inter_delay=0.01, save_data=False, plot_data=False, suppress_output=True` |
| 86 | +- `conftest.py` auto-clears QCoDeS instrument registry and `_ACTIVE_SWEEPS` between tests |
| 87 | +- Pandas is stubbed in `conftest.py` to avoid binary-compat issues in CI |
| 88 | + |
| 89 | +## Code Style |
| 90 | + |
| 91 | +- Ruff (line-length 88, Google docstrings, `py38` target) |
| 92 | +- Mypy with strict settings (configured in `pyproject.toml`) |
| 93 | +- `*_ui.py` files are auto-generated and excluded from all linting |
| 94 | + |
| 95 | +## Known Issues |
| 96 | + |
| 97 | +- `ipykernel` 7.0.x breaks Qt event loop; pinned to `>=6.29,!=7.*` |
| 98 | +- pytest-qt can cause macOS segfaults during teardown; `conftest.py` includes extensive guards |
0 commit comments