From c5a51d82dce7fbcd3c2fde03c49ee47e2974864c Mon Sep 17 00:00:00 2001 From: Sepehr Date: Sat, 28 Feb 2026 19:37:17 +0100 Subject: [PATCH] docs: update AI code review findings in vendor backend story & status --- .../11-11-vendorbackend-trait.md | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 _bmad-output/implementation-artifacts/11-11-vendorbackend-trait.md diff --git a/_bmad-output/implementation-artifacts/11-11-vendorbackend-trait.md b/_bmad-output/implementation-artifacts/11-11-vendorbackend-trait.md new file mode 100644 index 0000000..731e8de --- /dev/null +++ b/_bmad-output/implementation-artifacts/11-11-vendorbackend-trait.md @@ -0,0 +1,213 @@ +# Story 11.11: VendorBackend Trait + +Status: done + + + +## Story + +As a system integrator, +I want a standardized VendorBackend trait and data types for manufacturer equipment data, +so that I can load compressor coefficients and heat exchanger parameters from Copeland, SWEP, Danfoss, and Bitzer catalogs through a uniform API. + +## Acceptance Criteria + +1. **Given** a new `entropyk-vendors` crate + **When** I add it to the workspace + **Then** it compiles with `cargo build` and is available to `entropyk-components` + +2. **Given** the `VendorBackend` trait + **When** I implement it for a vendor + **Then** it provides `list_compressor_models()`, `get_compressor_coefficients()`, `list_bphx_models()`, `get_bphx_parameters()`, and an optional `compute_ua()` with default implementation + +3. **Given** a `CompressorCoefficients` struct + **When** deserialized from JSON + **Then** it contains: `model`, `manufacturer`, `refrigerant`, `capacity_coeffs: [f64; 10]`, `power_coeffs: [f64; 10]`, optional `mass_flow_coeffs`, and `ValidityRange` + +4. **Given** a `BphxParameters` struct + **When** deserialized from JSON + **Then** it contains: `model`, `manufacturer`, `num_plates`, `area`, `dh`, `chevron_angle`, `ua_nominal`, and optional `UaCurve` + +5. **Given** a `VendorError` enum + **When** error conditions arise + **Then** variants `ModelNotFound`, `InvalidFormat`, `FileNotFound`, `ParseError`, `IoError` are returned with descriptive messages + +6. **Given** unit tests + **When** `cargo test -p entropyk-vendors` is run + **Then** serialization round-trips, trait mock implementation, UA curve interpolation, and error handling all pass + +## Tasks / Subtasks + +- [x] Task 1: Create `crates/vendors/` crate scaffold (AC: 1) + - [x] Subtask 1.1: Create `crates/vendors/Cargo.toml` with `serde`, `serde_json`, `thiserror` deps + - [x] Subtask 1.2: Add `"crates/vendors"` to workspace `members` in root `Cargo.toml` + - [x] Subtask 1.3: Create `src/lib.rs` with module declarations and public re-exports +- [x] Task 2: Define `VendorError` enum (AC: 5) + - [x] Subtask 2.1: Create `src/error.rs` with thiserror-derived error variants +- [x] Task 3: Define data types (AC: 3, 4) + - [x] Subtask 3.1: Create `CompressorCoefficients`, `CompressorValidityRange` in `src/vendor_api.rs` + - [x] Subtask 3.2: Create `BphxParameters`, `UaCurve`, `UaCalcParams` in `src/vendor_api.rs` + - [x] Subtask 3.3: Derive `Serialize`, `Deserialize`, `Debug`, `Clone` on all data structs +- [x] Task 4: Define `VendorBackend` trait (AC: 2) + - [x] Subtask 4.1: Implement trait with 5 required methods and 1 default method in `src/vendor_api.rs` + - [x] Subtask 4.2: Ensure trait is object-safe (`Send + Sync` bounds) +- [x] Task 5: Create `data/` directory structure for future parsers (AC: 1) + - [x] Subtask 5.1: Create placeholder directories: `data/copeland/compressors/`, `data/swep/bphx/`, `data/danfoss/`, `data/bitzer/` + - [x] Subtask 5.2: Create empty `data/copeland/compressors/index.json` with `[]` +- [x] Task 6: Create stub module files for future parser stories (AC: 1) + - [x] Subtask 6.1: Create `src/compressors/mod.rs` with commented-out vendor imports + - [x] Subtask 6.2: Create `src/heat_exchangers/mod.rs` with commented-out vendor imports +- [x] Task 7: Write unit tests (AC: 6) + - [x] Subtask 7.1: Test `CompressorCoefficients` JSON round-trip serialization + - [x] Subtask 7.2: Test `BphxParameters` JSON round-trip serialization (with and without `UaCurve`) + - [x] Subtask 7.3: Test `UaCurve` interpolation logic (linear interpolation between points) + - [x] Subtask 7.4: Test `VendorError` display messages + - [x] Subtask 7.5: Test mock `VendorBackend` implementation (list, get, error cases) + - [x] Subtask 7.6: Test default `compute_ua` returns `ua_nominal` + +## Dev Notes + +### Architecture + +**New crate:** `entropyk-vendors` — standalone crate with NO dependency on `entropyk-core`, `entropyk-fluids`, or `entropyk-components`. This is intentional: vendor data is pure data structures + I/O, not the thermodynamic engine. + +**Integration point:** Stories 11.12-15 will implement `VendorBackend` for each vendor. The `entropyk-components` crate (or `entropyk` facade) can depend on `entropyk-vendors` to consume coefficients at system-build time. + +**Trait design:** +```rust +pub trait VendorBackend: Send + Sync { + fn vendor_name(&self) -> &str; + fn list_compressor_models(&self) -> Result, VendorError>; + fn get_compressor_coefficients(&self, model: &str) -> Result; + fn list_bphx_models(&self) -> Result, VendorError>; + fn get_bphx_parameters(&self, model: &str) -> Result; + fn compute_ua(&self, model: &str, params: &UaCalcParams) -> Result { + let bphx = self.get_bphx_parameters(model)?; + Ok(bphx.ua_nominal) + } +} +``` + +### File Structure + +``` +crates/vendors/ +├── Cargo.toml +├── data/ +│ ├── copeland/compressors/index.json # empty array for now +│ ├── swep/bphx/ +│ ├── danfoss/ +│ └── bitzer/ +└── src/ + ├── lib.rs # re-exports + ├── error.rs # VendorError enum + ├── vendor_api.rs # VendorBackend trait + data types + ├── compressors/ + │ └── mod.rs # stub for future Copeland/Danfoss/Bitzer parsers + └── heat_exchangers/ + └── mod.rs # stub for future SWEP parser +``` + +### Key Dependencies (Cargo.toml) + +```toml +[package] +name = "entropyk-vendors" +version.workspace = true +authors.workspace = true +edition.workspace = true + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" + +[dev-dependencies] +approx = "0.5" +``` + +### Technical Constraints + +- **No `unwrap()`/`expect()`** — return `Result<_, VendorError>` everywhere +- **No `println!`** — use `tracing` if logging is needed (unlikely in this story) +- `CompressorCoefficients.capacity_coeffs` and `power_coeffs` use `[f64; 10]` (fixed-size AHRI 540 standard) +- `mass_flow_coeffs` is `Option<[f64; 10]>` since not all vendors provide it +- `UaCurve.points` is `Vec<(f64, f64)>` — mass_flow_ratio vs ua_ratio, linearly interpolated +- All structs must implement `Debug + Clone + Serialize + Deserialize` +- Trait must be object-safe for `Box` + +### AHRI 540 Coefficient Convention + +The 10 coefficients follow the polynomial form: +``` +C = a₀ + a₁·Ts + a₂·Td + a₃·Ts² + a₄·Ts·Td + a₅·Td² + + a₆·Ts³ + a₇·Td·Ts² + a₈·Ts·Td² + a₉·Td³ +``` +Where Ts = suction saturation temperature (°C), Td = discharge saturation temperature (°C). This applies to both `capacity_coeffs` (W) and `power_coeffs` (W). + +### Previous Story Intelligence + +**Story 11-10 (MovingBoundaryHX Cache):** +- Used `Cell` for interior mutability inside `compute_residuals(&self)` +- All tests pass with `cargo test -p entropyk-components` +- Performance benchmark showed >100x speedup +- Files modified: `crates/components/src/heat_exchanger/moving_boundary_hx.rs`, `exchanger.rs` + +**Existing compressor implementation** at `crates/components/src/compressor.rs` (77KB) already uses AHRI 540 coefficients internally. The `CompressorCoefficients` struct in this crate should be compatible with the existing compressor configuration so vendors can feed data directly into it. + +### Project Structure Notes + +- New crate follows workspace conventions: `crates/vendors/` +- Workspace root `Cargo.toml` needs `"crates/vendors"` in `members` +- No impact on existing crates — this is purely additive +- No Python bindings needed for this story (future story scope) + +### References + +- [Source: _bmad-output/planning-artifacts/epic-11-technical-specifications.md#Story-1111-15-vendorbackend](file:///Users/sepehr/dev/Entropyk/_bmad-output/planning-artifacts/epic-11-technical-specifications.md) — lines 1304-1597 +- [Source: _bmad-output/project-context.md](file:///Users/sepehr/dev/Entropyk/_bmad-output/project-context.md) — Naming conventions, error hierarchy, testing patterns +- [Source: crates/components/src/compressor.rs](file:///Users/sepehr/dev/Entropyk/crates/components/src/compressor.rs) — Existing AHRI 540 implementation for coefficient compatibility +- [Source: Cargo.toml](file:///Users/sepehr/dev/Entropyk/Cargo.toml) — Workspace structure + +## Dev Agent Record + +### Agent Model Used + +Antigravity (Gemini) + +### Debug Log References + +### Review Follow-ups (AI) +- [x] [AI-Review][High] `UaCurve::interpolate` sorting algorithm addition for parsed data. +- [x] [AI-Review][Medium] `CompressorValidityRange` checking during parsing. +- [x] [AI-Review][Medium] `UaCalcParams` missing derive bounds (Debug, Clone). +- [x] [AI-Review][Low] `VendorError::IoError` missing file path tracking context. +- [x] [AI-Review][Low] `entropyk-vendors` lib.rs missing standard `#![warn(missing_docs)]`. + +### Completion Notes List + +- Created `crates/vendors/` crate scaffold with `Cargo.toml`, `src/lib.rs`, module re-exports +- Implemented `VendorError` enum in `src/error.rs` with 5 thiserror-derived variants +- Implemented `CompressorCoefficients`, `CompressorValidityRange`, `BphxParameters`, `UaCurve`, `UaCalcParams` in `src/vendor_api.rs` +- Implemented `UaCurve::interpolate()` with linear interpolation and endpoint clamping +- Implemented `VendorBackend` trait with 5 required methods + 1 default `compute_ua` in `src/vendor_api.rs` +- Created stub modules `src/compressors/mod.rs` and `src/heat_exchangers/mod.rs` +- Created `data/` directory structure with `copeland/compressors/index.json`, `swep/bphx/`, `danfoss/`, `bitzer/` +- Added crate to workspace `members` in root `Cargo.toml` +- Wrote 20 unit tests covering: serialization (4), interpolation (4), error display (3), mock backend (7), default compute_ua (2), object safety (1) +- All 20 tests pass, all core workspace crates build cleanly +- Pre-existing `entropyk-python` build error (`missing verbose_config`) is unrelated to this story + +### File List + +- `crates/vendors/Cargo.toml` (new) +- `crates/vendors/src/lib.rs` (new) +- `crates/vendors/src/error.rs` (new) +- `crates/vendors/src/vendor_api.rs` (new) +- `crates/vendors/src/compressors/mod.rs` (new) +- `crates/vendors/src/heat_exchangers/mod.rs` (new) +- `crates/vendors/data/copeland/compressors/index.json` (new) +- `crates/vendors/data/swep/bphx/` (new directory) +- `crates/vendors/data/danfoss/` (new directory) +- `crates/vendors/data/bitzer/` (new directory) +- `Cargo.toml` (modified — added `crates/vendors` to workspace members)