titrages
La courbe de titrage d'un bécher quelconque par une burette quelconque — pH-métrie,
conductimétrie, potentiométrie, complexométrie, précipitation —, avec la dilution et
sans aucune approximation, et les nombres qu'on y lit : pH à un volume donné,
équivalences, points d'inflexion, méthode des tangentes. Un paquet Python, et un
paquet LaTeX qui calcule la même chose dans pgfplots.
Sommaire
- Le but
- Les moyens
- Installation
- Une solution, un titrage
- Tracer
- Conductimétrie et potentiométrie
- Complexométrie et précipitation
- Sans tampon : le pH calculé
- La base de constantes
- En ligne de commande
- Le paquet LaTeX
- La documentation
- Antériorité
- Limites
- Développement
- Sources
- Licence
Le but
Une courbe de titrage se calcule d'ordinaire par morceaux — avant, à, après l'équivalence —, chaque morceau par sa réaction prépondérante et sa formule approchée. Cela tient pour un acide faible par la soude ; cela ne tient plus pour un polyacide aux pKa voisins, un mélange, un titrant faible, un titrage en retour, ni pour l'EDTA sans tampon, dont le pH s'effondre en cours de route. Résoudre numériquement le polynôme en h ne vaut pas mieux : ses coefficients couvrent des dizaines d'ordres de grandeur, et le saut, là où tout se joue, est son point le plus mal conditionné.
titrages donne la courbe exacte, et les nombres qu'on y mesure, pour toute
composition :
>>> from titrages import Titrage, solution
>>> t = Titrage(solution({"CH3COOH": 0.1}), solution({"NaOH": 0.1}), volume_becher=10)
>>> t.pH([0, 5, 10]).round(2).tolist()
[2.88, 4.76, 8.73]
>>> t.volumes_equivalents()
(10.0,)
>>> [(round(s.volume, 2), round(s.pH, 2)) for s in t.sauts(20)]
[(10.0, 8.73)]
Le paquet rend des nombres et des tableaux numpy : une courbe (volume, pH,
pente), des sauts, des équivalences, une construction des tangentes. Le tracé est
au choix du lecteur : matplotlib par titrages.trace, pgfplots par la liste de
coordonnées de vers_pgfplots, ou directement par le paquet LaTeX.
Les moyens
- À pH imposé, tout est explicite. Les fractions de chaque forme se calculent,
puis l'électroneutralité donne le volume versé comme une fonction directe du pH,
V(pH) = -V0 R_bécher(pH) / R_burette(pH): pas de polynôme à résoudre. C'est la méthode de Kalka (2021) et de Brière (2002), généralisée à un bécher et une burette quelconques ; la dilution y est exacte, chaque résidu gardant les concentrations de sa solution. - Le sens inverse par dichotomie. Le pH à un volume donné annule une fonction croissante : une racine unique, trouvée à la précision de la machine, sans réglage ni point de départ.
- Des calculs qui ne perdent pas de chiffres. Les fractions se calculent en
échelle logarithmique — l'EDTA à pH 14 ne déborde pas —, et la pente sans
soustraire deux nombres voisins. Un point d'inflexion est un minimum de
|dV/dpH|, affiné jusqu'à la précision de la machine : 10⁻¹⁰ unité de pH. - La même idée partout. Le potentiel remplace le pH en rédox ; le ligand libre en complexométrie ; l'ion commun en précipitation, où l'on essaie chaque ensemble de solides et garde le seul cohérent. Quand le pH n'est pas tamponné, deux dichotomies imbriquées trouvent l'équilibre, dont l'unicité vient de la convexité des bilans.
- Chaque nombre est vérifié. Les tests confrontent le paquet à des calculs indépendants en précision arbitraire (mpmath), qui diluent à la main, et les trois implémentations — Python, Lua, pgfmath — entre elles.
Installation
pip install titrages
pip install "titrages[trace]" # avec matplotlib, pour titrages.trace
Python 3.11 ou plus récent, et numpy 1.24 ou plus ; matplotlib 3.7 ou plus seulement pour les tracés. Le paquet est testé sous Python 3.11 à 3.14.
La version de développement s'installe depuis GitHub
(pip install git+https://github.com/antnardo/titrages). Chaque
release porte aussi la roue
titrages-<version>-py3-none-any.whl, pour un poste sans git, et le sdist, avec
docs/, examples/ et tests/. Sans pip ni réseau, le dossier src/titrages/ posé
à côté d'un script suffit : from titrages import Titrage, solution fonctionne
depuis le dossier du script.
Le paquet LaTeX demande une distribution TeX avec pgfplots ; LuaLaTeX pour tout ce
qui n'est pas une courbe acide-base, rédox ou un nombre simple (voir
Le paquet LaTeX).
Une solution, un titrage
Une solution se décrit par ce qu'on y dissout, en mol/L ; les ions spectateurs se déduisent du sel :
>>> s = solution({"Na2CO3": 0.1})
>>> round(s.pH(), 2)
11.65
>>> round(s.concentrations()["HCO3-"], 4)
0.0045
Un mélange par un titrant faible, et un titrage en retour — la soude en excès a déjà transformé l'acide éthanoïque en éthanoate, que l'acide chlorhydrique titre après l'excès :
>>> t = Titrage(solution({"HCl": 0.05, "CH3COOH": 0.05}), solution({"NH3": 0.1}), volume_becher=20)
>>> [round(s.volume, 1) for s in t.sauts(30)]
[10.0, 20.0]
>>> Titrage(solution({"CH3COOH": 0.05, "NaOH": 0.1}), solution({"HCl": 0.1}), 10).volumes_equivalents()
(5.0, 10.0)
Les équivalences stœchiométriques et les points d'inflexion sont deux choses : l'acide phosphorique a trois équivalences et deux sauts. La méthode des tangentes part d'un point avant le saut et rend la construction complète :
>>> t = Titrage(solution({"CH3COOH": 0.1}), solution({"NaOH": 0.1}), volume_becher=10)
>>> tg = t.tangentes(7, 20)
>>> round(tg.volume, 2), round(tg.pH, 2)
(10.0, 8.65)
Deux règles valent partout : un float pour un scalaire, un tableau numpy pour un
tableau (t.pH(5), t.pH([5, 10])) ; les volumes dans l'unité qu'on veut, la même
partout.
Tracer
import matplotlib.pyplot as plt
from titrages import Titrage, indicateur, solution
from titrages.trace import tracer_sauts, tracer_tangentes, tracer_titrage, tracer_zone_virage
t = Titrage(solution({"H3PO4": 0.1}), solution({"NaOH": 0.1}), volume_becher=10)
ax = tracer_titrage(t, 35, lw=2)
tracer_sauts(t, 35, ax)
tracer_tangentes(t, 7, 35, ax) # depuis 7 mL, avant le premier saut
tracer_zone_virage(indicateur("phénolphtaléine"), ax)
plt.show()
Le même tracé pour l'acide éthanoïque, par
examples/01_acide_faible.py :
Chaque fonction de tracé dessine sur l'axe qu'on lui donne et le rend : elle se
compose avec le reste d'un script. vers_pgfplots(t.courbe(20)) rend la même courbe
en coordonnées pgfplots.
Conductimétrie et potentiométrie
La conductimétrie suit la loi de Kohlrausch, ions spectateurs compris (le Na⁺ de la
soude), en S/m — l'unité que donnent λ° en mS·m²·mol⁻¹ et c en mol/L ; un
conductimètre affiche des mS/cm, dix fois moins, et unite="mS/cm" le fait pour un
tracé :
>>> t = Titrage(solution({"HCl": 0.01}), solution({"NaOH": 0.1}), volume_becher=100)
>>> round(t.conductivite(10), 4)
0.1149
>>> t.courbe_conductimetrique(20, 3, corrigee=True).conductivite.round(4).tolist()
[0.4261, 0.1264, 0.3751]
En potentiométrie, les électrons remplacent les protons. Les couples à plusieurs étapes, ceux qui échangent des protons (pris au pH du milieu, donné aux deux solutions) et les couples dimériques (I₂/I⁻, Cr₂O₇²⁻/Cr³⁺) s'y traitent ; le potentiel se lit contre une électrode de référence :
>>> from titrages import TitrageRedox, solution_redox
>>> r = TitrageRedox(solution_redox({"Fe2+": 0.1}), solution_redox({"KMnO4": 0.02}), 10, reference="ECS")
>>> r.volumes_equivalents(), round(r.E(10), 3)
((10.0,), 1.143)
>>> v = TitrageRedox(solution_redox({"V2+": 0.1}, pH=1), solution_redox({"KMnO4": 0.02}, pH=1), 10)
>>> v.volumes_equivalents()
(10.0, 20.0, 30.0)
Complexométrie et précipitation
Un métal titré par un ligand, à pH tamponné : la courbe pM = f(V), complexes successifs et métaux en compétition compris ; les équivalences se comptent comme celles d'un polyacide, une par étape de complexation.
>>> from titrages import SolutionComplexation, TitrageComplexometrique, complexe, ligand
>>> t = TitrageComplexometrique(
... SolutionComplexation({"Ca2+": 0.01}), SolutionComplexation(ligand=0.01), 20,
... [complexe("Ca2+", "EDTA")], ligand("EDTA"), pH=10,
... )
>>> round(t.pM(20), 2), [round(s.volume, 3) for s in t.sauts(40)], t.volumes_equivalents()
(6.28, [20.0], (20.0,))
Un titrage par précipitation suit l'ion commun, pAg = f(V). La courbe a des points anguleux là où un solide apparaît ou disparaît — des événements, distincts des sauts : dans la méthode de Mohr, le chromate d'argent apparaît juste après l'équivalence du chlorure.
>>> from titrages import SolutionPrecipitation, TitrageParPrecipitation, precipite
>>> m = TitrageParPrecipitation(
... SolutionPrecipitation(partenaires={"Cl-": 0.05, "CrO4^2-": 0.005}),
... SolutionPrecipitation(commun=0.05), 20,
... [precipite("AgCl"), precipite("Ag2CrO4")], pH=7.5,
... )
>>> [round(v, 9) for v in m.volumes_equivalents()], [(e.partenaire, round(e.volume, 3)) for e in m.evenements(30)]
([20.0, 24.0], [('Cl-', 0.0), ('CrO4^2-', 20.012)])
Sans tampon : le pH calculé
Sans pH=, et dès que le pH compte — un ligand ou un partenaire qui a des acidités,
un hydroxyde, une solution qui apporte de l'acide-base —, le pH se calcule à partir
du contenu des solutions, et un UserWarning le rappelle : un pH oublié change la
chimie. L'EDTA, pesé sous la forme Na₂H₂Y, libère ses protons en se fixant ; sans
tampon, le pH tombe et le saut disparaît :
>>> import warnings
>>> with warnings.catch_warnings():
... warnings.simplefilter("ignore")
... s = TitrageComplexometrique(
... SolutionComplexation({"Ca2+": 0.01}), SolutionComplexation(ligand=0.01, protons_ligand=2), 20,
... [complexe("Ca2+", "EDTA")], ligand("EDTA"),
... )
>>> round(float(s.pH_du_melange(20)), 2), s.sauts(40)
(3.54, ())
Un tampon se décrit alors comme une solution (un couple acide-base dans le bécher et
la burette), et un hydroxyde comme un précipité de partenaire "OH-", que précipite
une base forte.
La base de constantes
Les espèces se cherchent par ce qu'on écrit — "NH3", "ammoniac",
"carbonate de sodium", "CO3^2-" ; casse, accents et écriture des charges ne
comptent pas. La base porte 35 couples acide-base, 15 couples rédox, 9 indicateurs,
les conductivités molaires ioniques, 13 ligands, 92 complexes et 56 précipités, dont
15 hydroxydes et oxydes. Les complexes et les précipités viennent des tables de
D. Harvey, d'après Martell et Smith ; la source et les conditions de chaque valeur
sont dans les fichiers de données et dans le champ note.
En ligne de commande
titrages pH CH3COOH=0.1 CH3COO-=0.05
titrages sigma HCl=0.01 # en S/m
titrages E Fe2+=0.1 Fe3+=0.01
titrages titrage H3PO4=0.1 --burette NaOH=0.1 --volume 10
titrages redox V2+=0.1 --burette KMnO4=0.02 --reference ECS
titrages couples phosph
titrages indicateurs
Le paquet LaTeX
Le dossier latex/ contient
titrages.sty et titrages.lua, à poser ensemble dans un dossier que TeX trouve
(par exemple TEXMFHOME/tex/latex/titrages/) ou à côté du document ; chaque release
porte aussi titrages-ctan.zip, avec la documentation compilée.
\usepackage{titrages}
\pgfplotsset{compat=1.18}
\titragedefinir{ethanoique}{
volume = 10,
becher = { espece = { pKa = 4.76, C = 0.1 } },
burette = { base forte = 0.1 },
}
\begin{tikzpicture}
\begin{axis}[xlabel={$V$ (mL)}, ylabel=pH]
\addplottitrage[thick]{ethanoique}
\addplottitragetangentes[red, dashed]{ethanoique}{7}
\end{axis}
\end{tikzpicture}
Le pH à l'équivalence vaut \num{\titragepH{ethanoique}{10}}.
Sous LuaLaTeX, titrages.lua calcule tout : courbes pH, σ, E, pM et pP, dérivée,
sauts, équivalences, tangentes, événements, pH calculé, et des nombres développables
(\titragepH, \titragepM, \titragesaut…). Sous pdfLaTeX, un moteur de secours
trace les courbes acide-base et rédox par pgfmath (quatre à cinq chiffres, invisible
au tracé) et donne par l3fp le pH, le potentiel, la conductivité ou le volume à un
point donné, plus lentement ; ni équivalences, ni sauts, ni tangentes, ni couples
dimériques, ni complexométrie, ni précipitation. Le détail est dans
latex/README.md et
dans la documentation titrages-doc.pdf.
La documentation
- docs/DOC.md : l'API complète, la méthode et ses formules, les modules.
- examples/ : dix-sept
scripts, décrits avec leurs courbes dans la galerie
examples/README.md. titrages-doc.pdf, la documentation du paquet LaTeX (syntaxe, clés, messages, index), jointe à chaque release.- docs/technique/ :
le document technique,
titrages-technique.tex— tous les calculs, les choix algorithmiques, les démonstrations d'unicité, et les écarts mesurés face aux calculs indépendants (mpmath), queverifications.pyrecalcule. - CHANGELOG.md.
Pour partir d'une masse pesée, le paquet pychimie
donne la masse molaire ; titrages n'en dépend pas, le pont tient dans le script :
from pychimie import masse_molaire
from titrages import Titrage, solution
C = 0.384 / masse_molaire("NaHCO3") / 0.100 # 384 mg dans 100 mL, en mol/L
t = Titrage(solution({"NaHCO3": C}), solution({"HCl": 0.1}), volume_becher=50)
Antériorité
pHcalc et
chempy calculent l'équilibre d'une composition
donnée, chempy avec complexation et précipitation ; ni l'un ni l'autre n'a d'objet
titrage, de dilution exacte, de sauts, de tangentes, de conductimétrie ni de rédox.
Les logiciels de spéciation (PHREEQC, Visual MINTEQ) résolvent n'importe quel mélange,
sans courbe de titrage ni points d'inflexion. Côté LaTeX,
phtitration trace huit configurations de titrage
par un titrant fort, sans mélange, sans nombres, ni conductimétrie ni rédox ;
titrages part du même calcul paramétrique exact et le généralise à un bécher et une
burette quelconques.
Limites
- Les solutions sont idéales : l'activité est la concentration, les conductivités
molaires sont les valeurs limites, qui surestiment σ dès quelques mmol/L. La
température n'intervient pas, mais on peut changer les valeurs de
pKe, de la pente de Nernst et de toutes les autres constantes ; les valeurs par défaut sont celles de 25 °C. - Les couples rédox qui échangent des protons sont pris à pH fixé ; un métal ou un gaz (Ag, H₂) n'est pas un couple de solution.
- Un seul ligand par titrage : un tampon ammoniacal qui complexerait aussi le métal, ou un indicateur métallochrome, en serait un second. Complexation et précipitation ne se combinent pas dans un même titrage.
- Les constantes de la base sont celles des tables à 25 °C ; elles diffèrent d'une table à l'autre, et la base le signale quand l'écart est notable. Les λ° de dix-sept ions, non tabulés, sont approchés par un ion voisin, nommé ; ceux de l'EDTA manquent.
Développement
git clone https://github.com/antnardo/titrages
cd titrages && uv sync
uv run pytest # tout, moteur Lua et paquet LaTeX compris quand TeX est là
uv run pytest -m "not latex" # sans compiler le paquet LaTeX
uv run ruff check src tests && uv run ruff format --check src tests && uv run mypy
cd latex/tests && texlua test_titrages.lua
Les tests marqués lua comparent le moteur Lua au moteur Python par texlua ; ceux
marqués latex compilent les documents de latex/tests/ par lualatex et
pdflatex et comparent leurs nombres au calcul Python. Les deux groupes sont sautés
quand TeX manque.
Sources
- H. Kalka, Polyprotic Acids and Beyond — An Algebraic Approach, Chemistry 2021, 3, 454-508, doi:10.3390/chemistry3020034.
- T. Brière, Une méthode simple de résolution des problèmes de pH et de titrages acido-basiques sans approximations, BUP 841, 2002.
- Y. Cortial, Courbe de dosage acidobasique et méthode des tangentes, BUP 734, 1991.
- W. M. Haynes (dir.), CRC Handbook of Chemistry and Physics, 97e éd., CRC Press, 2016 : constantes d'acidité, potentiels standard, conductivités molaires ioniques.
- D. Harvey, Analytical Chemistry 2.1, LibreTexts, § 16.10 à 16.12, d'après A. E. Martell et R. M. Smith, Critical Stability Constants, Plenum, 1976 : constantes de formation et produits de solubilité.
Licence
Le code — le paquet Python (src/), le paquet LaTeX (latex/), les tests et les
scripts — est sous licence MIT (LICENSE). Les exemples (examples/), contenu
scientifique et pédagogique plutôt qu'algorithmes, sont sous licence CC BY-NC-SA 4.0
(LICENSE-CC-BY-NC-SA-4.0).
Metadata
Release files for titrages 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| titrages-0.2.0.tar.gz | 669.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| titrages-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 766.6 kB
Release files / titrages-0.2.0.tar.gz
| Download URL | titrages-0.2.0.tar.gz |
|---|---|
| Size | 669.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0cd6f02a46dc3cf5f17e61b6aacbebcc3c014486e1b915dc2ef61b86c6596e28
|
|
BLAKE2b-256 checksum How to use checksums |
d11349c866a6c7ea9b916dad7ed864cb6aca6b555d0ff945ebc917d29ba0b097
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / titrages-0.2.0-py3-none-any.whl
| Download URL | titrages-0.2.0-py3-none-any.whl |
|---|---|
| Size | 96.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7d8e6ca8d16831a90c0fdc37a8b1b371a7f40840311c013d553aef4ba3e18b20
|
|
BLAKE2b-256 checksum How to use checksums |
a05c873e01126d0783495e1707a0dd4e9d1dd9fef3b62e2904d6af557260128b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for titrages 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for titrages-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.