Guest User

Untitled

a guest
Dec 26th, 2024
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.29 KB | None | 0 0
  1. Below is a step-by-step outline showing how one can:
  2. 1. Derive (or at least set up) the apparent solubility vs. pH expression for a tetra-protic molecule (i.e., having 4 labile protons) in the presence of a strong base.
  3. 2. Incorporate known/unknown parameters (one neutral solubility, three solubility products for 1:1, 1:2, 1:3 salts, and four pKa values).
  4. 3. Write a Python program that uses these equations to fit experimental solubility vs. pH data, thus determining (or refining) the pKa’s, solubilities, and solubility-product constants.
  5.  
  6. 1. System Description
  7.  
  8. Let the compound be , having four acidic protons with known (or to-be-fitted) pKa values:
  9.  
  10. \[
  11. \begin{aligned}
  12. \mathrm{H_4A}
  13. &\xrightleftharpoons{\mathrm{p}K_{a1}} \mathrm{H_3A}^{-} + \mathrm{H}^+ \\
  14. \mathrm{H_3A}^{-}
  15. &\xrightleftharpoons{\mathrm{p}K_{a2}} \mathrm{H_2A}^{2-} + \mathrm{H}^+ \\
  16. \mathrm{H_2A}^{2-}
  17. &\xrightleftharpoons{\mathrm{p}K_{a3}} \mathrm{HA}^{3-} + \mathrm{H}^+ \\
  18. \mathrm{HA}^{3-}
  19. &\xrightleftharpoons{\mathrm{p}K_{a4}} \mathrm{A}^{4-} + \mathrm{H}^+
  20. \end{aligned}
  21. \]
  22.  
  23. We also assume:
  24. 1. Neutral solid:  with intrinsic solubility .
  25. 2. Salt forms precipitate with a strong base cation (, e.g. ). We consider three salt forms whose solubility products are known (or to be fitted):
  26. •  with 
  27. •  with 
  28. •  with 
  29.  
  30. (If there were a fully deprotonated salt  with a known , you could similarly incorporate it. In this problem, we assume either it is too soluble to precipitate or is out of the pH range of interest.)
  31.  
  32. 2. Equilibria and pH Dependence
  33.  
  34. 2.1. Dissociation Fractions
  35.  
  36. For a generic tetra-protic acid , in solution at a given pH (with ), the ratio of each species can be written in terms of the four pKa values. One often defines (in the absence of precipitation) the “alpha” fractions:
  37.  
  38.  
  39. Here,
  40. •  is the fraction of 
  41. •  is the fraction of 
  42. •  is the fraction of 
  43. •  is the fraction of 
  44. •  is the fraction of .
  45.  
  46. However, in real solubility calculations, once any solid phase precipitates, the solution concentrations of these species become constrained by whichever solid phase is controlling solubility at that pH.
  47.  
  48. 2.2. Phases That Can Precipitate
  49.  
  50. We have potentially four solids:
  51. 1. Neutral form , with an intrinsic solubility .
  52. • If this alone were controlling solubility, we would have:
  53. and the total dissolved concentration would be
  54. \[
  55. S_{\mathrm{neutral}}(H)
  56. \;=\; S_0 \bigl(\alpha_0 + \alpha_1 + \alpha_2 + \alpha_3 + \alpha_4 \bigr)
  57. \quad \text{(computed at equilibrium)}
  58. \]
  59. with the appropriate expression for the  ratios.
  60. 2. 1:1 salt  with .
  61. • If this salt is controlling solubility, then at equilibrium:
  62. Given the pH (thus known ) and the strong base  (some function of total added base, though in many models we approximate \([\mathrm{M^+}}] \approx\) constant or at least known from charge balance), one obtains . Then the other species  follow from the acid-base equilibria. Summing these yields
  63. 3. 1:2 salt  with .
  64. • At saturation:
  65. From that, again use the pKa equilibria to find  and get
  66. 4. 1:3 salt  with .
  67. • Similarly:
  68. Then get the total dissolved -species from all equilibria.
  69.  
  70. (If needed, one could also include the 1:4 salt, .)
  71.  
  72. 2.3. Overall Apparent Solubility
  73.  
  74. Because any one of these solid phases (neutral or salt) can “take over” as the limiting (lowest) solubility phase at a given pH, the actual or apparent solubility  at pH  pH is:
  75.  
  76. \[
  77. S_{\mathrm{app}}(\mathrm{pH})
  78. \;=\;
  79. \min\Bigl[
  80. S_{\mathrm{neutral}}(H),\;
  81. S_{\mathrm{1{:}1\,salt}}(H),\;
  82. S_{\mathrm{1{:}2\,salt}}(H),\;
  83. S_{\mathrm{1{:}3\,salt}}(H)
  84. \Bigr].
  85. \]
  86.  
  87. This leads to a piecewise curve across pH. In many practical situations:
  88. • At low pH, the neutral form  controls (lowest solubility).
  89. • At some intermediate pH, the 1:1 salt takes over.
  90. • Then possibly the 1:2 salt at even higher pH, etc.
  91.  
  92. Hence, plotting  vs. pH often results in multiple “legs” each controlled by a different phase.
  93.  
  94. 3. Fitting Experimental Data
  95.  
  96. Suppose you have experimental data , a set of pH values and measured total solubilities. Your unknowns might be:
  97. •  (intrinsic neutral solubility)
  98. • 
  99. • 
  100.  
  101. You can perform a non-linear least-squares fit of your theoretical curve
  102.  
  103.  
  104. to the observed data .
  105.  
  106. 3.1. Implementation Outline
  107. 1. Define a Python function app_solubility(pH, params) that:
  108. • Extracts parameters .
  109. • Computes .
  110. • For each possible controlling phase (neutral, 1:1 salt, 1:2 salt, 1:3 salt):
  111. 1. Compute the saturating concentration of that key species (e.g.,  for neutral, or , etc.).
  112. 2. Use the acid-base equilibrium relationships to convert from that “key species concentration” to total dissolved .
  113. • Take the minimum of those four total concentrations. That is your app_solubility at that pH.
  114. 2. Use scipy.optimize.curve_fit or another optimizer to find the best-fit parameters by minimizing the error between app_solubility(pH_i, params) and the experimental .
  115.  
  116. 4. Example Python Code
  117.  
  118. Below is a minimal illustrative code snippet using scipy.optimize.curve_fit. In a real scenario, you will need to:
  119. • Decide how you handle  (constant ionic strength? explicit charge balance?).
  120. • Possibly refine initial guesses for the parameters.
  121. • Add constraints so that all  and  remain positive, etc.
  122.  
  123. import numpy as np
  124. from scipy.optimize import curve_fit
  125.  
  126. def total_solubility_neutral(H, S0, pKa):
  127. """
  128. If neutral H4A(s) is controlling:
  129. - [H4A] = S0
  130. - Then [H3A-], [H2A2-], [HA3-], [A4-] come from acid-base equilibria
  131. Returns total [A] in solution.
  132. """
  133. # pKa = [pKa1, pKa2, pKa3, pKa4]
  134. pKa1, pKa2, pKa3, pKa4 = pKa
  135. # Stepwise dissociation constants in "concentration form"
  136. Ka1 = 10**(-pKa1)
  137. Ka2 = 10**(-pKa2)
  138. Ka3 = 10**(-pKa3)
  139. Ka4 = 10**(-pKa4)
  140.  
  141. # We know [H4A] = S0 at saturation
  142. H4A = S0
  143.  
  144. # Then H3A- / H4A = Ka1 / [H+]
  145. H3A_minus = H4A * (Ka1 / H)
  146. # H2A2- / H3A- = Ka2 / [H+], etc.
  147. H2A_2minus = H3A_minus * (Ka2 / H)
  148. HA_3minus = H2A_2minus * (Ka3 / H)
  149. A_4minus = HA_3minus * (Ka4 / H)
  150.  
  151. return H4A + H3A_minus + H2A_2minus + HA_3minus + A_4minus
  152.  
  153. def total_solubility_salt(H, Ksp, pKa, charge, M_plus):
  154. """
  155. Computes total [A] if a salt of form H_(4-charge)A^(charge-) • (M+)^(charge)
  156. is the controlling solid.
  157.  
  158. For example, if charge=1, we have H3A- controlling, so:
  159. [H3A-]*[M+] = Ksp => [H3A-] = Ksp / [M+].
  160. Then use acid-base equilibria to find total [A].
  161.  
  162. Input:
  163. - H = [H+]
  164. - Ksp = K_sp, e.g. Ksp1
  165. - pKa = list [pKa1, pKa2, pKa3, pKa4]
  166. - charge = 1,2,3,... corresponding to how many protons removed from H4A
  167. - M_plus = [M+] in solution (approx. constant or known)
  168. """
  169. pKa1, pKa2, pKa3, pKa4 = pKa
  170. Ka1 = 10**(-pKa1)
  171. Ka2 = 10**(-pKa2)
  172. Ka3 = 10**(-pKa3)
  173. Ka4 = 10**(-pKa4)
  174.  
  175. # The “key” species is H_(4-charge)A^(charge-).
  176. # We find that concentration from the solubility product:
  177. # [H_(4-charge)A^(charge-)] * [M+]^charge = Ksp
  178. # => [H_(4-charge)A^(charge-)] = Ksp / ([M+]^charge)
  179. key_conc = Ksp / (M_plus**charge)
  180.  
  181. # Now backtrack or forward-track to get all other species.
  182. # Example: if charge=1, key = [H3A-].
  183. # From [H3A-], we get [H4A], [H2A2-], [HA3-], [A4-].
  184.  
  185. if charge == 1:
  186. H3A_minus = key_conc
  187. # H4A
  188. H4A = H3A_minus * (H/Ka1)
  189. # H2A2-
  190. H2A_2minus = H3A_minus * (Ka2 / H)
  191. # HA3-
  192. HA_3minus = H2A_2minus * (Ka3 / H)
  193. # A4-
  194. A_4minus = HA_3minus * (Ka4 / H)
  195.  
  196. elif charge == 2:
  197. H2A_2minus = key_conc
  198. # H3A-
  199. H3A_minus = H2A_2minus * (H/Ka2)
  200. # H4A
  201. H4A = H3A_minus * (H/Ka1)
  202. # HA3-
  203. HA_3minus = H2A_2minus * (Ka3 / H)
  204. # A4minus
  205. A_4minus = HA_3minus * (Ka4 / H)
  206.  
  207. elif charge == 3:
  208. HA_3minus = key_conc
  209. # H2A2-
  210. H2A_2minus = HA_3minus * (H/Ka3)
  211. # H3A-
  212. H3A_minus = H2A_2minus * (H/Ka2)
  213. # H4A
  214. H4A = H3A_minus * (H/Ka1)
  215. # A4-
  216. A_4minus = HA_3minus * (Ka4 / H)
  217.  
  218. else:
  219. # If charge=0 or 4, etc., adapt similarly
  220. raise ValueError("charge not in {1,2,3} in this example.")
  221.  
  222. return H4A + H3A_minus + H2A_2minus + HA_3minus + A_4minus
  223.  
  224. def app_solubility(
  225. pH,
  226. S0,
  227. Ksp1, Ksp2, Ksp3,
  228. pKa1, pKa2, pKa3, pKa4,
  229. M_plus
  230. ):
  231. """
  232. Main function to return the apparent solubility at a given pH,
  233. given parameters.
  234. """
  235. # Convert pH -> [H+]
  236. H = 10**(-pH)
  237. pKa = [pKa1, pKa2, pKa3, pKa4]
  238.  
  239. # 1) Neutral-limited solubility
  240. S_neutral = total_solubility_neutral(H, S0, pKa)
  241.  
  242. # 2) 1:1 salt-limited
  243. S_1to1 = total_solubility_salt(H, Ksp1, pKa, charge=1, M_plus=M_plus)
  244.  
  245. # 3) 1:2 salt-limited
  246. S_1to2 = total_solubility_salt(H, Ksp2, pKa, charge=2, M_plus=M_plus)
  247.  
  248. # 4) 1:3 salt-limited
  249. S_1to3 = total_solubility_salt(H, Ksp3, pKa, charge=3, M_plus=M_plus)
  250.  
  251. # Actual solubility is the minimum across these possible precipitations
  252. return np.minimum.reduce([S_neutral, S_1to1, S_1to2, S_1to3])
  253.  
  254. def fit_solubility_curve(pH_data, S_data, initial_guess, M_plus_const):
  255. """
  256. Fits the (pH vs. S) data using scipy's curve_fit.
  257. :param pH_data: array-like of pH values
  258. :param S_data: array-like of experimentally measured solubilities
  259. :param initial_guess: tuple/list of initial guesses for
  260. (S0, Ksp1, Ksp2, Ksp3, pKa1, pKa2, pKa3, pKa4)
  261. :param M_plus_const: chosen [M+] for the fit (or you can incorporate it in parameters)
  262. """
  263.  
  264. # Define a "wrapper" so that curve_fit sees an array for x (pH) and returns model S
  265. def model_func(pH_array, S0, Ksp1, Ksp2, Ksp3, pKa1, pKa2, pKa3, pKa4):
  266. # Vectorized evaluation
  267. return np.array([
  268. app_solubility(
  269. pH_val,
  270. S0, Ksp1, Ksp2, Ksp3,
  271. pKa1, pKa2, pKa3, pKa4,
  272. M_plus_const
  273. )
  274. for pH_val in pH_array
  275. ])
  276.  
  277. popt, pcov = curve_fit(
  278. f=model_func,
  279. xdata=pH_data,
  280. ydata=S_data,
  281. p0=initial_guess
  282. )
  283. return popt, pcov
  284.  
  285. # ---------------------------------------------------------------------------
  286. # Example usage:
  287.  
  288. if __name__ == "__main__":
  289.  
  290. # Suppose we have some mock data:
  291. pH_exp = np.array([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
  292. S_exp = np.array([0.1, 0.12, 0.18, 0.25, 0.4, 1.2, 2.5]) # hypothetical
  293.  
  294. # We'll guess [M+] is near 0.1 M, for example:
  295. M_plus_guess = 0.1
  296.  
  297. # Initial guesses for parameters:
  298. # (S0, Ksp1, Ksp2, Ksp3, pKa1, pKa2, pKa3, pKa4)
  299. init_guess = (0.1, 1e-5, 1e-8, 1e-10, 3.0, 5.0, 7.0, 9.0)
  300.  
  301. # Fit
  302. popt, pcov = fit_solubility_curve(pH_exp, S_exp, init_guess, M_plus_guess)
  303.  
  304. print("Fitted parameters:")
  305. print("S0 =", popt[0])
  306. print("Ksp1 =", popt[1])
  307. print("Ksp2 =", popt[2])
  308. print("Ksp3 =", popt[3])
  309. print("pKa1 =", popt[4])
  310. print("pKa2 =", popt[5])
  311. print("pKa3 =", popt[6])
  312. print("pKa4 =", popt[7])
  313.  
  314. # Then you can plot or otherwise analyze:
  315. import matplotlib.pyplot as plt
  316.  
  317. pH_grid = np.linspace(2, 8, 100)
  318. S_fit = [app_solubility(
  319. pH_val,
  320. *popt,
  321. M_plus_guess
  322. ) for pH_val in pH_grid]
  323.  
  324. plt.plot(pH_grid, S_fit, label='Fitted curve')
  325. plt.scatter(pH_exp, S_exp, color='red', label='Experimental data')
  326. plt.yscale('log')
  327. plt.xlabel('pH')
  328. plt.ylabel('Solubility [mol/L] (log scale)')
  329. plt.legend()
  330. plt.show()
  331.  
  332. Notes on the Code and Approach
  333. 1. Piecewise Minima: The app_solubility function takes the minimum of the solubility limited by each potentially precipitating phase. In practice, one often sees “segment switching” in a solubility–pH plot.
  334. 2.  Dependence:
  335. • In real titration data,  may vary with pH. If you have a well-buffered system or a known dosing schedule, you can explicitly compute  from the mass balance of added base minus any precipitation. Or you might treat  as nearly constant at each pH step.
  336. 3. Bounds & Constraints: In real fitting, you should place constraints so pKa’s remain in ascending order and so that  and  are > 0. In scipy.optimize.curve_fit, you can specify bounds=([ ... ], [ ... ]) for lower and upper bounds.
  337.  
  338. 5. Summary
  339.  
  340. By:
  341. • Defining each possible solid-phase equilibrium condition,
  342. • Converting that to total dissolved species concentrations via the acid dissociation constants, and
  343. • Taking the minimum across all possible solids,
  344.  
  345. one obtains the apparent solubility vs. pH profile. Fitting that theoretical curve to experimental data (via non-linear least squares) allows one to refine or deduce the unknown parameters .
  346.  
  347. This general method extends to any polyprotic compound with multiple salts, as long as you include each relevant precipitating phase in the “competition.”
Add Comment
Please, Sign In to add comment