# 07 — Public API Surface & Build-Hygiene Audit This document records the final consistency audit for milestone #1 (Architecture & API Design Document). It is the authoritative map from the **canonical public names** to the **modules that define them**, and should be updated whenever the public surface changes (see `05_errors_extensibility.md` for the deprecation policy). ## 1. Canonical public API surface Everything below is importable directly from `pidtune` unless marked *namespace-only* (then it is accessed via its subpackage). ### Exceptions (`pidtune.exceptions`) | Name | Kind | Notes | |---|---|---| | `PIDTuneError` | class | Root of the hierarchy. | | `ModelError` | class | Model-operation failures. | | `ModelValidationError` | class | Also a `ValueError`. Raised eagerly at model construction. | | `IdentificationError` | class | Step-test fitting failures. | | `TuningError` | class | Base for tuning failures. | | `TuningNotApplicableError` | class | Method/model mismatch. | | `ConvergenceError` | class | Carries `iterations` and `best_result` attributes. | | `SimulationError` | class | Integrator blow-up, bad time grids. | | `ConfigurationError` | class | Also a `ValueError`. Bad algorithm options. | | `MissingDependencyError` | class | Also an `ImportError`. Carries `dependency`, `feature`, `extra`. | ### Models (`pidtune.models`) | Name | Defined in | Notes | |---|---|---| | `ProcessModel` | `models/base.py` | ABC: step/frequency response, validation, serialisation. | | `FOPDTModel` | `models/fopdt.py` | `gain`, `time_constant`, `dead_time`. | | `SOPDTModel` | `models/sopdt.py` | Two-time-constant and ωn/ζ parameterisations. | | `TransferFunctionModel` | `models/transfer_function.py` | num/den coefficient arrays + optional dead time. | ### Controllers (`pidtune.controllers`) | Name | Defined in | Notes | |---|---|---| | `PIDGains` | `controllers/gains.py` | Immutable; form conversions. | | `ControllerForm` | `controllers/gains.py` | Enum: `PARALLEL`, `STANDARD`, `SERIES`. | | `PIDController` | `controllers/pid.py` | Discrete PID with anti-windup, derivative filter, bumpless transfer. | ### Tuners (`pidtune.tuners`) | Name | Defined in | Notes | |---|---|---| | `Tuner` | `tuners/base.py` | ABC with `tune(model, **options) -> TuningResult`. | | `TuningResult` | `tuners/base.py` | `gains`, `method`, `diagnostics`, provenance metadata. | | `ZieglerNicholsTuner` | `tuners/ziegler_nichols.py` | Reaction-curve and ultimate-cycle variants. | | `CohenCoonTuner` | `tuners/cohen_coon.py` | Requires `dead_time > 0` (else `TuningNotApplicableError`). | | `AMIGOTuner` | `tuners/amigo.py` | Robustness-oriented rules. | | `IMCTuner` | `tuners/imc.py` | `lambda_factor` selects the closed-loop time constant. | | `RelayAutotuner` | `tuners/relay.py` | Runs against live/simulated plant; may raise `ConvergenceError`. | | `OptimizationTuner` | `tuners/optimization.py` | Lazy SciPy import; raises `MissingDependencyError` without the `optim` extra. | ### Namespace-only subpackages These are re-exported as module objects from `pidtune` (i.e. `pidtune.simulation`, `pidtune.identification`, `pidtune.metrics`), and their members are **not** lifted to the top level in 0.1. This keeps the top-level surface small while simulation/identification APIs stabilise; promotion to the top level is a roadmap item for 0.2 (`06_roadmap.md`). * `pidtune.simulation` — plant wrappers (`plant.py`), test signals (`signals.py`), and the closed-loop simulator (`loop.py`). * `pidtune.identification` — step-response model fitting (`step_response.py`). * `pidtune.metrics` — IAE/ISE/ITAE, overshoot, settling time, rise time. ## 2. Dependency matrix | Dependency | Constraint | Required by | Declared in | |---|---|---|---| | `numpy` | `>=1.22` | models, controllers, tuners, simulation, identification, metrics | `[project.dependencies]` | | `scipy` | `>=1.10` | `tuners/optimization.py` (minimizers); optional transfer-function utilities | `[project.optional-dependencies] optim` (and `dev`) | Rules enforced by this audit: 1. **Core imports NumPy only.** Any module importing SciPy must do so *inside* the function/method that needs it and raise `MissingDependencyError("scipy", feature, "optim")` on `ImportError`. `tuners/optimization.py` follows this pattern; if a future revision of `models/transfer_function.py` adds SciPy-backed utilities, the same pattern applies. 2. **No undeclared imports.** Standard library + NumPy are the only unconditional imports across `src/pidtune`. CI (roadmap item M2) will enforce this with an import-linter contract. 3. **No lockfile is committed.** Resolution is left to the installer; contributors generate environments with `pip install -e .[dev]`. ## 3. Audit checklist (performed for milestone sign-off) - [x] Every name in `pidtune.__all__` is imported in `src/pidtune/__init__.py` and defined in the module listed in §1. - [x] Subpackage `__init__` files (`models`, `controllers`, `tuners`) re-export exactly the §1 names with explicit `__all__` lists. - [x] `pyproject.toml` declares `numpy>=1.22` as the sole runtime dependency; SciPy is confined to the `optim`/`dev` extras. - [x] No exact version pins; all constraints are lower bounds. - [x] `requires-python = ">=3.9"`; all stubs use `from __future__ import annotations` so PEP 604 unions (`X | None`) are safe on 3.9. - [x] Build backend (`hatchling>=1.21`) configured for the `src/` layout via `[tool.hatch.build.targets.wheel] packages`. - [x] Exception classes raised in docstrings/contracts across `docs/design/*.md` all exist in `pidtune/exceptions.py`. - [ ] *Deferred to milestone M2 (implementation):* executable test that `import pidtune` succeeds and `pidtune.__all__` resolves — this requires a runnable environment and will be the first CI check. ## 4. Known follow-ups for implementers * When `simulation` and `identification` members are promoted to the top level (0.2), add them to §1 and to `pidtune.__all__` in the same change set. * `ConvergenceError.best_result` is typed `object | None` at the design stage; tighten to a `TypeVar`-parameterised generic if it proves ergonomic during implementation. * Verify on first CI run that `mkdocstrings` renders the NumPy-style docstrings used throughout the stubs (configured in the `docs` extra).