chore: remove deprecated flow_boundary and update docs to match new architecture
This commit is contained in:
35
crates/vendors/data/copeland/compressors/ZP49KCE-TFD.json
vendored
Normal file
35
crates/vendors/data/copeland/compressors/ZP49KCE-TFD.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"model": "ZP49KCE-TFD",
|
||||
"manufacturer": "Copeland",
|
||||
"refrigerant": "R410A",
|
||||
"capacity_coeffs": [
|
||||
16500.0,
|
||||
320.0,
|
||||
-110.0,
|
||||
2.3,
|
||||
1.6,
|
||||
-3.8,
|
||||
0.04,
|
||||
0.025,
|
||||
-0.018,
|
||||
0.009
|
||||
],
|
||||
"power_coeffs": [
|
||||
4100.0,
|
||||
88.0,
|
||||
42.0,
|
||||
0.75,
|
||||
0.45,
|
||||
1.1,
|
||||
0.018,
|
||||
0.009,
|
||||
0.008,
|
||||
0.004
|
||||
],
|
||||
"validity": {
|
||||
"t_suction_min": -10.0,
|
||||
"t_suction_max": 20.0,
|
||||
"t_discharge_min": 25.0,
|
||||
"t_discharge_max": 65.0
|
||||
}
|
||||
}
|
||||
35
crates/vendors/data/copeland/compressors/ZP54KCE-TFD.json
vendored
Normal file
35
crates/vendors/data/copeland/compressors/ZP54KCE-TFD.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"model": "ZP54KCE-TFD",
|
||||
"manufacturer": "Copeland",
|
||||
"refrigerant": "R410A",
|
||||
"capacity_coeffs": [
|
||||
18000.0,
|
||||
350.0,
|
||||
-120.0,
|
||||
2.5,
|
||||
1.8,
|
||||
-4.2,
|
||||
0.05,
|
||||
0.03,
|
||||
-0.02,
|
||||
0.01
|
||||
],
|
||||
"power_coeffs": [
|
||||
4500.0,
|
||||
95.0,
|
||||
45.0,
|
||||
0.8,
|
||||
0.5,
|
||||
1.2,
|
||||
0.02,
|
||||
0.01,
|
||||
0.01,
|
||||
0.005
|
||||
],
|
||||
"validity": {
|
||||
"t_suction_min": -10.0,
|
||||
"t_suction_max": 20.0,
|
||||
"t_discharge_min": 25.0,
|
||||
"t_discharge_max": 65.0
|
||||
}
|
||||
}
|
||||
4
crates/vendors/data/copeland/compressors/index.json
vendored
Normal file
4
crates/vendors/data/copeland/compressors/index.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
"ZP54KCE-TFD",
|
||||
"ZP49KCE-TFD"
|
||||
]
|
||||
35
crates/vendors/data/danfoss/compressors/SH090-4.json
vendored
Normal file
35
crates/vendors/data/danfoss/compressors/SH090-4.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"model": "SH090-4",
|
||||
"manufacturer": "Danfoss",
|
||||
"refrigerant": "R410A",
|
||||
"capacity_coeffs": [
|
||||
25000.0,
|
||||
500.0,
|
||||
-150.0,
|
||||
3.5,
|
||||
2.5,
|
||||
-5.0,
|
||||
0.05,
|
||||
0.03,
|
||||
-0.02,
|
||||
0.01
|
||||
],
|
||||
"power_coeffs": [
|
||||
6000.0,
|
||||
150.0,
|
||||
60.0,
|
||||
1.5,
|
||||
1.0,
|
||||
1.5,
|
||||
0.02,
|
||||
0.015,
|
||||
0.01,
|
||||
0.005
|
||||
],
|
||||
"validity": {
|
||||
"t_suction_min": -15.0,
|
||||
"t_suction_max": 15.0,
|
||||
"t_discharge_min": 30.0,
|
||||
"t_discharge_max": 65.0
|
||||
}
|
||||
}
|
||||
35
crates/vendors/data/danfoss/compressors/SH140-4.json
vendored
Normal file
35
crates/vendors/data/danfoss/compressors/SH140-4.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"model": "SH140-4",
|
||||
"manufacturer": "Danfoss",
|
||||
"refrigerant": "R410A",
|
||||
"capacity_coeffs": [
|
||||
38000.0,
|
||||
750.0,
|
||||
-200.0,
|
||||
5.0,
|
||||
3.8,
|
||||
-7.0,
|
||||
0.08,
|
||||
0.045,
|
||||
-0.03,
|
||||
0.015
|
||||
],
|
||||
"power_coeffs": [
|
||||
9500.0,
|
||||
220.0,
|
||||
90.0,
|
||||
2.2,
|
||||
1.5,
|
||||
2.3,
|
||||
0.03,
|
||||
0.02,
|
||||
0.015,
|
||||
0.008
|
||||
],
|
||||
"validity": {
|
||||
"t_suction_min": -15.0,
|
||||
"t_suction_max": 15.0,
|
||||
"t_discharge_min": 30.0,
|
||||
"t_discharge_max": 65.0
|
||||
}
|
||||
}
|
||||
4
crates/vendors/data/danfoss/compressors/index.json
vendored
Normal file
4
crates/vendors/data/danfoss/compressors/index.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
"SH090-4",
|
||||
"SH140-4"
|
||||
]
|
||||
320
crates/vendors/src/compressors/danfoss.rs
vendored
Normal file
320
crates/vendors/src/compressors/danfoss.rs
vendored
Normal file
@@ -0,0 +1,320 @@
|
||||
//! Danfoss compressor data backend.
|
||||
//!
|
||||
//! Loads AHRI 540 compressor coefficients from JSON files in the
|
||||
//! `data/danfoss/compressors/` directory.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::error::VendorError;
|
||||
use crate::vendor_api::{
|
||||
BphxParameters, CompressorCoefficients, UaCalcParams, VendorBackend,
|
||||
};
|
||||
|
||||
/// Backend for Danfoss scroll compressor data.
|
||||
///
|
||||
/// Loads an index file (`index.json`) listing available compressor models,
|
||||
/// then eagerly pre-caches each model's JSON file into memory.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use entropyk_vendors::compressors::danfoss::DanfossBackend;
|
||||
/// use entropyk_vendors::VendorBackend;
|
||||
///
|
||||
/// let backend = DanfossBackend::new().expect("load danfoss data");
|
||||
/// let models = backend.list_compressor_models().unwrap();
|
||||
/// println!("Available: {:?}", models);
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct DanfossBackend {
|
||||
/// Root path to the Danfoss data directory.
|
||||
data_path: PathBuf,
|
||||
/// Pre-loaded compressor coefficients keyed by model name.
|
||||
compressor_cache: HashMap<String, CompressorCoefficients>,
|
||||
/// Sorted list of available models.
|
||||
sorted_models: Vec<String>,
|
||||
}
|
||||
|
||||
impl DanfossBackend {
|
||||
/// Create a new Danfoss backend, loading all compressor models from disk.
|
||||
///
|
||||
/// The data directory is resolved via the `ENTROPYK_DATA` environment variable.
|
||||
/// If unset, it falls back to the compile-time `CARGO_MANIFEST_DIR/data` in debug mode,
|
||||
/// or `./data` in release mode.
|
||||
pub fn new() -> Result<Self, VendorError> {
|
||||
let base_path = std::env::var("ENTROPYK_DATA")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("data")
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
PathBuf::from("data")
|
||||
}
|
||||
});
|
||||
|
||||
let data_path = base_path.join("danfoss");
|
||||
|
||||
let mut backend = Self {
|
||||
data_path,
|
||||
compressor_cache: HashMap::new(),
|
||||
sorted_models: Vec::new(),
|
||||
};
|
||||
|
||||
backend.load_index()?;
|
||||
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
/// Create a new Danfoss backend from a custom data path.
|
||||
///
|
||||
/// Useful for testing with alternative data directories.
|
||||
pub fn from_path(data_path: PathBuf) -> Result<Self, VendorError> {
|
||||
let mut backend = Self {
|
||||
data_path,
|
||||
compressor_cache: HashMap::new(),
|
||||
sorted_models: Vec::new(),
|
||||
};
|
||||
|
||||
backend.load_index()?;
|
||||
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
/// Load the compressor index and pre-cache all referenced models.
|
||||
fn load_index(&mut self) -> Result<(), VendorError> {
|
||||
let index_path = self.data_path.join("compressors").join("index.json");
|
||||
let index_content = std::fs::read_to_string(&index_path).map_err(|e| {
|
||||
VendorError::IoError {
|
||||
path: index_path.display().to_string(),
|
||||
source: e,
|
||||
}
|
||||
})?;
|
||||
|
||||
let models: Vec<String> = serde_json::from_str(&index_content).map_err(|e| {
|
||||
VendorError::InvalidFormat(format!("Parse error in {}: {}", index_path.display(), e))
|
||||
})?;
|
||||
|
||||
for model in models {
|
||||
match self.load_model(&model) {
|
||||
Ok(coeffs) => {
|
||||
self.compressor_cache.insert(model.clone(), coeffs);
|
||||
self.sorted_models.push(model);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("[entropyk-vendors] Skipping Danfoss model {}: {}", model, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.sorted_models.sort();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load a single compressor model from its JSON file.
|
||||
fn load_model(&self, model: &str) -> Result<CompressorCoefficients, VendorError> {
|
||||
if model.contains('/') || model.contains('\\') || model.contains("..") {
|
||||
return Err(VendorError::ModelNotFound(model.to_string()));
|
||||
}
|
||||
|
||||
let model_path = self
|
||||
.data_path
|
||||
.join("compressors")
|
||||
.join(format!("{}.json", model));
|
||||
|
||||
let content = std::fs::read_to_string(&model_path).map_err(|e| VendorError::IoError {
|
||||
path: model_path.display().to_string(),
|
||||
source: e,
|
||||
})?;
|
||||
|
||||
let coeffs: CompressorCoefficients = serde_json::from_str(&content).map_err(|e| {
|
||||
VendorError::InvalidFormat(format!("Parse error in {}: {}", model_path.display(), e))
|
||||
})?;
|
||||
|
||||
Ok(coeffs)
|
||||
}
|
||||
}
|
||||
|
||||
impl VendorBackend for DanfossBackend {
|
||||
fn vendor_name(&self) -> &str {
|
||||
"Danfoss"
|
||||
}
|
||||
|
||||
fn list_compressor_models(&self) -> Result<Vec<String>, VendorError> {
|
||||
Ok(self.sorted_models.clone())
|
||||
}
|
||||
|
||||
fn get_compressor_coefficients(
|
||||
&self,
|
||||
model: &str,
|
||||
) -> Result<CompressorCoefficients, VendorError> {
|
||||
self.compressor_cache
|
||||
.get(model)
|
||||
.cloned()
|
||||
.ok_or_else(|| VendorError::ModelNotFound(model.to_string()))
|
||||
}
|
||||
|
||||
fn list_bphx_models(&self) -> Result<Vec<String>, VendorError> {
|
||||
// Danfoss does not provide BPHX data
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn get_bphx_parameters(&self, model: &str) -> Result<BphxParameters, VendorError> {
|
||||
Err(VendorError::InvalidFormat(format!(
|
||||
"Danfoss does not provide BPHX data (requested: {})",
|
||||
model
|
||||
)))
|
||||
}
|
||||
|
||||
fn compute_ua(&self, model: &str, _params: &UaCalcParams) -> Result<f64, VendorError> {
|
||||
Err(VendorError::InvalidFormat(format!(
|
||||
"Danfoss does not provide BPHX/UA data (requested: {})",
|
||||
model
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_backend_new() {
|
||||
let backend = DanfossBackend::new();
|
||||
assert!(backend.is_ok(), "DanfossBackend::new() should succeed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_backend_from_path() {
|
||||
let base_path = std::env::var("ENTROPYK_DATA")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("data")
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
PathBuf::from("data")
|
||||
}
|
||||
});
|
||||
|
||||
let backend = DanfossBackend::from_path(base_path.join("danfoss"));
|
||||
assert!(backend.is_ok(), "DanfossBackend::from_path() should succeed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_vendor_name() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
assert_eq!(backend.vendor_name(), "Danfoss");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_list_compressor_models() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
let models = backend.list_compressor_models().unwrap();
|
||||
assert_eq!(models.len(), 2);
|
||||
assert!(models.contains(&"SH140-4".to_string()));
|
||||
assert!(models.contains(&"SH090-4".to_string()));
|
||||
assert_eq!(models, vec!["SH090-4".to_string(), "SH140-4".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_get_compressor_sh140() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
let coeffs = backend
|
||||
.get_compressor_coefficients("SH140-4")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(coeffs.model, "SH140-4");
|
||||
assert_eq!(coeffs.manufacturer, "Danfoss");
|
||||
assert_eq!(coeffs.refrigerant, "R410A");
|
||||
assert_eq!(coeffs.capacity_coeffs.len(), 10);
|
||||
assert_eq!(coeffs.power_coeffs.len(), 10);
|
||||
// Check first capacity coefficient
|
||||
assert!((coeffs.capacity_coeffs[0] - 38000.0).abs() < 1e-10);
|
||||
// Check first power coefficient
|
||||
assert!((coeffs.power_coeffs[0] - 9500.0).abs() < 1e-10);
|
||||
// Check last capacity coefficient
|
||||
assert!((coeffs.capacity_coeffs[9] - 0.015).abs() < 1e-10);
|
||||
// Check last power coefficient
|
||||
assert!((coeffs.power_coeffs[9] - 0.008).abs() < 1e-10);
|
||||
// mass_flow_coeffs not provided in Danfoss data
|
||||
assert!(coeffs.mass_flow_coeffs.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_get_compressor_sh090() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
let coeffs = backend
|
||||
.get_compressor_coefficients("SH090-4")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(coeffs.model, "SH090-4");
|
||||
assert_eq!(coeffs.manufacturer, "Danfoss");
|
||||
assert_eq!(coeffs.refrigerant, "R410A");
|
||||
assert!((coeffs.capacity_coeffs[0] - 25000.0).abs() < 1e-10);
|
||||
assert!((coeffs.power_coeffs[0] - 6000.0).abs() < 1e-10);
|
||||
assert!((coeffs.capacity_coeffs[9] - 0.01).abs() < 1e-10);
|
||||
assert!((coeffs.power_coeffs[9] - 0.005).abs() < 1e-10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_validity_range() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
let coeffs = backend
|
||||
.get_compressor_coefficients("SH140-4")
|
||||
.unwrap();
|
||||
|
||||
assert!((coeffs.validity.t_suction_min - (-15.0)).abs() < 1e-10);
|
||||
assert!((coeffs.validity.t_suction_max - 15.0).abs() < 1e-10);
|
||||
assert!((coeffs.validity.t_discharge_min - 30.0).abs() < 1e-10);
|
||||
assert!((coeffs.validity.t_discharge_max - 65.0).abs() < 1e-10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_model_not_found() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
let result = backend.get_compressor_coefficients("NONEXISTENT");
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
VendorError::ModelNotFound(m) => assert_eq!(m, "NONEXISTENT"),
|
||||
other => panic!("Expected ModelNotFound, got: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_list_bphx_empty() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
let models = backend.list_bphx_models().unwrap();
|
||||
assert!(models.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_get_bphx_returns_error() {
|
||||
let backend = DanfossBackend::new().unwrap();
|
||||
let result = backend.get_bphx_parameters("anything");
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
VendorError::InvalidFormat(msg) => {
|
||||
assert!(msg.contains("Danfoss does not provide BPHX"));
|
||||
}
|
||||
other => panic!("Expected InvalidFormat, got: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_danfoss_object_safety() {
|
||||
let backend: Box<dyn VendorBackend> = Box::new(DanfossBackend::new().unwrap());
|
||||
assert_eq!(backend.vendor_name(), "Danfoss");
|
||||
let models = backend.list_compressor_models().unwrap();
|
||||
assert!(!models.is_empty());
|
||||
}
|
||||
}
|
||||
11
crates/vendors/src/compressors/mod.rs
vendored
Normal file
11
crates/vendors/src/compressors/mod.rs
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//! Compressor vendor backend implementations.
|
||||
//!
|
||||
//! Each vendor module implements [`VendorBackend`](crate::VendorBackend) for
|
||||
//! loading AHRI 540 compressor coefficients from vendor-specific data files.
|
||||
|
||||
/// Copeland (Emerson) compressor data backend.
|
||||
pub mod copeland;
|
||||
|
||||
// Future vendor implementations (stories 11.14, 11.15):
|
||||
pub mod danfoss;
|
||||
// pub mod bitzer; // Story 11.15
|
||||
7
crates/vendors/src/heat_exchangers/mod.rs
vendored
Normal file
7
crates/vendors/src/heat_exchangers/mod.rs
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
//! Heat exchanger vendor backend implementations.
|
||||
//!
|
||||
//! Each vendor module implements [`VendorBackend`](crate::VendorBackend) for
|
||||
//! loading BPHX parameters and UA curves from vendor-specific data files.
|
||||
|
||||
/// SWEP brazed-plate heat exchanger data backend.
|
||||
pub mod swep;
|
||||
3
crates/vendors/src/lib.rs
vendored
3
crates/vendors/src/lib.rs
vendored
@@ -21,6 +21,9 @@ pub mod compressors;
|
||||
pub mod heat_exchangers;
|
||||
|
||||
// Public re-exports for convenience
|
||||
pub use compressors::copeland::CopelandBackend;
|
||||
pub use compressors::danfoss::DanfossBackend;
|
||||
pub use heat_exchangers::swep::SwepBackend;
|
||||
pub use error::VendorError;
|
||||
pub use vendor_api::{
|
||||
BphxParameters, CompressorCoefficients, CompressorValidityRange, UaCalcParams, UaCurve,
|
||||
|
||||
Reference in New Issue
Block a user