pychnosz.models.archer_wang API documentation
Description: Archer & Wang (1990) dielectric constant correlation for water …

# Module `pychnosz.models.archer_wang`

Archer & Wang (1990) dielectric constant correlation for water.

This module implements the accurate dielectric constant calculation used in the original R version of CHNOSZ.

Reference: Archer, D. G., and Wang, P. (1990) The dielectric constant of water and Debye-Hückel limiting law slopes. Journal of Physical and Chemical Reference Data, 19, 371-411.

## Functions

`def water_AW90(T: float | numpy.ndarray = 298.15,
rho: float | numpy.ndarray = 1000.0,
P: float | numpy.ndarray = 0.1) ‑> float | numpy.ndarray`
  Expand source code

      def water_AW90(T: Union[float, np.ndarray] = 298.15,
                     rho: Union[float, np.ndarray] = 1000.0,
                     P: Union[float, np.ndarray] = 0.1) -> Union[float, np.ndarray]:
          """
          Calculate dielectric constant of water using Archer & Wang (1990) correlation.

          This is a direct Python translation of the R function water.AW90() from
          the original CHNOSZ package.

          Parameters
          ----------
          T : float or array
              Temperature in Kelvin
          rho : float or array
              Density in kg/m³
          P : float or array
              Pressure in MPa

          Returns
          -------
          float or array
              Dielectric constant (dimensionless)

          Examples
          --------
          >>> # Water at 25°C, 1000 kg/m³, 0.1 MPa
          >>> eps = water_AW90(298.15, 1000.0, 0.1)
          >>> print(f"Dielectric constant: {eps:.1f}")  # Should be ~78.4
          """

          # Convert inputs to arrays
          T = np.atleast_1d(np.asarray(T, dtype=float))
          rho = np.atleast_1d(np.asarray(rho, dtype=float))
          P = np.atleast_1d(np.asarray(P, dtype=float))

          # Make all arrays the same length
          max_len = max(len(T), len(rho), len(P))
          if len(T) < max_len:
              T = np.resize(T, max_len)
          if len(rho) < max_len:
              rho = np.resize(rho, max_len)
          if len(P) < max_len:
              P = np.resize(P, max_len)

          # Table 2 coefficients from Archer & Wang (1990)
          b = np.array([
              -4.044525E-2, 103.6180,    75.32165,
              -23.23778,    -3.548184,   -1246.311,
              263307.7,     -6.928953E-1, -204.4473
          ])

          # Physical constants
          alpha = 18.1458392E-30  # polarizability, m³
          mu = 6.1375776E-30      # dipole moment, C·m
          N_A = 6.0221367E23      # Avogadro's number, mol⁻¹
          k = 1.380658E-23        # Boltzmann constant, J·K⁻¹
          M = 0.0180153           # molar mass of water, kg/mol
          rho_0 = 1000.0          # reference density, kg/m³
          epsilon_0 = 8.8541878E-12  # permittivity of vacuum, C²·J⁻¹·m⁻¹

          # Initialize output
          epsilon = np.full_like(T, np.nan)

          for i in range(len(T)):
              T_i = T[i]
              rho_i = rho[i]
              P_i = P[i]

              # Skip invalid conditions
              if np.isnan(T_i) or np.isnan(rho_i) or np.isnan(P_i):
                  continue
              if T_i <= 0 or rho_i <= 0 or P_i < 0:
                  continue

              # Equation 3: rho function
              def rhofun():
                  return (b[0]*P_i/T_i +
                          b[1]/np.sqrt(T_i) +
                          b[2]/(T_i-215) +
                          b[3]/np.sqrt(T_i-215) +
                          b[4]/(T_i-215)**0.25 +
                          np.exp(b[5]/T_i + b[6]/T_i**2 + b[7]*P_i/T_i + b[8]*P_i/T_i**2))

              # g function
              def gfun():
                  return rhofun() * rho_i/rho_0 + 1.0

              # mu function
              def mufun():
                  return gfun() * mu**2

              # Right-hand side of Equation 1
              V_m = M / rho_i  # molar volume, m³/mol
              epsfun_rhs = N_A * (alpha + mufun()/(3*epsilon_0*k*T_i)) / (3*V_m)

              # Solve quadratic equation (Equation 1 rearranged)
              # Original: (ε-1)(2ε+1)/(9ε) = rhs
              # Rearranged to: 2ε² - (9*rhs + 1)ε + 1 = 0
              # Using quadratic formula with positive root
              discriminant = (9*epsfun_rhs + 1)**2 + 8
              if discriminant < 0:
                  warnings.warn(f'water_AW90: negative discriminant at T={T_i:.1f} K, '
                               f'rho={rho_i:.0f} kg/m3', stacklevel=2)
                  continue

              epsilon_calc = (9*epsfun_rhs + 1 + np.sqrt(discriminant)) / 4.0

              # Check for reasonable result
              if epsilon_calc < 1.0 or epsilon_calc > 200.0:
                  warnings.warn(f'water_AW90: unrealistic dielectric constant {epsilon_calc:.1f} '
                               f'at T={T_i:.1f} K, rho={rho_i:.0f} kg/m3', stacklevel=2)
                  continue

              epsilon[i] = epsilon_calc

          # Return scalar if input was scalar
          if len(epsilon) == 1:
              return epsilon[0]
          else:
              return epsilon

  Calculate dielectric constant of water using Archer & Wang (1990) correlation.

  This is a direct Python translation of the R function water.AW90() from the original CHNOSZ package.

  ## Parameters

  `T` :`float` or `array`
    Temperature in Kelvin
  `rho` :`float` or `array`
    Density in kg/m³
  `P` :`float` or `array`
    Pressure in MPa

  ## Returns

  `float` or `array`
    Dielectric constant (dimensionless)

  ## Examples

      >>> # Water at 25°C, 1000 kg/m³, 0.1 MPa
      >>> eps = water_AW90(298.15, 1000.0, 0.1)
      >>> print(f"Dielectric constant: {eps:.1f}")  # Should be ~78.4