def test_molar_volume(self): # molar volume of water at [temperature(C), pressure(applied_bar), specific volume] Millero TABLE IV param = np.array([[5, 0, 1.000036], [30, 0, 1.004369], [75, 0, 1.025805], [35, 100, 1.001597], [55, 300, 1.001642]]) # converting param to [temperature(K), pressure(atm), molar volume] param[:, 0] = un.celsius_2_kelvin(param[:, 0]) # 1 atm = 1 applied_atm + 1, according to Millero param[:, 1] = (param[:, 1] / un.atm_2_bar(1)) + 1 param[:, 2] = (param[:, 2] / 1e6) * fm.WaterPropertiesFineMillero(300).MolecularWeight # testing molar volume up to a precision of 10^-11 wfm = fm.WaterPropertiesFineMillero(param[:, 0], param[:, 1]) test_vals = np.allclose(wfm.molar_volume(), param[:, 2], 0, 1e-11) self.assertTrue(test_vals)
def test_free_energy_dp_excess(self): # parameters in [temperature (K), concentration of ions (M), ion size (A), x_pair, free energy excess] param = np.array([[273.15, 1.0, 8, 0.4, -0.0036], [298.15, 1.0, 8, 0.4, -0.0038], [300.00, 1.0, 9, 0.6, -0.0064], [298.15, 2.0, 7, 0.4, -0.0074], [300.00, 4.0, 5, 0.5, -0.0086], [300.00, 3.5, 5, 0.7, -0.0071]]) obj_water_bp = wbp.WaterPropertiesFineMillero(param[:, 0]) obj_water_aw = waw.WaterPropertiesFineMillero(param[:, 0]) obj_bj = bj.Bjerrum(obj_water_bp) obj_bj_2 = bj.Bjerrum(obj_water_aw) obj_dp = dp.Dipole(obj_bj) obj_dp_2 = dp.Dipole(obj_bj_2) # testing params up to a precision of 10^-4 test_1 = np.allclose( obj_dp.free_energy_dp_excess(param[:, 1], param[:, 2], param[:, 3]), param[:, 4], 0, 1e-4) test_2 = np.allclose( obj_dp_2.free_energy_dp_excess(param[:, 1], param[:, 2], param[:, 3]), param[:, 4], 0, 1e-4) self.assertTrue(test_1) self.assertTrue(test_2)
def test_molarity_conversion(self): # 1 molar NaCl = 1.021411855 molar NaCl at 25C c = 1 water = wfm.WaterPropertiesFineMillero(298.15) salt = nacl.NaClPropertiesRogersPitzer(298.15) convert = con.molarity_2_molality(c, water.molar_volume(), salt.molar_vol(1.021411855), water.MolecularWeight) # test precision up to 10^-6 test = np.allclose(convert, 1.021411855, 0, 1e-2) self.assertTrue(test)
def test_pot_chem_dp_excess(self): # parameters in [temperature (K), concentration of ions (M), ion size (A), x_pair, [mu_p, mu_p, mu_0]] param = np.array([[ 273.15, 1.0, 8, 0.4, -1.39225705e-05, -1.39225705e-05, -5.82590279e-02 ], [ 298.15, 1.0, 8, 0.4, -1.47196945e-05, -1.47196945e-05, -6.08761754e-02 ], [ 300.00, 1.0, 9, 0.6, -2.21908764e-05, -2.21908764e-05, -4.87829304e-02 ], [ 298.15, 2.0, 7, 0.4, -4.60404423e-05, -4.60404423e-05, -8.97120601e-02 ], [ 300.00, 4.0, 5, 0.5, -3.07170181e-05, -3.07170181e-05, -1.14731930e-01 ], [ 300.00, 3.5, 5, 0.7, -9.91702493e-06, -9.91702493e-06, -7.65120554e-02 ]]) # testing params [mu_p, mu_p, mu_0] for i in range(len(param)): for j in range(3): obj_water_bp = wbp.WaterPropertiesFineMillero(param[i, 0]) obj_water_aw = waw.WaterPropertiesFineMillero(param[i, 0]) obj_bj = bj.Bjerrum(obj_water_bp) obj_bj_2 = bj.Bjerrum(obj_water_aw) obj_dp = dp.Dipole(obj_bj) obj_dp_2 = dp.Dipole(obj_bj_2) dp_method = obj_dp.pot_chem_dp_excess(param[i, 1], param[i, 2], param[i, 3]) dp_method_2 = obj_dp_2.pot_chem_dp_excess( param[i, 1], param[i, 2], param[i, 3]) # testing params [mu_p, mu_p] up to a precision of 10^-7 test_1 = np.allclose(dp_method[j], param[i, 4 + j], 0, 1e-7) test_2 = np.allclose(dp_method_2[j], param[i, 4 + j], 0, 1e-7) # testing params [mu_0] up to a precision of 10^-4 if j == 2: test_1 = np.allclose(dp_method[j], param[i, 4 + j], 0, 1e-4) test_2 = np.allclose(dp_method_2[j], param[i, 4 + j], 0, 1e-4) self.assertTrue(test_1) self.assertTrue(test_2)
def test_a_v(self): # apparent molal volume [temperature(C), Pressure(bar), molal vol (cc/mol)] Bradley & Pitzer TABLE IV param = np.array([[10, 100, 1.61], [60, 100, 2.55], [120, 100, 4.91], [70, 400, 2.59], [25, 600, 1.66]]) # converting param to [temperature(K), pressure(atm), molal vol (cc/mol)] param[:, 0] = un.celsius_2_kelvin(param[:, 0]) param[:, 1] = param[:, 1] / un.atm_2_bar(1) # testing apparent molal volume up to a precision of 10^-2 wfm = fm.WaterPropertiesFineMillero(param[:, 0], param[:, 1]) test_vals = np.allclose(wfm.a_v(), param[:, 2], 0, 1e-2) self.assertTrue(test_vals)
def molar_vol(self, m): """ Molar volume of electrolyte solution according to Pitzer :cite:`Pitzer1973a` .. math:: :label: molar_vol \\upsilon_{\\phi}(m)=\\upsilon^{\\infty}_1+\\nu|z_{+}z_{-}|A_v h(I) \\cr +2RT(\\nu_{+}\\nu_{-})m\\left[ \\frac{\\partial \\beta^{(0)}_{\\pm}}{\\partial P}+2\\frac{\\partial \\beta^{(1)}_{\\pm}}{\\partial P} g_{p1}(x)+2\\frac{\\partial \\beta^{(2)}_{\\pm}}{\\partial P}g_{p1}(x)\\right] \\cr +2RT(\\nu_{+}\\nu_{-})^{\\frac{3}{2}}m^{2}\\left[\\frac{\\partial C^{(0)}_{\\pm}}{\\partial P}+2\\frac{ \\partial C^{(1)}_{\\pm}}{\\partial P}g_{p1}(x) + 2\\frac{\\partial C^{(2)}_{\\pm}}{\\partial P} g_{p1}(x)\\right] \\cr +2RT(\\nu_{+}\\nu_{-})^2 m^{3}\\left[\\frac{\\partial D^{(0)}_{\\pm}} {\\partial P} + 2\\frac{\\partial D^{(1)}_{\\pm}}{\\partial P}g_{p1}(x)) + 2\\frac{\\partial D^{(2)}_{\\pm}}{\\partial P}g_{p1}(x)\\right] functions are defined in Eq. :eq:`wang_function_1` :return: Molar volume of electrolyte solution in SI (float) """ # the factor 10 is the conversion from J to bar cm^3 ct = 10 * un.r_gas() * self.tk # stoichiometric_coefficients nu, nu_prod, z_prod, nz_prod_plus = self.mat # ionic strength i_str = self.ionic_strength(m) # Pitzer parameters pressure derivative and Vp vp, beta0_der_p, beta1_der_p, beta2_der_p, C0_der_p, C1_der_p, C2_der_p, D0_der_p, D1_der_p, D2_der_p = self.params_der_p BV = (beta0_der_p + 2 * beta1_der_p * self.g_fun_phi(self.alpha_b1, i_str**0.5) + 2 * beta2_der_p * self.g_fun_phi(self.alpha_b1, i_str**0.5)) CV = (C0_der_p + 2 * C1_der_p * self.g_fun_phi(self.alpha_c1, i_str) + 2 * C2_der_p * self.g_fun_phi(self.alpha_c2, i_str)) DV = (D0_der_p + 2 * D1_der_p * self.g_fun_phi(self.alpha_d1, i_str**1.5) + 2 * D2_der_p * self.g_fun_phi(self.alpha_d2, i_str**1.5)) val_1 = vp + nu * z_prod * wp.WaterPropertiesFineMillero( self.tk, self.pa).a_v() * self.h_fun(i_str) val_2 = 2 * nu_prod * ct * m * BV val_3 = 2 * nu_prod**1.5 * ct * m**2 * CV val_4 = 2 * nu_prod**2 * ct * m**3 * DV # molar volume in cm^3/mol val = val_1 + val_2 + val_3 + val_4 # return in m^3/mol molar_vol = 1e-6 * val return molar_vol
def test_a_phi(self): # osmotic coefficient of water [temperature(C), Pressure(bar), osmotic coefficient] Bradley & Pitzer TABLE II param = np.array([[10, 100, 3.80e-1], [60, 100, 4.17e-1], [120, 100, 4.82e-1], [70, 400, 4.19e-1], [25, 600, 3.81e-1]]) # converting param to [temperature(K), pressure(atm), osmotic coefficient] param[:, 0] = un.celsius_2_kelvin(param[:, 0]) param[:, 1] = param[:, 1] / un.atm_2_bar(1) # testing osmotic coefficient up to a precision of 10^-3 wfm = fm.WaterPropertiesFineMillero(param[:, 0], param[:, 1]) test_vals = np.allclose(wfm.a_phi(), param[:, 2], 0, 1e-3) self.assertTrue(test_vals)
def test_dielectric_constant(self): # dielectric constant of water at [temperature(K), pressure(MPa), dielectric constant] param = np.array([[338.15, 0.1, 65.20777], [318.15, 0.1, 71.50373], [353.15, 0.1, 60.84250], [273.15, 1, 87.89296], [278.15, 60, 88.17593]]) # converting param to [temperature(K), pressure(atm), dielectric constant] param[:, 1] = param[:, 1] * 1e6 / un.atm_2_pascal(1) # testing dielectric constant up to a precision of 10^-5 wfm = fm.WaterPropertiesFineMillero(param[:, 0], param[:, 1]) test_vals = np.allclose(wfm.dielectric_constant(), param[:, 2], 0, 1e-5) self.assertTrue(test_vals)
def molar_vol_infinite_dilution(self): """ Partial molal volume of solute at infinite dilution according to Pitzer :cite:`Pitzer1973a` .. math:: :label: mol_infty_dilution \\upsilon_1^{\\infty}=\\frac{\\upsilon(m_r)}{m_r}-Y \\upsilon_0^{\\infty}-\\nu|z_{+}z_{-}|A_v h(I_r) -2\\nu_+\\nu_{-} RT\\left[ m_r B^{\\nu}_{\\pm}+\\nu_{+}z_{+}m^2_rC^{\\nu}_{\\pm}\\right] :return: Partial molal volume of solute at infinite dilution in SI (float) """ # the factor 10 is the conversion from J to bar cm^3 ct = 10 * un.r_gas() * self.tk # molar volume water in cm^3/mol vol_water = 1e6 * wp.WaterPropertiesFineMillero( self.tk, self.pa).molar_volume() # stoichiometric_coefficients nu, nu_prod, z_prod, nz_prod_plus = self.mat m_r = self.p_ref[1] y_r = self.p_ref[2] vp, beta0_der_p, beta1_der_p, beta2_der_p, C0_der_p, C1_der_p, C2_der_p, D0_der_p, D1_der_p, D2_der_p = self.params_der_p i_str = self.ionic_strength(m_r) mv_i_0 = vp / m_r - y_r * vol_water mv_i_1 = -nu * z_prod * wp.WaterPropertiesFineMillero( self.tk, self.pa).a_v() * self.h_fun(i_str) mv_i_2 = -2 * nu_prod * ct * (m_r * beta0_der_p + nz_prod_plus * m_r**2 * C0_der_p) # this is in cm^3/mol mv_i = mv_i_0 + mv_i_1 + mv_i_2 # return in SI m^3 molar_vol_infinite_dilution = 1e-6 * mv_i return molar_vol_infinite_dilution
def test_compressibility(self): # compressibility of water at [temperature(C), pressure(applied_bar), compressibility (10^6 bar-1)] Millero TABLE V param = np.array([[5, 0, 49.175], [30, 0, 44.771], [75, 0, 45.622], [35, 100, 43.305], [55, 300, 40.911]]) # converting param to [temperature(K), pressure(atm), compressibility (atm-1)] param[:, 0] = un.celsius_2_kelvin(param[:, 0]) # 1 atm = 1 applied_atm + 1, according to Millero param[:, 1] = (param[:, 1] / un.atm_2_bar(1)) + 1 param[:, 2] = (param[:, 2] / 1e6) * un.atm_2_bar(1) # testing compressibility up to a precision of 10^-9 wfm = fm.WaterPropertiesFineMillero(param[:, 0], param[:, 1]) test_vals = np.allclose(wfm.compressibility(), param[:, 2], 0, 1e-9) self.assertTrue(test_vals)
def test_density(self): # density of water at [temperature(C), pressure(applied_bar), specific volume] Millero TABLEIV param = np.array([[5, 0, 1.000036], [30, 0, 1.004369], [75, 0, 1.025805], [30, 100, 0.999939], [55, 300, 1.001642]]) # converting param to [temperature(K), pressure(atm), specific volume] param[:, 0] = un.celsius_2_kelvin(param[:, 0]) # 1 atm = 1 applied_atm + 1, according to Millero param[:, 1] = (param[:, 1] / un.atm_2_bar(1)) + 1 # testing density up to a precision of 10^-6 wfm = fm.WaterPropertiesFineMillero(param[:, 0], param[:, 1]) test_vals = np.allclose(1e3 / wfm.density(), param[:, 2], 0, 1e-6) self.assertTrue(test_vals)
def test_a_h(self): # enthalpy coefficient [temperature(C), Pressure(bar), enthalpy coeff (AH / RT)] Bradley & Pitzer TABLE III param = np.array([[10, 100, 0.641], [60, 100, 1.180], [120, 100, 2.05], [70, 400, 1.24], [25, 600, 0.736]]) # converting param to [temperature(K), pressure(atm), enthalpy coeff (AH / RT)] param[:, 0] = un.celsius_2_kelvin(param[:, 0]) param[:, 1] = param[:, 1] / un.atm_2_bar(1) # testing enthalpy coeff up to a precision of 10^-2 wfm = fm.WaterPropertiesFineMillero(param[:, 0], param[:, 1]) test_vals = np.allclose( ((2 / 3) * wfm.a_h()) / (un.r_gas() * param[:, 0]), param[:, 2], 0, 1e-2) self.assertTrue(test_vals, (wfm.a_h()) / (un.r_gas() * param[:, 0]))
def density_sol(self, m): """ Density of electrolyte solution according to Pitzer :cite:`Pitzer1973a` :return: Density of electrolyte solution in SI (float) """ mw = wp.WaterPropertiesFineMillero(self.tk, self.pa).MolecularWeight # convert to cm^3/mol v_water = 1e6 * wp.WaterPropertiesFineMillero(self.tk, self.pa).molar_volume() # convert to cm^3/mol v_salt = 1e6 * self.molar_vol(m) mw_salt = self.p_ref[0] # density in g/cm^3 dens = mw * (1 + 1e-3 * m * mw_salt) / (v_water + 1e-3 * m * mw * v_salt) # return density in kg/m3 density_sol = 1e3 * dens return density_sol
def apparent_molal_enthalpy(self, m): """ Apparent molal enthalpy according to Wang and Pitzer :cite:`Wang1998` .. math:: L_{\\phi} = \\frac{\\nu|z_{+}z_{-}|A_{H}}{2b} \\ln(1+bI^{\\frac{1}{2}}) \\cr -2RT^{2}(\\nu_{+} \\nu_{-})m \\left[\\frac{\\partial \\beta^{(0)}_{\\pm}}{\\partial T}+2\\frac{\\partial \\beta^{(1)}_{\\pm}}{\\partial T}g_{p1}(x)+2\\frac{\\partial \\beta^{(2)}_{\\pm}}{\\partial T} g_{p1}(x)\\right] \\cr -2RT^{2}(\\nu_{+}\\nu_{-})^{\\frac{3}{2}}m^{2}\\left[\\frac{\\partial C^{(0)}_{\\pm}}{\\partial T}+2\\frac{\\partial C^{(1)}_{\\pm}}{\\partial T}g_{p1}(x)+2\\frac{\\partial C^{(2)}_{\\pm}}{\\partial T}g_{p1}(x)\\right] \\cr -2RT^{2}(\\nu_{+}\\nu_{-})^{2}m^{3}\\left[ \\frac{\\partial D^{(0)}_{\\pm}}{\\partial T}+2\\frac{\\partial D^{(1)}_{\\pm}}{\\partial T}g_{p1}(x) +2\\frac{\\partial D^{(2)}_{\\pm}}{\\partial T}g_{p1}(x)\\right] functions are defined in Eq. :eq:`wang_function_1` :return: apparent molal enthalpy in SI (float) """ # stoichiometric_coefficients nu, nu_prod, z_prod, nz_prod_plus = self.mat # ionic strength i_str = self.ionic_strength(m) # Pitzer Parameters temperature derivative beta0_der_t, beta1_der_t, beta2_der_t, C0_der_t, C1_der_t, C2_der_t, D0_der_t, D1_der_t, D2_der_t = self.params_der_t BL = (beta0_der_t + 2 * beta1_der_t * self.g_fun_phi(self.alpha_b1, i_str**0.5) + 2 * beta2_der_t * self.g_fun_phi(self.alpha_b1, i_str**0.5)) CL = (C0_der_t + 2 * C1_der_t * self.g_fun_phi(self.alpha_c1, i_str) + 2 * C2_der_t * self.g_fun_phi(self.alpha_c2, i_str)) DL = (D0_der_t + 2 * D1_der_t * self.g_fun_phi(self.alpha_d1, i_str**1.5) + 2 * D2_der_t * self.g_fun_phi(self.alpha_d2, i_str**1.5)) a_h = wp.WaterPropertiesFineMillero(self.tk, self.pa).a_h() val_1 = nu * z_prod * ( a_h / (2 * self.b_param)) * np.log(1 + self.b_param * np.sqrt(i_str)) val_2 = -2 * un.r_gas() * self.tk**2 * nu_prod * m * BL val_3 = -2 * un.r_gas() * self.tk**2 * nu_prod**1.5 * m**2 * CL val_4 = -2 * un.r_gas() * self.tk**2 * nu_prod**2 * m**3 * DL l_phi = val_1 + val_2 + val_3 + val_4 return l_phi
def test_bjerrum_length(self): # parameters to test (water objects at param (K) temperatures) param = np.array([273.15, 298.15, 300, 310, 320]) obj_water_bp = wbp.WaterPropertiesFineMillero(param) obj_water_aw = waw.WaterPropertiesFineMillero(param) obj_bj = bj.Bjerrum(obj_water_bp) obj_bj_2 = bj.Bjerrum(obj_water_aw) # testing params up to a precision of 10^-2 bj_length_param = np.array( [6.95955102, 7.15043775, 7.16686699, 7.26135158, 7.36525138]) test_1 = np.allclose(obj_bj.bjerrum_length, bj_length_param, 0, 1e-2) test_2 = np.allclose(obj_bj_2.bjerrum_length, bj_length_param, 0, 1e-2) self.assertTrue(test_1) self.assertTrue(test_2)
def log_gamma(self, m): """ Activity coefficient according to Wang & Pitzer :cite:`Wang1998` .. math:: :label: pitzer_activity \\ln \\gamma_{\\pm} = -|z_{+}z_{-}|A_{\\gamma}f_{p2}(I) \\cr + \\frac{2(\\nu_{+}\\nu_{-})}{\\nu} m \\left[2\\beta^{(0)}_{\\pm} + 2\\beta^{(1)}_{\\pm} f_{p3}(\\alpha_{B1}, I) + 2\\beta^{(2)}_{\\pm}f_{p3}(\\alpha_{B2}, I)\\right]\\cr + \\frac{2(\\nu_{+}\\nu_{-})^{\\frac{3}{2}}}{\\nu} m^{2}\\left[3C^{(0)}_{\\pm} + 2C^{(1)}_{\\pm} f_{p4}(\\alpha_{C1}, I) + 2C^{(2)}_{\\pm}f_{p4}(\\alpha_{C2}, I)\\right] \\cr + \\frac{2(\\nu_{+}\\nu_{-})^{2}}{\\nu} m^{3}\\left[4D^{(0)}_{\\pm} + 2D^{(1)}_{\\pm} f_{p5}(\\alpha_{D1}, I) + 2D^{(2)}_{\\pm}f_{p5}(\\alpha_{D2}, I)\\right] functions are defined in Eq. :eq:`pitzer_function_2`, :eq:`pitzer_function_3`, :eq:`pitzer_function_4`, :eq:`pitzer_function_5` :return: activity coefficient (float) """ # pressure is 1 atm press = 1 # stoichiometric_coefficients nu, nu_prod, z_prod, nz_prod_plus = self.mat # ionic strength i_str = self.ionic_strength(m) # Pitzer Parameters beta0, beta1, beta2, C0, C1, C2, D0, D1, D2 = self.params BY = (2 * beta0 + 2 * beta1 * self.p_fun_gamma(self.alpha_b1, i_str) + 2 * beta2 * self.p_fun_gamma(self.alpha_b2, i_str)) CY = (3 * C0 + 2 * C1 * self.p_fun_gamma_2(self.alpha_c1, i_str) + 2 * C2 * self.p_fun_gamma_2(self.alpha_c2, i_str)) DY = (4 * D0 + 2 * D1 * self.p_fun_gamma_3(self.alpha_d1, i_str) + 2 * D2 * self.p_fun_gamma_3(self.alpha_d2, i_str)) val_1 = -z_prod * wp.WaterPropertiesFineMillero( self.tk, press).a_phi() * self.h_fun_gamma(i_str) val_2 = 2 * m * (nu_prod / nu) * BY val_3 = (2 * nu_prod**1.5 / nu) * m**2 * CY val_4 = (2 * nu_prod**2 / nu) * m**3 * DY log_gamma = val_1 + val_2 + val_3 + val_4 return log_gamma
def test_temp_star(self): # parameters in [temperature (K), radius (A), reduced temperature] param = np.array([[273.15, 2, 0.28737486], [298.15, 2, 0.27970316], [300, 2, 0.27906197], [298.15, 5, 0.69925789], [300, 0.5, 0.06976549], [300, 1, 0.13953098]]) obj_water_bp = wbp.WaterPropertiesFineMillero(param[:, 0]) obj_water_aw = waw.WaterPropertiesFineMillero(param[:, 0]) obj_bj = bj.Bjerrum(obj_water_bp) obj_bj_2 = bj.Bjerrum(obj_water_aw) # testing params up to a precision of 10^-3 test_1 = np.allclose(obj_bj.temp_star(param[:, 1]), param[:, 2], 0, 1e-3) test_2 = np.allclose(obj_bj_2.temp_star(param[:, 1]), param[:, 2], 0, 1e-3) self.assertTrue(test_1) self.assertTrue(test_2)
def osmotic_coeff(self, m): """ Osmotic coefficient according to Wang & Pitzer :cite:`Wang1998` .. math:: \\phi = 1 - |z_{+}z_{-}|A_{\\phi}\\frac{I^{\\frac{1}{2}}}{\\left(1 + bI^{\\frac{1}{2}}\\right)}\\cr +\\frac{2\\left(\\nu_{+}\\nu_{-}\\right)}{\\nu}m\\left[\\beta_{\\pm}^{(0)}+\\beta_{\\pm}^{(1)}e^{ -\\alpha_{B1}I^{\\frac{1}{2}}}+ \\beta_{\\pm}^{(2)}e^{-\\alpha_{B2}I^{\\frac{1}{2}}}\\right] \\cr +\\frac{2\\left(\\nu_{+}\\nu_{-}\\right)^{\\frac{3}{2}}}{\\nu}m^{2}\\left[2\\left[C_{\\pm}^{(0)}+ C_{\\pm}^{(1)}e^{-\\alpha_{C1}I}+ C_{\\pm}^{(2)}e^{-\\alpha_{C2}I}\\right]\\right] \\cr +\\frac{2\\left(\\nu_{+}\\nu_{-}\\right)^{2}}{\\nu}m^{3}\\left[3\\left[D_{\\pm}^{(0)}+D_{\\pm}^{(1)}e^{ -\\alpha_{D1}I^{\\frac{3}{2}}}+ D_{\\pm}^{(2)}e^{-\\alpha_{D2}I^{\\frac{3}{2}}}\\right]\\right] \\cr :return: osmotic coefficient in SI (float) """ # pressure is 1 atm press = 1 # stoichiometric_coefficients nu, nu_prod, z_prod, nz_prod_plus = self.mat # ionic strength i_str = self.ionic_strength(m) # Pitzer Parameters beta0, beta1, beta2, C0, C1, C2, D0, D1, D2 = self.params B = (beta0 + beta1 * np.exp(-self.alpha_b1 * i_str**0.5) + beta2 * np.exp(-self.alpha_b2 * i_str**0.5)) C = (2 * (C0 + C1 * np.exp(-self.alpha_c1 * i_str) + C2 * np.exp(-self.alpha_c2 * i_str))) D = (3 * (D0 + D1 * np.exp(-self.alpha_d1 * i_str**1.5) + D2 * np.exp(-self.alpha_d2 * i_str**1.5))) val_1 = -z_prod * wp.WaterPropertiesFineMillero( self.tk, press).a_phi() * np.sqrt(i_str) / ( 1 + self.b_param * np.sqrt(i_str)) val_2 = 2 * m * (nu_prod / nu) * B val_3 = (2 * nu_prod**1.5 / nu) * m**2 * C val_4 = (2 * nu_prod**2 / nu) * m**3 * D osmotic_coefficient = 1 + val_1 + val_2 + val_3 + val_4 return osmotic_coefficient
def test_bjerrum_constant(self): # parameters in [temperature (K), radius (A), Bjerrum constant] param = np.array([[273.15, 2, 177.15962555], [298.15, 2, 199.08793975], [300, 2, 201.07303174], [298.15, 5, 4.48179914], [300, 1, 5697.97522674]]) obj_water_bp = wbp.WaterPropertiesFineMillero(param[:, 0]) obj_water_aw = waw.WaterPropertiesFineMillero(param[:, 0]) obj_bj = bj.Bjerrum(obj_water_bp) obj_bj_2 = bj.Bjerrum(obj_water_aw) # testing params up to a precision of 10 test_1 = np.allclose(obj_bj.bjerrum_constant(param[:, 1]), param[:, 2], 0, 1e1) test_2 = np.allclose(obj_bj_2.bjerrum_constant(param[:, 1]), param[:, 2], 0, 1e1) self.assertTrue(test_1) self.assertTrue(test_2)
def test_bjerrum_constant_approx(self): # parameters in [temperature (K), radius (A), Bjerrum constant (approx)] param = np.array([[273.15, 2, 175.6241625002495], [298.15, 2, 197.37695820415016], [300, 2, 199.34627081662805], [298.15, 5, 4.437998539403452], [300, 1, 5670.345869397299]]) # testing params up to a precision of 10^2 for i in range(len(param)): obj_water_bp = wbp.WaterPropertiesFineMillero(param[i, 0]) obj_water_aw = waw.WaterPropertiesFineMillero(param[i, 0]) obj_bj = bj.Bjerrum(obj_water_bp).bjerrum_constant_approx(param[i, 1]) obj_bj_2 = bj.Bjerrum(obj_water_aw).bjerrum_constant_approx( param[i, 1]) test_1 = np.allclose(obj_bj[0], param[i, 2], 0, 1e2) test_2 = np.allclose(obj_bj_2[0], param[i, 2], 0, 1e2) self.assertTrue(test_1) self.assertTrue(test_2)
def __init__(self, tk, pa=1): """ constructor :param tk: temperature in kelvin :param pa: pressure in atmospheres :instantiate: temperature, pressure, stoichiometry coefficients, Pitzer Parameters """ self.tk = tk self.pa = pa # Calculations nacl parameters and coefficients self.mat_stoich = np.array([[1, 1], [1, -1]]) self.m_ref = 5.550825 self.y_ref = 10 self.m_weight = 58.4428 self.p_ref = np.array([self.m_weight, self.m_ref, self.y_ref]) # values for ion strength dependence and ion size constants in the extended Pitzer model self.alpha_b1 = 2.0 self.alpha_b2 = 0 self.alpha_c1 = 0 self.alpha_c2 = 0 self.alpha_d1 = 0 self.alpha_d2 = 0 self.b_param = 1.2 self.ion_param = np.array([ self.alpha_b1, self.alpha_b2, self.alpha_c1, self.alpha_c2, self.alpha_d1, self.alpha_d2, self.b_param ]) self.cm = np.zeros(28) self.cm[0] = 1.0249125e3 self.cm[1] = 2.7796679e-1 self.cm[2] = -3.0203919e-4 self.cm[3] = 1.4977178e-6 self.cm[4] = -7.2002329e-2 self.cm[5] = 3.1453130e-4 self.cm[6] = -5.9795994e-7 self.cm[7] = -6.6596010e-6 self.cm[8] = 3.0407621e-8 self.cm[9] = 5.3699517e-5 self.cm[10] = 2.2020163e-3 self.cm[11] = -2.6538013e-7 self.cm[12] = 8.6255554e-10 self.cm[13] = -2.6829310e-2 self.cm[14] = -1.1173488e-7 self.cm[15] = -2.6249802e-7 self.cm[16] = 3.4926500e-10 self.cm[17] = -8.3571924e-13 # exponent in 18 (19 in reference) is barely visible, it is 5 (confirmed) self.cm[18] = 3.0669940e-5 self.cm[19] = 1.9767979e-11 self.cm[20] = -1.9144105e-10 self.cm[21] = 3.1387857e-14 self.cm[22] = -9.6461948e-9 self.cm[23] = 2.2902837e-5 self.cm[24] = -4.3314252e-4 self.cm[25] = -9.0550901e-8 self.cm[26] = 8.6926600e-11 self.cm[27] = 5.1904777e-4 self.qm = np.zeros(19) self.qm[0] = 0.0765 self.qm[1] = -777.03 self.qm[2] = -4.4706 self.qm[3] = 0.008946 self.qm[4] = -3.3158e-6 self.qm[5] = 0.2664 # this value is not provided self.qm[6] = 0 # this value is not provided self.qm[7] = 0 self.qm[8] = 6.1608e-5 self.qm[9] = 1.0715e-6 self.qm[10] = 0.00127 self.qm[11] = 33.317 self.qm[12] = 0.09421 self.qm[13] = -4.655e-5 # this value is not provided self.qm[14] = 0 self.qm[15] = 41587.11 self.qm[16] = -315.90 self.qm[17] = 0.8514 self.qm[18] = -8.3637e-4 # Pitzer Parameters self.tc = 298.15 self.beta0_1 = self.qm[0] + self.qm[1] * ( 1 / self.tk - 1 / self.tc) + self.qm[2] * np.log(self.tk / self.tc) self.beta0_2 = self.qm[3] * (self.tk - self.tc) + self.qm[4] * ( self.tk**2 - self.tc**2) self.beta0 = self.beta0_1 + self.beta0_2 self.beta1 = self.qm[5] + self.qm[8] * ( self.tk - self.tc) + self.qm[9] * (self.tk**2 - self.tc**2) self.beta2 = 0 self.c_phi_1 = self.qm[10] + self.qm[11] * ( 1 / self.tk - 1 / self.tc) + self.qm[12] * np.log( self.tk / self.tc) self.c_phi = self.c_phi_1 + self.qm[13] * (self.tk - self.tc) self.C0 = self.c_phi / 2 self.C1 = 0 self.C2 = 0 self.D0 = 0 self.D1 = 0 self.D2 = 0 self.params = np.array([ self.beta0, self.beta1, self.beta2, self.C0, self.C1, self.C2, self.D0, self.D1, self.D2 ]) # Pitzer Parameters pressure derivative self.pr = un.atm_2_bar(self.pa) self.pr_atm = un.atm_2_bar(1) self.vp_0 = self.cm[0] + self.cm[1] * self.tk + self.cm[ 2] * self.tk**2 + self.cm[3] * self.tk**3 self.vp_1 = (self.pr - self.pr_atm) * ( self.cm[4] + self.cm[5] * self.tk + self.cm[6] * self.tk**2) self.vp_2 = (self.pr - self.pr_atm)**2 * (self.cm[7] + self.cm[8] * self.tk) self.vp_sum = self.vp_0 + self.vp_1 + self.vp_2 self.bp_0 = self.cm[9] + self.cm[10] / (self.tk - 227) + self.cm[ 11] * self.tk + self.cm[12] * self.tk**2 + self.cm[13] / (680 - self.tk) self.bp_1_1 = self.cm[14] + self.cm[15] / ( self.tk - 227) + self.cm[16] * self.tk + self.cm[17] * self.tk**2 self.bp_1 = (self.bp_1_1 + self.cm[18] / (680 - self.tk)) * (self.pr - self.pr_atm) self.bp_2_1 = self.cm[19] + self.cm[20] / ( self.tk - 227) + self.cm[21] * self.tk + self.cm[22] / (680 - self.tk) self.bp_2 = self.bp_2_1 * (self.pr - self.pr_atm)**2 self.beta0_der_p = self.bp_0 + self.bp_1 + self.bp_2 self.beta1_der_p = 0 self.beta2_der_p = 0 self.cq = self.cm[23] + self.cm[24] / (self.tk - 227) + self.cm[ 25] * self.tk + self.cm[26] * self.tk**2 + self.cm[27] / (680 - self.tk) self.cp = 0.5 * self.cq self.C0_der_p = self.cp self.C1_der_p = 0 self.C2_der_p = 0 self.D0_der_p = 0 self.D1_der_p = 0 self.D2_der_p = 0 # calculates the molar volume at infinite dilution (vp) # the factor 10 is the conversion from J to bar cm^3 self.ct = 10 * un.r_gas() * self.tk # molar volume water in cm^3/mol self.vol_water = 1e6 * bp.WaterPropertiesFineMillero( self.tk, self.pa).molar_volume() self.mv_i_0 = self.vp_sum / self.m_ref - self.y_ref * self.vol_water self.mv_i_1 = -2 * bp.WaterPropertiesFineMillero(self.tk, self.pa).a_v( ) * (0.5 * np.log(1 + self.b_param * np.sqrt(self.m_ref)) / self.b_param) self.mv_i_2 = -2 * self.ct * (self.m_ref * self.beta0_der_p + self.m_ref**2 * self.C0_der_p) # this is in cm^3/mol self.mv_i = self.mv_i_0 + self.mv_i_1 + self.mv_i_2 # return in SI m^3 self.molar_vol_infinite_dilution = 1e-6 * self.mv_i # infinite molar volume, convert to cm^3/mol self.vp = 1e6 * self.molar_vol_infinite_dilution self.params_der_p = np.array([ self.vp, self.beta0_der_p, self.beta1_der_p, self.beta2_der_p, self.C0_der_p, self.C1_der_p, self.C2_der_p, self.D0_der_p, self.D1_der_p, self.D2_der_p ]) # Pitzer Parameters temperature derivative self.beta0_der_t = 2 * self.qm[4] * self.tk + self.qm[ 2] / self.tk - self.qm[1] / (self.tk**2) + self.qm[3] self.beta1_der_t = 2 * self.qm[9] * self.tk + self.qm[8] self.beta2_der_t = 0 self.c_phi_der_t = self.qm[12] / self.tk - self.qm[11] / ( self.tk**2) + self.qm[13] self.C0_der_t = self.c_phi_der_t / 2 self.C1_der_t = 0 self.C2_der_t = 0 self.D0_der_t = 0 self.D1_der_t = 0 self.D2_der_t = 0 self.params_der_t = np.array([ self.beta0_der_t, self.beta1_der_t, self.beta2_der_t, self.C0_der_t, self.C1_der_t, self.C2_der_t, self.D0_der_t, self.D1_der_t, self.D2_der_t ]) super().__init__(tk, pa)
def __init__( self, param_poly, param_salt, temp, df_w, x_ini, p_ini, n_k, chi_p, chi_e, param_s): """ The constructor, with the following parameters :param param_poly: polymer parameters :math:`(\\phi_p, \\frac{\\upsilon_w} \ {\\upsilon_p}, \\Delta F_p)` :param param_salt: salt parameters (see definition below) :param temp: temprature in Kelvin :param df_w: free energy change upon formation of hydrogen bond \ in water (in :math:`k_BT` units) :param x_ini: fraction of polymer hydrogen bonds :param p_ini: fraction of water hydrogen bonds :param n_k: number of Kuhn lengths for the polymer :param chi_p: Flory Huggins parameter between water and polymer :param chi_e: Flory Huggins parameter between polymer and salt :param param_s: microscopic salt parameters \ :math:`(h_+, h_-, d_+, d_-, m_+, m_-, \\nu_+, \\nu_-, log(gamma), osm)` \ (number of water molecules \ forming the hydration shell, diameter, maximum number of water \ molecules that maybe bound to each ion, the number of ions per salt,\ activity coefficient, osmotic coefficient) the parameter param_salt is given by :math:`(m_s, \\frac{\\upsilon_w}{\\upsilon_+}, \ \\frac{\\upsilon_w}{\\upsilon_-}, \\Delta F_a, \\Delta F_b)` where :math:`m_s` is the concentration in mol/kg """ #concentration of salt (NaCl) # concentration in mol/kg self.conc_l = param_salt[0] # concentration in mol/L self.T = temp #salt_nacl = nacl.NaClPropertiesRogersPitzer(self.T) #param_saltolvent = obj_water_bp.molar_volume() #m_solvent = obj_water_bp.MolecularWeight #param_saltolute = salt_nacl.molar_vol(self.conc_l) #self.conc = con.molality_2_molarity(self.conc_l, #param_saltolvent, #param_saltolute, #m_solvent) obj_water_bp = wbp.WaterPropertiesFineMillero(self.T) # molecular volumes self.u_p = param_poly[1] self.u_a = param_salt[1] self.u_b = param_salt[2] self.u_s = 1 / (1 / self.u_a + 1 /self.u_b) v_w = obj_water_bp.molar_volume() # molar volume of water den = obj_water_bp.density() # density of water in SI unit from Ref[4] self.D_w = 1 / den / v_w #55.509 # mol/kg water # volume fractions self.phi_p = param_poly[0] self.param_salt = self.conc_l / self.u_s / self.D_w self.V_w = 1 self.V_all = (self.param_salt + self.V_w) / (1 - self.phi_p) self.phi_s = self.param_salt / self.V_all self.phi_w = self.V_w / self.V_all # polymer and polymer interaction parameters self.n = n_k self.chi_p = chi_p self.chi_e = chi_e # hydration numbers self.h_a = param_s[0] self.h_b = param_s[1] self.m_a = param_s[4] self.m_b = param_s[5] # number of ions per salt self.nu_a = param_s[6] self.nu_b = param_s[7] self.nu = self.nu_a + self.nu_a # fraction of hydration bonds (in water:x, in polymer:y) self.x = x_ini self.p = p_ini # relative free energies of water association self.df_w = df_w self.df_p = param_poly[2] self.df_a = param_salt[3] self.df_b = param_salt[4] self.i_size_a = param_s[2] self.i_size_b = param_s[3] self.i_size = 0.5 * (self.i_size_a + self.i_size_b) # fraction of water in each ion size self.f_a = self.h_a * self.nu_a * self.u_s * self.phi_s / self.phi_w self.f_b = self.h_b * self.nu_b * self.u_s * self.phi_s / self.phi_w # Derivative of parameters self.df_adp = self.f_a / self.phi_w self.df_ads = self.f_a * (1 / self.phi_s + 1 / self.phi_w)# self.df_bdp = self.f_b / self.phi_w self.df_bds = self.f_b * (1 / self.phi_s + 1 / self.phi_w) self.dxdp = - self.x / self.phi_p self.dxds = 0 self.dydp = self.p / self.phi_w self.dyds = self.p / self.phi_w self.gamma = param_s[8] self.osm = param_s[9] #S_para = nacl.NaClPropertiesRogersPitzer(tk=self.T, pa=1) #self.gamma = S_para.log_gamma(self.conc_l) #self.osm = S_para.osmotic_coeff(self.conc_l) self.A_a = self.nu_a * self.m_a * self.conc_l / self.D_w self.B_b = self.nu_b * self.m_b * self.conc_l / self.D_w
def test_potential_df(self): """ checks if chemical potentials add up give free energy """ num_pnts = 10 phi_val = np.linspace(1e-1, 0.8, num_pnts) potential_w = np.zeros_like(phi_val) potential_p = np.zeros_like(phi_val) potential_s = np.zeros_like(phi_val) f_com = np.zeros_like(phi_val) F_com = np.zeros_like(phi_val) v_p = np.array([0.4, 1 / 3, 10 / 3]) v_s = np.array([2, 1, 1, -100 / 3, -100 / 3]) temp = 298 df_w = 10 / 3 x_ini = 0.1 p_ini = 0.2 n_k = 100 chi_p = 0.5 chi_s = 0.4 #param_s = np.array([7, 7, 1, 1, 8, 8, 1, 1, 0., 0.]) param_s = np.array([7., 7., 1., 1., 8., 8., 1., 1., 0., 0.]) obj_water_bp = wbp.WaterPropertiesFineMillero(temp) v_w = obj_water_bp.molar_volume() # molar volume of water den = obj_water_bp.density() # density of water in SI unit from Ref[4] D_w = 1 / den / v_w #55.509 # mol/kg water for ind, phi_p in enumerate(phi_val): S_para = nacl.NaClPropertiesRogersPitzer(tk=temp, pa=1) param_s[8] = S_para.log_gamma(v_s[0]) param_s[9] = S_para.osmotic_coeff(v_s[0]) polymer_sol = Pss.PolymerSolutionSalts( np.array([phi_p, 1 / 3, 10 / 3]), v_s, temp, df_w, x_ini, p_ini, n_k, chi_p, chi_s, param_s) potential_w[ind] = polymer_sol.chem_potential_w_full() potential_p[ind] = polymer_sol.chem_potential_p_full() potential_s[ind] = polymer_sol.chem_potential_s_full() f_com[ind] = polymer_sol.free() # concentration in mols/litre conc = v_s[0] # molecular volumes u_p = v_p[1] u_a = v_s[1] u_b = v_s[2] u_s = 1 / (1 / u_a + 1 / u_b) # volume fractions V_s = conc / u_s / D_w V_w = 1 V_all = (V_s + V_w) / (1 - phi_p) phi_s = V_s / V_all phi_w = 1 - phi_s - phi_p F_com[ind] = (u_p / n_k * phi_p * potential_p[ind] + u_s * phi_s * potential_s[ind] + phi_w * potential_w[ind]) self.assertTrue(np.allclose(F_com, f_com, rtol=0.0, atol=1e-14))