% phtitration package % Copyright (c) 2026 Khaldi Mohammed Elhadi % Released under the MIT License \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{phtitration}[2026/08/16 v1.0.0 Accurate pH titration curves] \RequirePackage{tikz} \RequirePackage{pgfplots} \RequirePackage{xstring} \RequirePackage{etoolbox} \usetikzlibrary{calc, decorations.markings} \pgfplotsset{compat=1.18} % ========================================================= % Internal variables % ========================================================= \newcommand{\pht@type}{weak-acid} \newcommand{\pht@Ca}{0.1} \newcommand{\pht@Va}{20} \newcommand{\pht@Cb}{0.1} \newcommand{\pht@Vb}{20} \newcommand{\pht@pKa}{4.76} \newcommand{\pht@pKaone}{4.76} \newcommand{\pht@pKatwo}{9.24} \newcommand{\pht@pKathree}{12.0} \newcommand{\pht@pKb}{4.75} \newcommand{\pht@pKbone}{4.75} \newcommand{\pht@pKbtwo}{8.00} \newcommand{\pht@pKbthree}{11.00} % Tangent method variables \newcommand{\pht@tangent@slope}{0.15} \newcommand{\pht@tangent@distance}{1.5} \newcommand{\pht@tangent@length}{10} \newcommand{\pht@tangent@xshift}{-5} % Styling variables \newcommand{\pht@curve@color}{black} \newcommand{\pht@curve@style}{very thick} \newcommand{\pht@tangent@color}{black} \newcommand{\pht@tangent@style}{thin} \newcommand{\pht@eq@color}{black} \newcommand{\pht@eq@mark}{*} \newcommand{\pht@eq@size}{2pt} \newcommand{\pht@eq@label@pos}{above left} \newcommand{\pht@font@style}{\footnotesize} \newif\ifpht@equivalence \newif\ifpht@half \newif\ifpht@tangents \newif\ifpht@coordinates \pht@equivalencetrue \pht@halftrue \pht@tangentsfalse \pht@coordinatesfalse % ========================================================= % TikZ Styles for Tangent Method (Equality marks //) % ========================================================= \tikzset{ pht tick/.style={ decoration={ markings, mark=at position 0.5 with { \draw[solid, thick, \pht@tangent@color] (-2pt,-3pt) -- (1pt,3pt); \draw[solid, thick, \pht@tangent@color] (0pt,-3pt) -- (3pt,3pt); } }, postaction={decorate} } } % ========================================================= % Keys % ========================================================= \pgfkeys{ /pht/.is family, /pht, type/.store in=\pht@type, Ca/.store in=\pht@Ca, Va/.store in=\pht@Va, Cb/.store in=\pht@Cb, Vb/.store in=\pht@Vb, pKa/.store in=\pht@pKa, pKa1/.store in=\pht@pKaone, pKa2/.store in=\pht@pKatwo, pKa3/.store in=\pht@pKathree, pKb/.store in=\pht@pKb, pKb1/.store in=\pht@pKbone, pKb2/.store in=\pht@pKbtwo, pKb3/.store in=\pht@pKbthree, show-equivalence/.is if=pht@equivalence, show-equivalence=true, show-half-equivalence/.is if=pht@half, show-half-equivalence=false, show-tangents/.is if=pht@tangents, show-tangents=false, show-coordinates/.is if=pht@coordinates, show-coordinates=false, tangent-slope/.store in=\pht@tangent@slope, tangent-distance/.store in=\pht@tangent@distance, tangent-length/.store in=\pht@tangent@length, tangent-xshift/.store in=\pht@tangent@xshift, curve-color/.store in=\pht@curve@color, curve-style/.store in=\pht@curve@style, tangent-color/.store in=\pht@tangent@color, tangent-style/.store in=\pht@tangent@style, eq-color/.store in=\pht@eq@color, eq-mark/.store in=\pht@eq@mark, eq-size/.store in=\pht@eq@size, eq-label-pos/.store in=\pht@eq@label@pos, font-style/.store in=\pht@font@style, } % ========================================================= % Helper Macro: Draw Coordinates Text % ========================================================= \newcommand{\pht@label}[3][\pht@eq@label@pos]{% \ifpht@coordinates \node[#1, font=\pht@font@style, text=\pht@eq@color] at ({#2},{#3}) {$({\pgfmathprintnumber[fixed, precision=2]{#2}}, {\pgfmathprintnumber[fixed, precision=2]{#3}})$}; \fi } % ========================================================= % Helper Macro: Draw Tangent Method % ========================================================= \newcommand{\pht@draw@tangents}[3]{% \ifpht@tangents \pgfmathsetmacro{\m}{\pht@tangent@slope * #3} \pgfmathsetmacro{\dx}{\pht@tangent@length} \pgfmathsetmacro{\dy}{\pht@tangent@distance} \pgfmathsetmacro{\xshift}{\pht@tangent@xshift} \coordinate (T1A) at ({#1 - \dx}, {#2 - \dy - \m*\dx}); \coordinate (T1B) at ({#1 + \dx}, {#2 - \dy + \m*\dx}); \coordinate (T2A) at ({#1 - \dx}, {#2 + \dy - \m*\dx}); \coordinate (T2B) at ({#1 + \dx}, {#2 + \dy + \m*\dx}); \coordinate (TMA) at ({#1 - \dx}, {#2 - \m*\dx}); \coordinate (TMB) at ({#1 + \dx}, {#2 + \m*\dx}); \draw[\pht@tangent@style, \pht@tangent@color] (T1A) -- (T1B); \draw[\pht@tangent@style, \pht@tangent@color] (T2A) -- (T2B); \draw[\pht@tangent@style, \pht@tangent@color, dashed] (TMA) -- (TMB); \coordinate (P1) at ({#1 + \xshift}, {#2 - \dy + \m*\xshift}); \coordinate (PM) at ($(TMA)!(P1)!(TMB)$); \coordinate (P2) at ($(T2A)!(P1)!(T2B)$); \draw[\pht@tangent@style, \pht@tangent@color] (P1) -- (PM) [pht tick]; \draw[\pht@tangent@style, \pht@tangent@color] (PM) -- (P2) [pht tick]; \draw[dashed, \pht@tangent@style, gray] ({#1}, 0) node[below, font=\pht@font@style, text=\pht@eq@color] {$V_E$} -- ({#1}, {#2}) node[\pht@eq@label@pos, inner sep=2pt, font=\pht@font@style, text=\pht@eq@color] {$E$} -- (0, {#2}) node[left, font=\pht@font@style, text=\pht@eq@color] {$\text{pH}_E$}; \fi } % ========================================================= % Main command % ========================================================= \newcommand{\phtitration}[1][]{% \pgfkeys{/pht,#1}% % ----------------------------------------------------- % Strong acid + strong base % ----------------------------------------------------- \ifdefstring{\pht@type}{strong-acid}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Ca*\pht@Va/\pht@Cb } \addplot[\pht@curve@style, \pht@curve@color, samples=300, domain=0:\pht@Ve*2] ({x}, { x < \pht@Ve ? -log10(max(1e-14, (\pht@Ca*\pht@Va-\pht@Cb*x)/(\pht@Va+x))) : 14+log10(max(1e-14, (\pht@Cb*x-\pht@Ca*\pht@Va)/(\pht@Va+x))) }); \ifpht@equivalence \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates {({\pht@Ve},{7})}; \pht@label{\pht@Ve}{7} \fi \pht@draw@tangents{\pht@Ve}{7}{1} }{} % ----------------------------------------------------- % Weak monoprotic acid + strong base % ----------------------------------------------------- \ifdefstring{\pht@type}{weak-acid}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Ca*\pht@Va/\pht@Cb } \addplot[\pht@curve@style, \pht@curve@color, samples=400, domain=0:\pht@Ve*2] ({x}, { x < \pht@Ve ? ( x == 0 ? -log10(sqrt(10^(-\pht@pKa)*\pht@Ca)) : \pht@pKa+log10(max(1e-14, \pht@Cb*x/max(1e-14, \pht@Ca*\pht@Va-\pht@Cb*x))) ) : 14+log10(max(1e-14, (\pht@Cb*x-\pht@Ca*\pht@Va)/(\pht@Va+x))) }); \ifpht@half \pgfmathsetmacro{\pht@Vhalf}{\pht@Ve/2} \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates {({\pht@Vhalf},{\pht@pKa})}; \pht@label[below right]{\pht@Vhalf}{\pht@pKa} \ifpht@tangents \draw[dashed, \pht@tangent@style, gray] ({\pht@Vhalf}, 0) node[below, font=\pht@font@style, text=\pht@eq@color] {$\frac{V_E}{2}$} -- ({\pht@Vhalf}, {\pht@pKa}) -- (0, {\pht@pKa}) node[left, font=\pht@font@style, text=\pht@eq@color] {$\text{pH} = pK_a$}; \fi \fi \pgfmathsetmacro{\pht@Csalt}{\pht@Ca*\pht@Va/(\pht@Va+\pht@Ve)} \pgfmathsetmacro{\pht@pHeq}{7 + 0.5*\pht@pKa + 0.5*log10(\pht@Csalt)} \ifpht@equivalence \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates {({\pht@Ve},{\pht@pHeq})}; \pht@label{\pht@Ve}{\pht@pHeq} \fi \pht@draw@tangents{\pht@Ve}{\pht@pHeq}{1} }{} % ----------------------------------------------------- % Diprotic acid % ----------------------------------------------------- \ifdefstring{\pht@type}{diprotic-acid}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Ca*\pht@Va/\pht@Cb } \pgfmathsetmacro{\pht@VeTwo}{ 2*\pht@Ve } \pgfmathsetmacro{\pht@MaxVol}{\pht@VeTwo * 1.5} \addplot[\pht@curve@style, \pht@curve@color, samples=500, domain=0:14, variable=\ph, restrict x to domain=0:\pht@MaxVol] ( { \pht@Va * ( \pht@Ca * ( 10^(-\ph - \pht@pKaone) + 2 * 10^(-\pht@pKaone - \pht@pKatwo) ) / ( 10^(-2*\ph) + 10^(-\ph - \pht@pKaone) + 10^(-\pht@pKaone - \pht@pKatwo) ) - ( 10^(-\ph) - 10^(\ph - 14) ) ) / ( \pht@Cb + ( 10^(-\ph) - 10^(\ph - 14) ) ) }, { \ph } ); \ifpht@equivalence \pgfmathsetmacro{\pht@CsaltTwo}{\pht@Ca*\pht@Va/(\pht@Va+\pht@VeTwo)} \pgfmathsetmacro{\pht@pHeqTwo}{7 + 0.5*\pht@pKatwo + 0.5*log10(\pht@CsaltTwo)} \pgfmathsetmacro{\pht@pHone}{(\pht@pKaone+\pht@pKatwo)/2} \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates { ({\pht@Ve},{\pht@pHone}) ({\pht@VeTwo},{\pht@pHeqTwo}) }; \pht@label{\pht@Ve}{\pht@pHone} \pht@label{\pht@VeTwo}{\pht@pHeqTwo} \fi \pht@draw@tangents{\pht@VeTwo}{\pht@pHeqTwo}{1} }{} % ----------------------------------------------------- % Triprotic acid % ----------------------------------------------------- \ifdefstring{\pht@type}{triprotic-acid}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Ca*\pht@Va/\pht@Cb } \pgfmathsetmacro{\pht@VeTwo}{ 2*\pht@Ve } \pgfmathsetmacro{\pht@VeThree}{ 3*\pht@Ve } \pgfmathsetmacro{\pht@MaxVol}{\pht@VeThree * 1.3} \addplot[\pht@curve@style, \pht@curve@color, samples=600, domain=0:14, variable=\ph, restrict x to domain=0:\pht@MaxVol] ( { \pht@Va * ( \pht@Ca * ( 10^(-2*\ph - \pht@pKaone) + 2 * 10^(-\ph - \pht@pKaone - \pht@pKatwo) + 3 * 10^(-\pht@pKaone - \pht@pKatwo - \pht@pKathree) ) / ( 10^(-3*\ph) + 10^(-2*\ph - \pht@pKaone) + 10^(-\ph - \pht@pKaone - \pht@pKatwo) + 10^(-\pht@pKaone - \pht@pKatwo - \pht@pKathree) ) - ( 10^(-\ph) - 10^(\ph - 14) ) ) / ( \pht@Cb + ( 10^(-\ph) - 10^(\ph - 14) ) ) }, { \ph } ); \ifpht@equivalence \pgfmathsetmacro{\pht@CsaltThree}{\pht@Ca*\pht@Va/(\pht@Va+\pht@VeThree)} \pgfmathsetmacro{\pht@pHeqThree}{7 + 0.5*\pht@pKathree + 0.5*log10(\pht@CsaltThree)} \pgfmathsetmacro{\pht@pHone}{(\pht@pKaone+\pht@pKatwo)/2} \pgfmathsetmacro{\pht@pHtwo}{(\pht@pKatwo+\pht@pKathree)/2} \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates { ({\pht@Ve},{\pht@pHone}) ({\pht@VeTwo},{\pht@pHtwo}) ({\pht@VeThree},{\pht@pHeqThree}) }; \pht@label{\pht@Ve}{\pht@pHone} \pht@label{\pht@VeTwo}{\pht@pHtwo} \pht@label{\pht@VeThree}{\pht@pHeqThree} \fi \pht@draw@tangents{\pht@VeThree}{\pht@pHeqThree}{1} }{} % ----------------------------------------------------- % Strong base + strong acid % ----------------------------------------------------- \ifdefstring{\pht@type}{strong-base}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Cb*\pht@Vb/\pht@Ca } \addplot[\pht@curve@style, \pht@curve@color, samples=300, domain=0:\pht@Ve*2] ({x}, { x < \pht@Ve ? 14+log10(max(1e-14, (\pht@Cb*\pht@Vb-\pht@Ca*x)/(\pht@Vb+x))) : -log10(max(1e-14, (\pht@Ca*x-\pht@Cb*\pht@Vb)/(\pht@Vb+x))) }); \ifpht@equivalence \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates {({\pht@Ve},{7})}; \pht@label{\pht@Ve}{7} \fi \pht@draw@tangents{\pht@Ve}{7}{-1} }{} % ----------------------------------------------------- % Weak base + strong acid % ----------------------------------------------------- \ifdefstring{\pht@type}{weak-base}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Cb*\pht@Vb/\pht@Ca } \addplot[\pht@curve@style, \pht@curve@color, samples=400, domain=0:\pht@Ve*2] ({x}, { x < \pht@Ve ? 14-( \pht@pKb+log10(max(1e-14, \pht@Ca*x/max(1e-14, \pht@Cb*\pht@Vb-\pht@Ca*x))) ) : -log10(max(1e-14, (\pht@Ca*x-\pht@Cb*\pht@Vb)/(\pht@Vb+x))) }); \ifpht@half \pgfmathsetmacro{\pht@Vhalf}{\pht@Ve/2} \pgfmathsetmacro{\pht@pHhalf}{14-\pht@pKb} \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates {({\pht@Vhalf},{\pht@pHhalf})}; \pht@label[above right]{\pht@Vhalf}{\pht@pHhalf} \ifpht@tangents \draw[dashed, \pht@tangent@style, gray] ({\pht@Vhalf}, 0) node[below, font=\pht@font@style, text=\pht@eq@color] {$\frac{V_E}{2}$} -- ({\pht@Vhalf}, {\pht@pHhalf}) -- (0, {\pht@pHhalf}) node[left, font=\pht@font@style, text=\pht@eq@color] {$\text{pH} = 14-pK_b$}; \fi \fi \pgfmathsetmacro{\pht@Csalt}{\pht@Cb*\pht@Vb/(\pht@Vb+\pht@Ve)} \pgfmathsetmacro{\pht@pHeq}{7 - 0.5*\pht@pKb - 0.5*log10(\pht@Csalt)} \ifpht@equivalence \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates {({\pht@Ve},{\pht@pHeq})}; \pht@label{\pht@Ve}{\pht@pHeq} \fi \pht@draw@tangents{\pht@Ve}{\pht@pHeq}{-1} }{} % ----------------------------------------------------- % Dibase % ----------------------------------------------------- \ifdefstring{\pht@type}{dibase}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Cb*\pht@Vb/\pht@Ca } \pgfmathsetmacro{\pht@VeTwo}{ 2*\pht@Ve } \pgfmathsetmacro{\pht@MaxVol}{\pht@VeTwo * 1.5} \addplot[\pht@curve@style, \pht@curve@color, samples=500, domain=0:14, variable=\ph, restrict x to domain=0:\pht@MaxVol] ( { \pht@Vb * ( \pht@Cb * ( 10^(\ph - 14 - \pht@pKbone) + 2 * 10^(-\pht@pKbone - \pht@pKbtwo) ) / ( 10^(2*\ph - 28) + 10^(\ph - 14 - \pht@pKbone) + 10^(-\pht@pKbone - \pht@pKbtwo) ) + ( 10^(-\ph) - 10^(\ph - 14) ) ) / ( \pht@Ca - ( 10^(-\ph) - 10^(\ph - 14) ) ) }, { \ph } ); \ifpht@equivalence \pgfmathsetmacro{\pht@CsaltTwo}{\pht@Cb*\pht@Vb/(\pht@Vb+\pht@VeTwo)} \pgfmathsetmacro{\pht@pHeqTwo}{7 - 0.5*\pht@pKbtwo - 0.5*log10(\pht@CsaltTwo)} \pgfmathsetmacro{\pht@pHone}{14-(\pht@pKbone+\pht@pKbtwo)/2} \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates { ({\pht@Ve},{\pht@pHone}) ({\pht@VeTwo},{\pht@pHeqTwo}) }; \pht@label{\pht@Ve}{\pht@pHone} \pht@label{\pht@VeTwo}{\pht@pHeqTwo} \fi \pht@draw@tangents{\pht@VeTwo}{\pht@pHeqTwo}{-1} }{} % ----------------------------------------------------- % Tribase % ----------------------------------------------------- \ifdefstring{\pht@type}{tribase}{% \pgfmathsetmacro{\pht@Ve}{ \pht@Cb*\pht@Vb/\pht@Ca } \pgfmathsetmacro{\pht@VeTwo}{ 2*\pht@Ve } \pgfmathsetmacro{\pht@VeThree}{ 3*\pht@Ve } \pgfmathsetmacro{\pht@MaxVol}{\pht@VeThree * 1.3} \addplot[\pht@curve@style, \pht@curve@color, samples=600, domain=0:14, variable=\ph, restrict x to domain=0:\pht@MaxVol] ( { \pht@Vb * ( \pht@Cb * ( 10^(2*\ph - 28 - \pht@pKbone) + 2 * 10^(\ph - 14 - \pht@pKbone - \pht@pKbtwo) + 3 * 10^(-\pht@pKbone - \pht@pKbtwo - \pht@pKbthree) ) / ( 10^(3*\ph - 42) + 10^(2*\ph - 28 - \pht@pKbone) + 10^(\ph - 14 - \pht@pKbone - \pht@pKbtwo) + 10^(-\pht@pKbone - \pht@pKbtwo - \pht@pKbthree) ) + ( 10^(-\ph) - 10^(\ph - 14) ) ) / ( \pht@Ca - ( 10^(-\ph) - 10^(\ph - 14) ) ) }, { \ph } ); \ifpht@equivalence \pgfmathsetmacro{\pht@CsaltThree}{\pht@Cb*\pht@Vb/(\pht@Vb+\pht@VeThree)} \pgfmathsetmacro{\pht@pHeqThree}{7 - 0.5*\pht@pKbthree - 0.5*log10(\pht@CsaltThree)} \pgfmathsetmacro{\pht@pHone}{14-(\pht@pKbone+\pht@pKbtwo)/2} \pgfmathsetmacro{\pht@pHtwo}{14-(\pht@pKbtwo+\pht@pKbthree)/2} \addplot[only marks, mark=\pht@eq@mark, mark size=\pht@eq@size, \pht@eq@color] coordinates { ({\pht@Ve},{\pht@pHone}) ({\pht@VeTwo},{\pht@pHtwo}) ({\pht@VeThree},{\pht@pHeqThree}) }; \pht@label{\pht@Ve}{\pht@pHone} \pht@label{\pht@VeTwo}{\pht@pHtwo} \pht@label{\pht@VeThree}{\pht@pHeqThree} \fi \pht@draw@tangents{\pht@VeThree}{\pht@pHeqThree}{-1} }{} } \endinput