I wrote this script to calculate the optimal amount of brewing salts to add to your water profile to end up with a target profile. I grabbed most of my numbers from ProMash, and so far this seems to be working well for me.
It is a python script, so you'll need the python interpreter with numpy, and the PyIMSL numerical libraries (freely available here:
Code:
from numpy import *
from imsl.math.linLsqLinConstraints import linLsqLinConstraints
from imsl.math.writeMatrix import writeMatrix
from imsl.math.vectorNorm import vectorNorm
#Replace the values in base_water with your water profile
base_water = [26.0, 3.6, 13.2, 28.0, 12.2, 68]
#################Values taken from ProMash################
burton_on_trent = [268., 62., 30., 638., 36., 141.]
calistoga_mineral = [11.1, 62., 30., 638., 36., 141.]
distilled_water = [0.,0.,0.,0.,0.,0.]
dortmund = [260.,23.,69.,240.,206.,270.]
dublin = [118., 4., 12., 54., 19., 319.]
edinburgh = [140., 36., 92., 231., 60., 270.]
london = [90., 4., 24., 58., 18., 123.]
marin_county_ca = [12., 10., 15., 17., 13., 74.]
munich = [76., 18., 1., 10., 2., 152.]
pilsen = [7., 3., 3.2, 5.8, 5., 9.]
vienna = [200., 60., 8., 125., 12., 120.]
yorkshire = [105., 17., 23., 66., 30., 153.]
###########################################################
target = burton_on_trent
#Salt contents (PPM / (grams per gallon))
caso4 = [61.5, 0.0, 0.0, 147.4, 0.0, 0.0]
mgso4 = [0.0, 26.1, 0.0, 103.0, 0.0, 0.0]
nacl = [0.0, 0.0, 103.9, 0.0, 160.3, 0.0]
nahco3 = [0.0, 0.0, 72.0, 0.0, 0.0, 189.0]
cacl2 = [72.0, 0.0, 0.0, 0.0, 127.4, 0.0]
caco3 = [105.8, 0.0, 0.0, 0.0, 0.0, 158.4]
# The overall goal here is to find x ST:
# Ax =~ b
# x >= 0
a = transpose([caso4, mgso4, nacl, nahco3, cacl2, caco3])
b = array(target) - array(base_water)
xlb = [0.] * 6
xub = [-1.0e30] * 6
c = []
con_type = []
bu = []
bl = []
residual = []
x = linLsqLinConstraints(a, b, c, bl, bu, con_type, xlb, xub, residual=residual)
ion_labels = ['', 'Ca', 'Mg', 'Na', 'SO4', 'Cl', 'HCO3']
salt_labels = ['', 'CaSO4', 'MgSO4', 'NaCl', 'NaHCO3', 'CaCl2', 'CaCO3']
writeMatrix("Salt amounts to add \n(Grams per Gallon)", x, colLabels=salt_labels)
writeMatrix("Target Profile \n(PPM)", target, colLabels=ion_labels)
writeMatrix("Base Water + Recomended Salt Additions \n(PPM)", dot(a,x) + array(base_water), colLabels=ion_labels)
writeMatrix("Residuals \n(PPM)", residual, colLabels=ion_labels)
print ("\nNorm of residual = %f" % (vectorNorm(residual)))
Here's a run calculating additions from my water to Burton on Trent. Plugging these numbers back into ProMash I get these exact totals.