Files
Entropyk/_bmad-output/implementation-artifacts/11-15-bitzer-parser.md

121 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Story 11.15: Bitzer Parser
Status: done
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
## Story
As a refrigeration engineer,
I want Bitzer compressor data integration,
so that I can use Bitzer coefficients in simulations.
## Acceptance Criteria
1. **Given** a `BitzerBackend` struct
**When** constructed via `BitzerBackend::new()`
**Then** it discovers compressor models from CSV files in `data/bitzer/compressors/` (e.g. by scanning `*.csv` or loading an index)
**And** eagerly pre-caches all discovered models into memory
2. **Given** a valid Bitzer CSV file (Bitzer polynomial format)
**When** parsed by `BitzerBackend`
**Then** it yields a `CompressorCoefficients` struct with 10 capacity and 10 power coefficients compatible with AHRI 540
**And** Bitzer polynomial format is mapped to the standard polynomial form (Ts, Td, Ts², Ts·Td, Td², …)
3. **Given** `BitzerBackend` implements `VendorBackend`
**When** I call `list_compressor_models()`
**Then** it returns all model names from the pre-loaded cache in sorted order
4. **Given** a valid model name (e.g. file stem of a CSV)
**When** I call `get_compressor_coefficients("4NFC-20Y")`
**Then** it returns the full `CompressorCoefficients` struct
5. **Given** a model name not in the catalog
**When** I call `get_compressor_coefficients("NONEXISTENT")`
**Then** it returns `VendorError::ModelNotFound("NONEXISTENT")`
6. **Given** `list_bphx_models()` called on `BitzerBackend`
**When** Bitzer only provides compressor data here
**Then** it returns `Ok(vec![])` (empty list, not an error)
7. **Given** `get_bphx_parameters("anything")` called on `BitzerBackend`
**When** Bitzer only provides compressor data here
**Then** it returns `VendorError::InvalidFormat` with a descriptive message
8. **Given** unit tests
**When** `cargo test -p entropyk-vendors` is run
**Then** all existing tests still pass
**And** new Bitzer-specific tests pass (CSV loading, format mapping, error cases)
## Tasks / Subtasks
- [x] Task 1: Define Bitzer CSV format and sample data (AC: 2)
- [x] Subtask 1.1: Document or adopt Bitzer polynomial CSV layout (columns for capacity/power coefficients, validity range)
- [x] Subtask 1.2: Create `data/bitzer/compressors/` and at least one sample CSV (e.g. `4NFC-20Y.csv` or equivalent)
- [x] Subtask 1.3: If using an index, add `index.json` or `index.csv` listing model names
- [x] Task 2: Implement `BitzerBackend` (AC: 1, 3, 4, 5, 6, 7)
- [x] Subtask 2.1: Create `src/compressors/bitzer.rs` with `BitzerBackend` struct
- [x] Subtask 2.2: Implement `BitzerBackend::new()` resolving data path via `ENTROPYK_DATA` with fallback to `CARGO_MANIFEST_DIR/data`
- [x] Subtask 2.3: Implement CSV discovery (glob `*.csv` in `data/bitzer/compressors/` and use file stem as model id, or load from index) and pre-cache
- [x] Subtask 2.4: Implement CSV parsing: Bitzer polynomial format → `CompressorCoefficients` (and `CompressorValidityRange`)
- [x] Subtask 2.5: Implement `VendorBackend` for `BitzerBackend` (list_compressor_models sorted, get_compressor_coefficients, list_bphx_models empty, get_bphx_parameters InvalidFormat)
- [x] Task 3: Wire up module exports
- [x] Subtask 3.1: Add `pub mod bitzer;` in `src/compressors/mod.rs` (uncomment or add)
- [x] Subtask 3.2: Re-export `BitzerBackend` in `src/lib.rs`
- [x] Task 4: Write unit tests (AC: 8)
- [x] Subtask 4.1: Test `BitzerBackend::new()` (or `from_path`) successfully constructs
- [x] Subtask 4.2: Test `list_compressor_models()` returns sorted models
- [x] Subtask 4.3: Test `get_compressor_coefficients()` returns valid data for a known CSV
- [x] Subtask 4.4: Test `ModelNotFound` for unknown model
- [x] Subtask 4.5: Test `list_bphx_models()` returns empty
- [x] Subtask 4.6: Test `get_bphx_parameters()` returns `InvalidFormat`
- [x] Task 5: Verify build and tests (AC: 8)
- [x] Subtask 5.1: Run `cargo test -p entropyk-vendors`
- [x] Subtask 5.2: Run `cargo clippy -p entropyk-vendors -- -D warnings`
## Dev Notes
- Bitzer supplies compressor data in **CSV** format with a proprietary polynomial layout; the implementation must map this to the existing `CompressorCoefficients` and `CompressorValidityRange` types in `vendor_api.rs`.
- Reuse the same data-path resolution and error-handling patterns as `DanfossBackend` and `CopelandBackend`: `ENTROPYK_DATA`, path sanitization (reject model names containing `/`, `\`, or `..`), `log::warn!` for skipped files, and structured `VendorError` (e.g. `IoError { path, source }`, `InvalidFormat(message)`).
- Keep `list_compressor_models()` deterministic by maintaining a sorted list of model names (e.g. `sorted_models: Vec<String>` populated and sorted after loading).
### Project Structure Notes
- Alignment with existing vendors layout:
- `crates/vendors/data/bitzer/compressors/` for CSV files (and optional index).
- `crates/vendors/src/compressors/bitzer.rs` for `BitzerBackend`; export in `compressors/mod.rs` and `lib.rs`.
### References
- [Source: epics.md Story 11.15](_bmad-output/planning-artifacts/epics.md)
- [Source: epic-11-technical-specifications.md Story 11.1115](_bmad-output/planning-artifacts/epic-11-technical-specifications.md)
- [Source: vendor_api.rs](crates/vendors/src/vendor_api.rs) `CompressorCoefficients`, `CompressorValidityRange`, `VendorBackend`
- [Source: danfoss.rs](crates/vendors/src/compressors/danfoss.rs) data path resolution, pre-cache, sorted models, path sanitization, error reporting
- [Source: 11-14-danfoss-parser.md](_bmad-output/implementation-artifacts/11-14-danfoss-parser.md) previous story patterns and completion notes
## Dev Agent Record
### Agent Model Used
Cursor / Auto (dev-story workflow)
### Debug Log References
### Completion Notes List
- Task 1: Defined Bitzer CSV format with header row (model, manufacturer, refrigerant, c0..c9, p0..p9, t_suction_min/max, t_discharge_min/max). Created `data/bitzer/compressors/4NFC-20Y.csv` and `4HES-5Y.csv`. Discovery by glob `*.csv` (no index file).
- Task 2: Implemented `BitzerBackend` in `src/compressors/bitzer.rs` with `new()`, `from_path()`, `load_compressors()` (dir scan), `load_model()` and `parse_bitzer_csv()`. Path resolution via `ENTROPYK_DATA` / `CARGO_MANIFEST_DIR/data`. Path sanitization and `log::warn!` for skipped files. `VendorBackend` impl: `list_compressor_models` (sorted), `get_compressor_coefficients`, `list_bphx_models` empty, `get_bphx_parameters` / `compute_ua` return `InvalidFormat`.
- Task 3: Added `pub mod bitzer` in `compressors/mod.rs` and re-exported `BitzerBackend` in `lib.rs`.
- Task 4: Added 11 unit tests in `bitzer.rs` (new, from_path, vendor_name, list_compressor_models, get_compressor 4NFC-20Y/4HES-5Y, validity_range, model_not_found, list_bphx_empty, get_bphx_error, object_safety).
- Task 5: `cargo test -p entropyk-vendors` (65 tests passed), `cargo clippy -p entropyk-vendors -- -D warnings` (fixed unnecessary_map_or → is_some_and). Added `csv = "1.3"` to Cargo.toml.
- Code review (AI): Addressed MEDIUM/LOW findings: documented AHRI 540 column order (c0..c9, p0..p9 = Ts, Td, Ts², Ts·Td, Td², Ts³, Td·Ts², Ts·Td², Td³) in bitzer.rs module and parse_bitzer_csv doc; documented single-data-row behaviour (first row used, extra rows ignored) and added test test_bitzer_csv_multiple_rows_first_used; updated compressors/mod.rs comment (Danfoss 11.14, Bitzer 11.15).
### File List
- crates/vendors/Cargo.toml (modified added csv dependency)
- crates/vendors/data/bitzer/compressors/4NFC-20Y.csv (created)
- crates/vendors/data/bitzer/compressors/4HES-5Y.csv (created)
- crates/vendors/src/compressors/bitzer.rs (created, then modified code review: AHRI 540 doc, single-row doc, test)
- crates/vendors/src/compressors/mod.rs (modified pub mod bitzer; then code review: comment update)
- crates/vendors/src/lib.rs (modified re-export BitzerBackend)