codextie-line-sensitivity-and-covariance.py
tie-line-sensitivity-and-covariance.py
"""Illustrate original binary lever-rule sensitivity and covariance examples."""
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
def phase_fraction(y, a, b, weight):
d = b - a
return d @ weight @ (y - a) / (d @ weight @ d)
# Independent finite-difference check of all residual-aware GLS derivatives.
a = np.array([0.60, 0.30])
b = np.array([0.20, 0.40])
y = np.array([0.470, 0.325])
weight = np.diag([1 / 0.010**2, 1 / 0.005**2])
d = b - a
q = d @ weight @ d
lam = phase_fraction(y, a, b, weight)
r = y - a - lam * d
g = d @ weight / q
h = r @ weight / q
expected = np.concatenate([g, -(1 - lam) * g - h, -lam * g + h])
x = np.concatenate([y, a, b])
numerical = []
for i in range(6):
perturb = np.zeros(6)
perturb[i] = 1e-6
lo, hi = x - perturb, x + perturb
numerical.append((phase_fraction(hi[:2], hi[2:4], hi[4:], weight)
- phase_fraction(lo[:2], lo[2:4], lo[4:], weight)) / 2e-6)
np.testing.assert_allclose(numerical, expected, rtol=1e-8, atol=1e-8)
offset = np.linspace(-0.006, 0.006, 401)
rho = np.linspace(-1, 1, 401)
uncertainty = 0.01 * np.sqrt(0.52 + 0.48 * rho)
np.testing.assert_allclose(uncertainty[[0, 200, -1]], [0.002, np.sqrt(0.52)*0.01, 0.01])
for m, c in [(1.0, 0.006), (1.03, -0.01), (0.9, 0.05)]:
assert abs(((m*.44+c)-(m*.2+c))/((m*.8+c)-(m*.2+c))-.4) < 1e-14
fig, axes = plt.subplots(1, 2, figsize=(9, 4), dpi=100, layout='constrained')
fig.set_facecolor('white')
for sep, color in [(0.60, '#1769aa'), (0.06, '#b24a20')]:
shifted = (0.4*sep - offset)/(sep - offset)
axes[0].plot(offset*1000, shifted, color=color, label=f'Endpoint separation {sep:.2f}')
axes[0].axhline(0.4, color='#777777', ls=':', lw=1)
axes[0].set(title='Closer endpoints amplify errors', xlabel='Error in endpoint a (×0.001 mole fraction)',
ylabel='Inferred phase fraction', ylim=(0.325, 0.465))
axes[0].legend(fontsize=8, loc='upper right')
axes[1].plot(rho, uncertainty, color='#1769aa')
axes[1].plot([-1, 0, 1], uncertainty[[0, 200, -1]], 'o', color='#1769aa', ms=4)
axes[1].set(title='Endpoint correlation matters', xlabel='Endpoint correlation coefficient',
ylabel='Standard uncertainty of phase fraction', ylim=(0, 0.012))
axes[1].text(0.04, 0.94, 'Overall composition exact\nEndpoint standard uncertainties: 0.006',
transform=axes[1].transAxes, va='top', fontsize=8)
for ax in axes:
ax.grid(alpha=0.2)
fig.savefig(Path.cwd()/'tie-line-sensitivity-and-covariance.png', facecolor='white')
plt.close(fig)