Le script aborde la modélisation de la mise en solution d'un acide dans de l'eau. Selon le cas, la détermination de la composition du système à l'état final se fait en considérant une ou deux réactions :
A la fin du script, sont proposés deux programmes automatisant :
Lorsque seule la réaction 1 est envisagée pour modéliser la mise en solution de l'acide dans l'eau, la recherche de l'avancement à l'état final s'effectue par résolution du polynôme issu de la relation de Guldberg-Waage. \begin{equation}\mathsf{ K_a=\frac{[A^-]_{eq} \cdot [H_3O^+]_{eq}}{[AH]_{eq}\cdot C°}=\frac{x^2}{(C-x)\cdot C°}} \end{equation}
En revanche, lorsque les deux réactions sont considérées, deux avancements doivent être introduits (donc deux inconnues) ce qui rend la recherche de l'état final plus difficile. Ce problème est contourné de manière à se ramener à la résolution d'une équation à une seule inconnue.
L'équation à résoudre est obtenue par l'écriture des deux relations de Guldberg-Waage ainsi que par une équation d'électroneutralité. \begin{equation}\mathsf{ K_a=\frac{[A^-]_{eq} \cdot [H_3O^+]_{eq}}{[AH]_{eq}\cdot C°} \\ K_e=\frac{[HO^-]_{eq} \cdot [H_3O^+]_{eq}}{(C°)^2} \\ [A^-]_{eq} + [HO^-]_{eq} = [H_3O^+]_{eq}} \end{equation}
Ainsi, en notant $\mathsf{h}$ la concentration des ions oxonium $\mathsf{[H_3O^+]_{eq}}$, l'équation à résoudre est : \begin{equation}\mathsf{ h^3 + K_a \cdot h^2 - (K_e + K_a \cdot C_0) \cdot h - K_a \cdot K_e = 0 } \end{equation}
La commande linspace
de la bibliothèque numpy
permet de générer une liste d'abscisses.
La bibliothèque matplolib
est utilisée pour tracer des graphiques.
Le module optimize
de la bibliothèque scipy
offre plusieurs fonctions pour rechercher les racines d'une fonction.
L'utilisateur peut modifier la valeur du $\mathsf{pK_a}$ du couple acide-base auquel appartient l'acide, ainsi que la concentration apportée, notée $\mathsf{C_0}$ de cet acide dans la solution.
#DETERMINATION DE LA COMPOSITION A L'ETAT FINAL
import numpy as np
import scipy.optimize as op
#Saisie des données relatives à l'acide introduit
pKa=5 # pKa du couple de l'acide
C0=1E-2 # concentration en mol/L
#Calcul des constantes d'équilibre
Ka=10**(-pKa)
Ke=10**(-14)
"""Modélisation par une seule réaction"""
#Equation à résoudre
def EFRP(h):
return h**2 - (C0-h)*Ka
#Recherche de la racine dans l'intervalle [0,CO]
hfRP=op.bisect(EFRP,0,C0)
#Caractérisation de l'état final : taux d'avancement et pH
tauRP=hfRP/C0
pHRP=-np.log10(hfRP)
"""Modélisation par deux réactions"""
#Equation à résoudre
def EF(h):
return h**3 + Ka * h**2 - (Ke+Ka*C0) * h - Ka*Ke
#Recherche de la racine
hf=op.bisect(EF,0,1)
#Caractérisation de l'état final : taux d'avancement et pH
tau=Ka/(hf+Ka)
pH=-np.log10(hf)
"""Affichage des resultats"""
print("Concentration C0 = ","{:.2e}".format(C0)," mol/L")
print("Avec une réaction : tau = ",round(tauRP,2)," et pH = ",round(pHRP,1))
print("Avec deux réactions : tau = ",round(tau,2)," et pH = ",round(pH,1))
Le script présenté ci-dessous reprend les fonctions définies dans le script précédent et automatise la recherche de l'état final pour différentes concentrations de l'acide (de $\mathsf{1\cdot10^{-1}}$ à $\mathsf{1\cdot10^{-8} mol\cdot L^{-1}}$).
Le taux d'avancement et le $\mathsf{pH}$ final sont fournis pour les deux modélisations (1 ou 2 réactions) en vue de comparer les résultats obtenus.
D'autre part, un graphique est généré pour faciliter l'identification des limites de validité d'une modélisation de la mise en solution d'un acide dans l'eau par une seule réaction.
Le code inclut des insctructions facultatives de mise en forme du tableau final qui peuvent sembler alourdir son appropriation. Ces instructions peuvent tout à fait être omises si l'utilisateur ne tient pas à avoir des colonnes alignées dans le tableau des résultats.
Il est possible de modifier la valeur du $\mathsf{pK_a}$ du couple auquel appartient l'acide mis en solution.
#DOMAINE DE VALIDITE D'UNE MODELISATION PAR UNE SEULE REACTION
import numpy as np
import scipy.optimize as op
import matplotlib.pyplot as plt
#Saisie du pKa du couple de l'acide étudié
pKa = 5
#Initialisation de listes de valeurs de pH calculées à l'état final
Liste_pHRP,Liste_pH=[],[]
#Initialisation d'un "tableau" des résultats
Result=[['{:^6}'.format('C'),'{:^6}'.format('tau2R'),'{:^6}'.format('tau1R'),'{:^6}'.format('pH2R'),'{:^6}'.format('pH1R')]]
#Boucle de recherche de l'état final en fonction de la concentration
Liste_pC = [.5*i for i in range(1,17)]
for i in Liste_pC :
def EF(h):
return h**3 + 10**-(pKa) * h**2 - (Ke+10**(-pKa)*C)* h - 10**(-pKa)*Ke
def EFRP(h):
return h**2 - (C-h)*10**(-pKa)
C=10**-i
hfRP=op.bisect(EFRP,0,C)
hf=op.bisect(EF,0,1)
Result.append(['{:.0e}'.format(C),'{:^6}'.format(round(10**-pKa/(hf+10**-pKa),2)),'{:^6}'.format(round(hfRP/C,2)),'{:^6}'.format((round(-np.log10(hf),1))),'{:^6}'.format(round(-np.log10(hfRP),1))])
Liste_pHRP.append(-np.log10(hfRP))
Liste_pH.append(-np.log10(hf))
#Procédure de tracé
plt.plot(Liste_pC,Liste_pHRP,'b--', label='Mod. 1 réaction')
plt.plot(Liste_pC,Liste_pH,'g--', label='Mod. 2 réactions')
plt.xlabel('pC')
plt.ylabel('pH')
plt.title(f"Mise en solution d'un acide dont le couple a un $pK_a$ = {pKa} \n pH calculé dans le cas d'une modélisation par 1 ou 2 réactions")
plt.legend()
plt.grid()
plt.show()
#Affichage d'un tableau de valeurs
print(f"pKa du couple de l'acide : pKa = {pKa}")
for i in [0,2,4,6,8,10,12,14,16]:
print(Result[i])
Quelle que soit la valeur du $\mathsf{pK_a}$ du couple auquel appartient l'acide mis en solution dans l'eau, la recherche de l'état final au moyen d'une seule réaction fournit des résultats robustes tant que le $\mathsf{pH}$ de la solution est inférieur à $\mathsf{6,5}$. La prise en compte de l'autoprotolyse de l'eau n'est nécessaire que lorsque le $pH$ calculé est supérieur à $\mathsf{6,5}$.
Le script recherche l'état final pour différentes valeurs de $\mathsf{pK_a}$ et différentes valeurs de concentrations apportées $\mathsf{C_0}$ d'un acide dans l'eau.
L'un des enjeux est de montrer que pour des transformations modélisées par une même réaction (associée par conséquent à une même valeur de constante thermodynamique d'équilibre), le taux d'avancement final est directement liée aux conditions expérimentales (ici, la concentration d'acide apporté).
Il est possible de modifier la valeur du $\mathsf{pK_a}$ du couple auquel appartient l'acide mis en solution.
#INFLUENCE DU pKa DU COUPLE DE L'ACIDE ET DE LA CONCENTRATION APPORTEE SUR LE TAUX DE DISSOCIATION D'UN ACIDE DANS L'EAU
import numpy as np
import scipy.optimize as op
import matplotlib.pyplot as plt
#Liste des valeurs de concentrations et de pKa testées
Liste_pC=[.25*i for i in range(1,30)]
Liste_pKa=[-2,0,2,4,6,8]
#Initialisation d'une liste des taux d'avancement calculés
Liste_tau=[]
#Boucle de recherche de l'état final
for pKa in Liste_pKa :
L=[]
for pC in Liste_pC:
def EF(h):
return h**3 + 10**-(pKa) * h**2 - (Ke+10**(-pKa)*10**-pC)* h - 10**(-pKa)*Ke
hf=op.bisect(EF,0,1)
L.append(10**-pKa/(hf+10**-pKa))
Liste_tau.append(L)
#Procédure de tracé
plt.plot(Liste_pC,Liste_tau[0],'b--', label=(f"pKa = {Liste_pKa[0]}"))
plt.plot(Liste_pC,Liste_tau[1],'g--', label=(f"pKa = {Liste_pKa[1]}"))
plt.plot(Liste_pC,Liste_tau[2],'y--', label=(f"pKa = {Liste_pKa[2]}"))
plt.plot(Liste_pC,Liste_tau[3],'k--', label=(f"pKa = {Liste_pKa[3]}"))
plt.plot(Liste_pC,Liste_tau[4],'r--', label=(f"pKa = {Liste_pKa[4]}"))
plt.plot(Liste_pC,Liste_tau[5],'m--', label=(f"pKa = {Liste_pKa[5]}"))
plt.xlabel('pC')
plt.ylabel("Taux d'avancement")
plt.title("Taux de dissociation en fonction du $pK_a$ et de la concentration $C_0$")
plt.legend()
plt.grid()
plt.show()
Pour les acides appartenant à des couples de $\mathsf{pK_a}$ négatif, le taux de dissociation de l'acide est pratiquement toujours de 100%. Ces acides sont qualifiés d'acides forts.