Source code for impedance_agent.core.models

# src/core/models.py
from typing import List, Dict, Optional, Any
from pydantic import BaseModel, Field, ConfigDict
import numpy as np
from datetime import datetime

[docs] class ImpedanceData(BaseModel): """Container for electrochemical impedance spectroscopy data.""" model_config = ConfigDict(arbitrary_types_allowed=True) frequency: np.ndarray = Field(description="Array of measurement frequencies in Hz") real: np.ndarray = Field(description="Real component of impedance in ohms") imaginary: np.ndarray = Field(description="Imaginary component of impedance in ohms") measurement_id: Optional[str] = Field(None, description="Unique identifier for the measurement") timestamp: Optional[datetime] = Field(None, description="When the measurement was taken")
[docs] class FitQualityMetrics(BaseModel): """Quality metrics for assessing impedance fits. References ---------- [1] Boukamp, B.A. "A Linear Kronig-Kramers Transform Test for Immittance Data Validation." J. Electrochem. Soc. 142 (1995) """ model_config = ConfigDict(arbitrary_types_allowed=True) vector_difference: float = Field(description="Quantified vector difference metric") vector_quality: str = Field(description='Assessment of vector matching quality ("excellent", "acceptable", "poor")') path_deviation: float = Field(description="Quantified path following metric") path_quality: str = Field(description='Assessment of path following quality ("excellent", "acceptable", "poor")') overall_quality: str = Field(description="Combined quality assessment")
[docs] class FitResult(BaseModel): """Results from equivalent circuit model fitting. Notes ----- The fitting process uses weighted complex nonlinear least squares with: * Parameter estimation minimizing: χ² = Σᵢ wᵢ|Z_{exp,i} - Z_{fit,i}|² * Akaike Information Criterion: AIC = -2ln(L) + 2k where L is likelihood and k is number of parameters * Parameter uncertainties via QR decomposition of weighted Jacobian * Correlation matrix from Hessian of objective function References ---------- [1] Sadkowski, A. "CNLS fits and Kramers-Kronig validation of resonant EIS data." Journal of Electroanalytical Chemistry (2004) [2] Ingdal, M., Johnsen, R., & Harrington, D. A. "The Akaike information criterion in weighted regression of immittance data." Electrochimica Acta (2019) """ model_config = ConfigDict(arbitrary_types_allowed=True) parameters: List[float] = Field(description="Optimized model parameters") errors: List[float] = Field(description="Parameter uncertainties (standard errors)") param_info: List[Dict] = Field(description="Parameter names and bounds") correlation_matrix: Optional[np.ndarray] = Field(None, description="Parameter correlation coefficients") chi_square: float = Field(description="Chi-square statistic") aic: float = Field(description="Akaike Information Criterion") wrms: float = Field(description="Weighted root mean square error") dof: int = Field(description="Degrees of freedom") measurement_id: Optional[str] = Field(None, description="Unique identifier for the measurement") timestamp: Optional[datetime] = Field(None, description="When the fit was performed") Z_fit: Optional[np.ndarray] = Field(None, description="Fitted impedance values") fit_quality: Optional[FitQualityMetrics] = Field(None, description="Detailed quality metrics")
[docs] class DRTResult(BaseModel): """Results from Distribution of Relaxation Times (DRT) analysis. Notes ----- The DRT analysis solves the integral equation: Z(ω) - R_∞ = R_pol ∫₀^∞ γ(τ)/(1 + iωτ)dτ Uses Tikhonov Regularization + Projected Gradient (TRPG) method. References ---------- [1] Kulikovsky, A. "PEM fuel cell distribution of relaxation times: a method for the calculation and behavior of an oxygen transport peak." (2021) """ model_config = ConfigDict(arbitrary_types_allowed=True) tau: np.ndarray = Field(description="Time constant array") gamma: np.ndarray = Field(description="DRT function values") peak_frequencies: List[float] = Field(description="Characteristic frequencies of identified processes") peak_polarizations: List[float] = Field(description="Polarization resistance of each process") regularization_param: float = Field(description="Optimal regularization parameter") residual: float = Field(description="Fitting residual") Z_fit: Optional[np.ndarray] = Field(None, description="Reconstructed impedance from DRT") residuals_real: Optional[np.ndarray] = Field(None, description="Real part fitting residuals") residuals_imag: Optional[np.ndarray] = Field(None, description="Imaginary part fitting residuals") fit_quality: Optional[FitQualityMetrics] = Field(None, description="Quality assessment metrics")
[docs] class LinKKResult(BaseModel): """Results from Lin-KK (Linear Kramers-Kronig) validation analysis. References ---------- [1] Boukamp, B.A. "A Linear Kronig‐Kramers Transform Test for Immittance Data Validation." J. Electrochem. Soc. 142 (1995) """ model_config = ConfigDict(arbitrary_types_allowed=True) M: int = Field(description="Number of basis functions used") mu: float = Field(description="Spacing parameter for basis functions") Z_fit: np.ndarray = Field(description="K-K consistent impedance") residuals_real: np.ndarray = Field(description="Real part residuals") residuals_imag: np.ndarray = Field(description="Imaginary part residuals") max_residual: float = Field(description="Maximum absolute residual") mean_residual: float = Field(description="Mean absolute residual") fit_quality: Optional[FitQualityMetrics] = Field(None, description="Quality assessment metrics")
[docs] class AnalysisResult(BaseModel): """Comprehensive impedance analysis results combining multiple methods. References ---------- [1] Barsoukov, E., Macdonald, J.R. "Impedance Spectroscopy: Theory, Experiment, and Applications." Wiley (2018) """ model_config = ConfigDict(arbitrary_types_allowed=True) ecm_fit: Optional[FitResult] = Field(None, description="Equivalent circuit fitting results") drt_fit: Optional[DRTResult] = Field(None, description="DRT analysis results") linkk_fit: Optional[LinKKResult] = Field(None, description="Data validation results") summary: str = Field("", description="Key findings and conclusions") recommendations: List[str] = Field(default_factory=list, description="Suggested next steps and improvements") overall_assessment: Dict[str, Any] = Field( default_factory=dict, description="Overall analysis including cross-method fit quality comparison" )