# HG changeset patch # User Michiel Broek # Date 1588326984 -7200 # Node ID 4bb005694ce73bf6f018cf7e41d6ec6ec1281a12 # Parent 74b442eae07b86452d7069262a58c251b4c05985 Version 0.3.32 Inventory water and profile water edit screens updated and added ion balance fields. Moved acid specs to global.js. In prod_edit and rec_edit changed the water calculations, eliminated the double percentage calculation. The pH values in the water tab have now 2 decimal digits. The mash pH field is only shown in auto calculate mode. The calculated acid addition results are now a bit better and compare with several famous spreadsheets. diff -r 74b442eae07b -r 4bb005694ce7 config.status --- a/config.status Sun Apr 26 10:36:38 2020 +0200 +++ b/config.status Fri May 01 11:56:24 2020 +0200 @@ -621,7 +621,7 @@ S["CC"]="gcc" S["CYEARS"]="2016-2020" S["COPYRIGHT"]="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved" -S["VERSION"]="0.3.31" +S["VERSION"]="0.3.32" S["PACKAGE"]="bms" S["SUBDIRS"]="bmsd doc script tools www" S["target_alias"]="" @@ -710,7 +710,7 @@ D["PACKAGE_STRING"]=" \"\"" D["PACKAGE_BUGREPORT"]=" \"\"" D["PACKAGE_URL"]=" \"\"" -D["VERSION"]=" \"0.3.31\"" +D["VERSION"]=" \"0.3.32\"" D["COPYRIGHT"]=" \"Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved\"" D["STDC_HEADERS"]=" 1" D["HAVE_SYS_TYPES_H"]=" 1" diff -r 74b442eae07b -r 4bb005694ce7 configure --- a/configure Sun Apr 26 10:36:38 2020 +0200 +++ b/configure Fri May 01 11:56:24 2020 +0200 @@ -2043,7 +2043,7 @@ PACKAGE="bms" -VERSION="0.3.31" +VERSION="0.3.32" COPYRIGHT="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved" CYEARS="2016-2020" diff -r 74b442eae07b -r 4bb005694ce7 configure.ac --- a/configure.ac Sun Apr 26 10:36:38 2020 +0200 +++ b/configure.ac Fri May 01 11:56:24 2020 +0200 @@ -8,7 +8,7 @@ dnl General settings dnl After changeing the version number, run autoconf! PACKAGE="bms" -VERSION="0.3.31" +VERSION="0.3.32" COPYRIGHT="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved" CYEARS="2016-2020" AC_SUBST(PACKAGE) diff -r 74b442eae07b -r 4bb005694ce7 www/inv_waters.php --- a/www/inv_waters.php Sun Apr 26 10:36:38 2020 +0200 +++ b/www/inv_waters.php Fri May 01 11:56:24 2020 +0200 @@ -26,28 +26,37 @@   - Calcium (Ca) mg/l: + Calcium (Ca) mg/L:
- Chloride (Cl) mg/l: + Sulfaat (CaSO4) mg/L: +
+ + + Magnesium (Mg) mg/L: +
+ Chloride (Cl) mg/L:
- Magnesium (Mg) mg/l: -
- Sulfaat (CaSO4) mg/l: -
+ Natrium (Na) mg/L: +
+ Bicarbonaat (HCO3) mg/L: +
- Natrium (Na) mg/l: -
- Bicarbonaat (HCO3) mg/l: -
+ + + Totale alkaliteit (CaCO3) mg/L: +
+ + +   Zuurgraad (pH):
- Totale alkaliteit (CaCO3) mg/l: -
+ Ionen balans meq/L: +
  @@ -63,10 +72,10 @@
- + - + diff -r 74b442eae07b -r 4bb005694ce7 www/js/global.js --- a/www/js/global.js Sun Apr 26 10:36:38 2020 +0200 +++ b/www/js/global.js Fri May 01 11:56:24 2020 +0200 @@ -370,10 +370,11 @@ AerationTypeAdapter = new $.jqx.dataAdapter(AerationTypeSource), AcidTypeData = [ - { id: 0, en: 'Lactic', nl: 'Melkzuur' }, - { id: 1, en: 'Hydrochloric', nl: 'Zoutzuur' }, - { id: 2, en: 'Phosphoric', nl: 'Fosforzuur' }, - { id: 3, en: 'Sulfuric', nl: 'Zwavelzuur' } + /* Note: AcidSG is for 100% use. AcidPrc is the default setting. */ + { id: 0, en: 'Lactic', nl: 'Melkzuur', pK1: 3.86, pK2: 20, pK3: 20, MolWt: 90.08, AcidSG: 1238, AcidPrc: 80 }, + { id: 1, en: 'Hydrochloric', nl: 'Zoutzuur', pK1: -7, pK2: 20, pK3: 20, MolWt: 36.46, AcidSG: 1497, AcidPrc: 28 }, + { id: 2, en: 'Phosphoric', nl: 'Fosforzuur', pK1: 2.12, pK2: 7.20, pK3: 12.44, MolWt: 98.00, AcidSG: 1765, AcidPrc: 25 }, + { id: 3, en: 'Sulfuric', nl: 'Zwavelzuur', pK1: -1, pK2: 1.92, pK3: 20, MolWt: 98.07, AcidSG: 1884, AcidPrc: 93 } ], AcidTypeSource = { localdata: AcidTypeData, diff -r 74b442eae07b -r 4bb005694ce7 www/js/inv_waters.js --- a/www/js/inv_waters.js Sun Apr 26 10:36:38 2020 +0200 +++ b/www/js/inv_waters.js Fri May 01 11:56:24 2020 +0200 @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (C) 2014-2019 + * Copyright (C) 2014-2020 * * Michiel Broek * @@ -56,6 +56,7 @@ $('#magnesium').jqxTooltip({ content: 'Magnesium (Mg).' }); $('#ph').jqxTooltip({ content: 'De zuurgraad (pH).' }); $('#total_alkalinity').jqxTooltip({ content: 'Totale alkaliniteit. Berekend meteen de Bicarbonaat.' }); + $('#balance').jqxTooltip({ content: 'De ionen balans van het water. Ideaal minder dan 0.1 verschil tussen kationen en anionen. Meer dan 0.5 is een fout in het waterraport.' }); $('#inventory').jqxTooltip({ content: 'Voorraad in liters.' }); $('#cost').jqxTooltip({ content: 'Kostprijs per liter. 5 cijfers achter de comma zodat het kraanwater er ook in kan.' }); @@ -151,6 +152,7 @@ $('#magnesium').jqxNumberInput(Spin1dec); $('#ph').jqxNumberInput(Spin2pH); $('#total_alkalinity').jqxNumberInput(Spin1dec); + $('#balance').jqxNumberInput(Show2dec); $('#inventory').jqxNumberInput(Spin1dec); $('#cost').jqxNumberInput({inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 5, spinButtons: true }); @@ -231,19 +233,57 @@ $('#inventory').val(dataRecord.inventory); $('#cost').val(dataRecord.cost); // show the popup window. + calcBalance(); $('#popupWindow').jqxWindow('open'); } } ] }); + function calcBalance() { + var cations = (dataRecord.calcium / 20.039) + (dataRecord.magnesium / 12.1525) + (dataRecord.sodium / 22.989); + var anions = (dataRecord.bicarbonate / 61.016) + (dataRecord.sulfate / 48.031) + (dataRecord.chloride / 35.4527); + var balance = Round(cations - anions, 2); + $('#balance').val(balance); + if (balance <= 0.1 && balance >= -0.1) + $('#wr_balance').html(""); + else if (balance <= 0.5 && balance >= -0.5) + $('#wr_balance').html(""); + else + $('#wr_balance').html(""); + } + + $('#calcium').on('change', function(event) { + dataRecord.calcium = parseFloat(event.args.value); + calcBalance(); + }); + $('#magnesium').on('change', function(event) { + dataRecord.magnesium = parseFloat(event.args.value); + calcBalance(); + }); + $('#sodium').on('change', function(event) { + dataRecord.sodium = parseFloat(event.args.value); + calcBalance(); + }); $('#total_alkalinity').on('change', function(event) { + dataRecord.total_alkalinity = parseFloat(event.args.value); dataRecord.bicarbonate = parseFloat(event.args.value) * 1.22; $('#bicarbonate').val(dataRecord.bicarbonate); + calcBalance(); }); $('#bicarbonate').on('change', function(event) { + dataRecord.bicarbonate = parseFloat(event.args.value); dataRecord.total_alkalinity = parseFloat(event.args.value) * 50 / 61; $('#total_alkalinity').val(dataRecord.total_alkalinity); + calcBalance(); + }); + $('#sulfate').on('change', function(event) { + dataRecord.sulfate = parseFloat(event.args.value); + calcBalance(); + }); + $('#chloride').on('change', function(event) { + dataRecord.chloride = parseFloat(event.args.value); + calcBalance(); }); // initialize the popup window and buttons. diff -r 74b442eae07b -r 4bb005694ce7 www/js/prod_edit.js --- a/www/js/prod_edit.js Sun Apr 26 10:36:38 2020 +0200 +++ b/www/js/prod_edit.js Fri May 01 11:56:24 2020 +0200 @@ -1064,25 +1064,25 @@ case 'Melkzuur': $('#wa_acid_name').val(0); $('#wa_acid').val(row.m_amount * 1000); - $('#wa_acid_perc').val(80); + $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc); // TODO: this ignores changed percentages. last_acid = 'Melkzuur'; break; case 'Zoutzuur': $('#wa_acid_name').val(1); $('#wa_acid').val(row.m_amount * 1000); - $('#wa_acid_perc').val(80); + $('#wa_acid_perc').val(AcidTypeData[1].AcidPrc); last_acid = 'Zoutzuur'; break; case 'Fosforzuur': $('#wa_acid_name').val(2); $('#wa_acid').val(row.m_amount * 1000); - $('#wa_acid_perc').val(80); + $('#wa_acid_perc').val(AcidTypeData[2].AcidPrc); last_acid = 'Fosforzuur'; break; case 'Zwavelzuur': $('#wa_acid_name').val(3); $('#wa_acid').val(row.m_amount * 1000); - $('#wa_acid_perc').val(80); + $('#wa_acid_perc').val(AcidTypeData[3].AcidPrc); last_acid = 'Zwavelzuur'; break; case 'NaHCO3': @@ -2794,22 +2794,13 @@ return Z - (Calc / 3.5 + Magn / 7); } - function ProtonDeficit(pHZ) { - - var rows, i, C1, ebc, x, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal')); - // proton deficit for the grist - if ((rows = $('#fermentableGrid').jqxGrid('getrows'))) { - for (i = 0; i < rows.length; i++) { - row = rows[i]; - if (row.f_added == 0 && row.f_graintype != 6) { // Added == Mash && graintype != No Malt - // Check if acid is required - C1 = 0; - if ((row.f_di_ph != 5.7) && ((row.f_acid_to_ph_57 < - 0.1) || (row.f_acid_to_ph_57 > 0.1))) { - C1 = row.f_acid_to_ph_57 / (row.f_di_ph - 5.7); - } else { + function BufferCapacity(di_ph, acid_to_ph_57, ebc, graintype) { + C1 = 0; + if ((di_ph != 5.7) && ((acid_to_ph_57 < - 0.1) || (acid_to_ph_57 > 0.1))) { + C1 = acid_to_ph_57 / (di_ph - 5.7); + } else { // If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid. - ebc = row.f_color; - switch (row.f_graintype) { + switch (graintype) { case 0: // Base, Special, Kilned case 3: case 5: C1 = 0.014 * ebc - 34.192; @@ -2821,7 +2812,19 @@ case 4: C1 = -149; // Sour malt break; } - } + } + return C1; + } + + function ProtonDeficit(pHZ) { + + var rows, i, C1, x, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal')); + // proton deficit for the grist + if ((rows = $('#fermentableGrid').jqxGrid('getrows'))) { + for (i = 0; i < rows.length; i++) { + row = rows[i]; + if (row.f_added == 0 && row.f_graintype != 6) { // Added == Mash && graintype != No Malt + C1 = BufferCapacity(row.f_di_ph, row.f_acid_to_ph_57, row.f_color, row.f_graintype); x = C1 * (pHZ - row.f_di_ph); // AcidRequired(ZpH) Result += x * row.f_amount; } @@ -2845,24 +2848,11 @@ pH += deltapH; pd = ProtonDeficit(pH); } - pH = Round(pH, 2); + pH = Round(pH, 6); //console.log('MashpH() n: ' + n + ' pH: ' + pH); return pH; } - function GetAcidSpecs(AT) { - switch (AT) { - case 0: // Melkzuur - return { pK1: 3.86, pK2: 20, pK3: 20, MolWt: 90.08, AcidSG: 1214, AcidPrc: 0.88 }; - case 1: // Zoutzuur - return { pK1: -7, pK2: 20, pK3: 20, MolWt: 36.46, AcidSG: 1142, AcidPrc: 0.28 }; - case 2: // Fosforzuur - return { pK1: 2.12, pK2: 7.20, pK3: 12.44, MolWt: 98.00, AcidSG: 1170, AcidPrc: 0.25 }; - case 3: // Zwavelzuur - return { pK1: -1, pK2: 1.92, pK3: 20, MolWt: 98.07, AcidSG: 1700, AcidPrc: 0.93 }; - } - } - function calcWater() { /* Can be called during loading and building the screens */ @@ -2876,7 +2866,6 @@ magnesium = 0, sodium = 0, total_alkalinity = 0, - bicarbonate = 0, chloride = 0, sulfate = 0, ph = 0, @@ -2884,11 +2873,10 @@ frac = 0, TpH = 0, protonDeficit = 0, - AT, BT, result, pK1, pK2, pK3, MolWt, AcidSG, AcidPrc, + AT, BT, r1d, r2d, f1d, f2d, f3d, deltapH, deltapd, pd, n, - Res, - wg_calcium, wg_sodium, wg_total_alkalinity, wg_chloride, wg_sulfate, wg_bicarbonate; + Res; if (dataRecord.w1_name == '') { return; @@ -2914,96 +2902,85 @@ total_alkalinity = dataRecord.w1_total_alkalinity; ph = dataRecord.w1_ph; } + var bicarbonate = total_alkalinity * 1.22; + + /* Save mixed water ions for later */ + var wg_calcium = calcium; + var wg_sodium = sodium; + var wg_total_alkalinity = total_alkalinity; + var wg_chloride = chloride; + var wg_sulfate = sulfate; + var wg_bicarbonate = bicarbonate; + $('#wg_amount').val(liters); - wg_calcium = calcium; - $('#wg_calcium').val(Math.round(calcium * 10) / 10); - //var wg_magnesium = magnesium; - $('#wg_magnesium').val(Math.round(magnesium * 10) / 10); - wg_sodium = sodium; - $('#wg_sodium').val(Math.round(sodium * 10) / 10); - wg_total_alkalinity = total_alkalinity; - $('#wg_total_alkalinity').val(Math.round(total_alkalinity * 10) / 10); - wg_chloride = chloride; - $('#wg_chloride').val(Math.round(chloride * 10) / 10); - wg_sulfate = sulfate; - $('#wg_sulfate').val(Math.round(sulfate * 10) / 10); - // Note: brouwhulp has the malts included here in the result. - //var wg_ph = ph; - var mash_ph = Round(MashpH(), 1); - $('#wg_ph').val(Round(ph, 1)); - $('#wb_ph').val(mash_ph); - $('#est_mash_ph').val(mash_ph); - bicarbonate = total_alkalinity * 1.22; - wg_bicarbonate = bicarbonate; - - // Noot: de volgende berekeningen geven bijna gelijke resultaten in Brun'water. - // Calculate Ca - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4; - calcium += 1000 * RA / liters; - - // Calculate Mg - RA = parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4; - magnesium += 1000 * RA / liters; - - // Calculate Na - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3; - sodium += 1000 * RA / liters; - - // Calculate SO4 - RA = parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 + - parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4; - sulfate += 1000 * RA / liters; - - // Calculate Cl - RA = 2 * parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 + - parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl; - chloride += 1000 * RA / liters; - // Einde noot. - - if (parseInt($('#wa_acid_name').val()) < 0 || parseInt($('#wa_acid_name').val()) > 3) { + $('#wg_calcium').val(Round(calcium, 1)); + $('#wg_magnesium').val(Round(magnesium, 1)); + $('#wg_sodium').val(Round(sodium, 1)); + $('#wg_total_alkalinity').val(Round(total_alkalinity, 1)); + $('#wg_chloride').val(Round(chloride, 1)); + $('#wg_sulfate').val(Round(sulfate, 1)); + $('#wg_ph').val(Round(ph, 2)); + + var mash_ph = Round(MashpH(), 3); + console.log('Distilled water mash pH: ' + mash_ph); + + /* Calculate Salt additions */ + if (liters > 0) { + calcium += (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000) / liters; + magnesium += (parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4 * 1000) / liters; + sodium += (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters; + sulfate += (parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 * 1000 + + parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 * 1000) / liters; + chloride += (2 * parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 * 1000 + + parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl * 1000) / liters; + } + + if (dataRecord.wa_acid_name < 0 || dataRecord,wa_acid_name >= AcidTypeData.length) { $('#wa_acid_name').val(0); dataRecord.wa_acid_name = 0; + dataRecord.wa_acid_perc = AcidTypeData[0].AcidPrc; + $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc); } if (last_acid == '') - last_acid = AcidTypeData[$('#wa_acid_name').val()].nl; - - if (parseInt($('#wa_base_name').val()) < 0 || parseInt($('#wa_base_name').val()) > 3) { + last_acid = AcidTypeData[dataRecord.wa_acid_name].nl; + + if (parseFloat(dataRecord.wa_acid_perc) == 0) { + dataRecord.wa_acid_perc = AcidTypeData[AT].AcidPrc; + $('#wa_acid_perc').val(AcidTypeData[AT].AcidPrc); + } + + if (dataRecord.wa_base_name < 0 || dataRecord.wa_base_name > 3) { $('#wa_base_name').val(0); dataRecord.wa_base_name = 0; } if (last_base == '') - last_base = BaseTypeData[$('#wa_base_name').val()].nl; + last_base = BaseTypeData[dataRecord.wa_base_name].nl; AT = dataRecord.wa_acid_name; BT = dataRecord.wa_base_name; - result = GetAcidSpecs(AT); - pK1 = result.pK1; - pK2 = result.pK2; - pK3 = result.pK3; - MolWt = result.MolWt; - AcidSG = result.AcidSG; - AcidPrc = result.AcidPrc; + /* Note that the next calculations do not correct the pH change by the added salts. + This pH change is at most 0.1 pH and is a minor difference in Acid amount. */ if (dataRecord.calc_acid) { + /* Auto calculate pH */ + $('.c_mashph').show(); TpH = parseFloat(dataRecord.mash_ph); protonDeficit = ProtonDeficit(TpH); //console.log('calc_acid tgt: ' + TpH + ' protonDeficit: ' + protonDeficit); if (protonDeficit > 0) { // Add acid $('#wa_base').val(0); + dataRecord.wa_base = 0; setWaterAgent(last_base, 0); - frac = CalcFrac(TpH, pK1, pK2, pK3); + frac = CalcFrac(TpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); Acid = protonDeficit / frac; - Acid *= MolWt; // mg + Acid *= AcidTypeData[AT].MolWt; // mg Acidmg = Acid; - Acid = Acid / AcidSG; // ml - - if (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) == 0) - $('#wa_acid_perc').val(AcidPrc); - Acid = Round(Acid * AcidPrc / (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) / 100), 2); // ml - //console.log('Final ml: ' + Acid); + Acid = Acid / AcidTypeData[AT].AcidSG; // ml + Acid = Round(Acid / (parseFloat(dataRecord.wa_acid_perc) / 100), 2); // ml + console.log('Mash auto Acid final ml: ' + Acid); $('#wa_acid').val(Acid); setWaterAgent(AcidTypeData[AT].nl, Acid); @@ -3011,9 +2988,10 @@ total_alkalinity = bicarbonate * 50 / 61; } else if (protonDeficit < 0) { //Add base $('#wa_acid').val(0); + dataRecord.wa_acid = 0; setWaterAgent(last_acid, 0); - r1d = Math.pow(10, (TpH - 6.38)); - r2d = Math.pow(10, (TpH - 10.38)); + r1d = Math.pow(10, (TpH - 6.35)); + r2d = Math.pow(10, (TpH - 10.33)); f1d = 1 / (1 + r1d + r1d * r2d); f2d = f1d * r1d; f3d = f2d * r2d; @@ -3025,13 +3003,11 @@ setWaterAgent('NaHCO3', Round(RA, 2)); if (liters > 0) { // Na - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3; - RA = 1000 * RA / liters; + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters; sodium = wg_sodium + RA; // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3; - RA = 1000 * RA / liters; + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3 * 1000) / liters; bicarbonate = wg_bicarbonate + RA; total_alkalinity = bicarbonate * 50 / 61; RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); @@ -3043,13 +3019,11 @@ $('#wa_base').val(Round(RA, 2)); setWaterAgent('Na2CO3', Round(RA, 2)); if (liters > 0) { - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3; - RA = 1000 * RA / liters; + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000+ + parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3 * 1000) / liters; sodium = wg_sodium + RA; // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3; - RA = 1000 * RA / liters; + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3 * 1000) / liters; bicarbonate = wg_bicarbonate + RA; total_alkalinity = bicarbonate * 50 / 61; RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); @@ -3064,15 +3038,13 @@ setWaterAgent('CaCO3', Round(RA, 2)); if (liters > 0) { //Bicarbonate - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3; - RA = 1000 * RA / liters; + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3 * 1000) / liters; bicarbonate = wg_bicarbonate + RA; total_alkalinity = bicarbonate * 50 / 61; //Ca precipitates out as Ca10(PO4)6(OH)2 - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaCO3; - RA = 1000 * RA / liters; + RA = (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaCO3 * 1000) / liters; calcium = wg_calcium + RA; RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); } @@ -3087,10 +3059,9 @@ total_alkalinity = wg_total_alkalinity + RA; bicarbonate = total_alkalinity * 61 / 50; // Calcium - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaOH2; - RA = 1000 * RA / liters; + RA = (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaOH2 * 1000) / liters; calcium = wg_calcium + RA; RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); } @@ -3098,98 +3069,86 @@ } } ph = TpH; - $('#wb_ph').val(Round(ph, 1)); - $('#est_mash_ph').val(Round(ph, 1)); - } else { // Manual + $('#wb_ph').val(Round(ph, 2)); + $('#est_mash_ph').val(Round(ph, 2)); + } else { + /* Manual calculate pH */ + $('.c_mashph').hide(); console.log('calc_acid no'); - // First add base salts - if (parseFloat($('#wa_base').jqxNumberInput('decimal')) > 0) { - if (liters > 0) { - switch (BT) { - case 0: // Sodiumbicarbonate, Na - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3; - RA = 1000 * RA / liters; - sodium = wg_sodium + RA; - // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - break; - case 1: // Sodiumcarbonate - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3; - RA = 1000 * RA / liters; - sodium = wg_sodium + RA; - // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - break; - case 2: // Calciumcarbonate: Bicarbonate - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - // Ca - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaCO3; - RA = 1000 * RA / liters; - calcium = wg_calcium + RA; - break; - } + if (parseFloat($('#wa_base').jqxNumberInput('decimal')) > 0 && liters > 0) { + /* First add the base salts */ + switch (BT) { + case 0: // Sodiumbicarbonate, Na + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters; + sodium = wg_sodium + RA; + // HCO3 + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3 * 1000) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + break; + case 1: // Sodiumcarbonate + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3 * 1000) / liters; + sodium = wg_sodium + RA; + // HCO3 + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3 * 1000) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + break; + case 2: // Calciumcarbonate: Bicarbonate + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 * 1000 / MMCaCO3) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + // Ca + RA = (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa * 1000 / MMCaCl2 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa * 1000 / MMCaSO4 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa * 1000/ MMCaCO3) / liters; + calcium = wg_calcium + RA; + break; } } - TpH = parseFloat(dataRecord.mash_ph); - pHa = MashpH(); // This one is in demi water, should be in adjusted water??? - // Then calculate the new pH with added acids - if (parseFloat($('#wa_acid').jqxNumberInput('decimal')) > 0) { - console.log('TpH: ' + TpH + ' water: ' + pHa); - Acid = parseFloat($('#wa_acid').jqxNumberInput('decimal')); - if (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) == 0) - $('#wa_acid_perc').val(AcidPrc); - Acid = Acid / AcidPrc * (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) / 100); // ml - Acid *= AcidSG; // ml - Acid /= MolWt; // mg - Acidmg = Acid; - - //find the pH where the protondeficit = protondeficit by the acid - frac = CalcFrac(pHa, pK1, pK2, pK3); - protonDeficit = Acid * frac; - - deltapH = 0.001; - deltapd = 0.1; - console.log('in calcWater() manual'); - pd = ProtonDeficit(pHa); - n = 0; - while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 2000)) { + pHa = Round(ph, 3); // Adjusted water pH + // Then calculate the new pH with added acids and malts + console.log('Mash pH: ' + pHa); + Acid = AcidTypeData[AT].AcidSG * (parseFloat(dataRecord.wa_acid_perc) / 100); // ml + Acid *= parseFloat($('#wa_acid').jqxNumberInput('decimal')); + Acid /= AcidTypeData[AT].MolWt; // mg + Acidmg = Acid; + + //find the pH where the protondeficit = protondeficit by the acid + frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); + protonDeficit = Round(Acid * frac, 3); + //console.log('protonDeficit Acid: ' + protonDeficit + ' frac: ' + frac + ' pH: ' + pHa); + + deltapH = 0.001; + deltapd = 0.1; + pd = Round(ProtonDeficit(pHa), 6); + n = 0; + while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 4000)) { n++; if (pd < (protonDeficit - deltapd)) pHa -= deltapH; else if (pd > (protonDeficit + deltapd)) pHa += deltapH; - frac = CalcFrac(pHa, pK1, pK2, pK3); + frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); protonDeficit = Acid * frac; pd = ProtonDeficit(pHa); - } - console.log('n: ' + n + ' pd: ' + pd + ' protonDeficit: ' + protonDeficit + ' frac: ' + frac + ' pHa: ' + pHa); - RA = wg_bicarbonate - protonDeficit * frac / liters; - bicarbonate = RA; - total_alkalinity = RA * 50 / 61; - ph = pHa; - $('#wb_ph').val(Round(ph, 1)); - $('#est_mash_ph').val(Round(ph, 1)); } + //console.log('n: ' + n + ' pd: ' + pd + ' protonDeficit: ' + protonDeficit + ' frac: ' + frac + ' pHa: ' + pHa); + RA = wg_bicarbonate - protonDeficit * frac / liters; + bicarbonate = RA; + total_alkalinity = RA * 50 / 61; + ph = pHa; + $('#wb_ph').val(Round(ph, 2)); + $('#est_mash_ph').val(Round(ph, 2)); } - if ((AT == 3) && (liters > 0)) { // Sulfuctic / Zwavelzuur + if ((AT == 3) && (liters > 0)) { // Sulfuric / Zwavelzuur RA = parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 + parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 + Acidmg / 1000 * MMSO4 / (MMSO4 + 2); @@ -3304,13 +3263,10 @@ function calcSparge() { - var TargetpH, Source_pH, Source_alkalinity, r1, r2, d, f1, f3, r143, r243, d43, f143, f343, - alkalinity, Ct, r1g, r2g, dg, f1g, f3g, Acid, AT, result, pK1, pK2, pK3, MolWt, AcidSG, AcidPrc, fract; - - // Code from BrewBuddy/Brouwhulp, who got it from http://www.brewery.org/brewery/library/Acidi0,00fWaterAJD0497.html - TargetpH = dataRecord.sparge_ph; - Source_pH = dataRecord.w1_ph; - Source_alkalinity = dataRecord.w1_total_alkalinity; + /* Based on the work of ajDeLange. */ + var TargetpH = dataRecord.sparge_ph; + var Source_pH = dataRecord.w1_ph; + var Source_alkalinity = dataRecord.w1_total_alkalinity; // Select watersource or fallback to the first source. if (dataRecord.sparge_source == 1) { // Source 2 if (dataRecord.w2_ph > 0.0) { @@ -3322,73 +3278,62 @@ } } else if (dataRecord.sparge_source == 2) { // Mixed if (dataRecord.w2_ph > 0.0) { - Source_pH = parseFloat($('#wg_ph').jqxNumberInput('decimal')); - Source_alkalinity = parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')); + Source_pH = parseFloat(dataRecord.wg_ph); + Source_alkalinity = parseFloat(dataRecord.wg_total_alkalinity); } else { dataRecord.sparge_source = 0; $('#sparge_source').val(0); } } - // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH - r1 = Math.pow(10, Source_pH - 6.38); - r2 = Math.pow(10, Source_pH - 10.373); - d = 1 + r1 + r1 * r2; - f1 = 1 / d; - f3 = r1 * r2 / d; + // Step 1: Compute the mole fractions of carbonic (f1) and carbonate(f3) at the source water pH + var r1 = Math.pow(10, Source_pH - 6.35); + var r2 = Math.pow(10, Source_pH - 10.33); + var d = 1 + r1 + r1 * r2; + var f1 = 1 / d; + var f3 = r1 * r2 / d; //Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) - r143 = Math.pow(10, 4.3 - 6.38); - r243 = Math.pow(10, 4.3 - 10.373); - d43 = 1 + r143 + r143 * r243; - f143 = 1 / d43; - f343 = r143 * r243 / d43; - - //Step 3. Convert the water alkalinity to milliequivalents/L - alkalinity = Source_alkalinity / 50; + var r143 = Math.pow(10, 4.3 - 6.35); + var r243 = Math.pow(10, 4.3 - 10.33); + var d43 = 1 + r143 + r143 * r243; + var f143 = 1 / d43; + var f343 = r143 * r243 / d43; //Step 4. Solve - Ct = (alkalinity - 1000 * (Math.pow(10, -4.3) - Math.pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343)); + var Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); //Step 5. Compute mole fractions at desired pH - r1g = Math.pow(10, TargetpH - 6.38); - r2g = Math.pow(10, TargetpH - 10.373); - dg = 1 + r1g + r1g * r2g; - f1g = 1 / dg; - f3g = r1g * r2g / dg; + var r1g = Math.pow(10, TargetpH - 6.35); + var r2g = Math.pow(10, TargetpH - 10.33); + var dg = 1 + r1g + r1g * r2g; + var f1g = 1 / dg; + var f3g = r1g * r2g / dg; //Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) - Acid = Ct * ((f1g - f1) + (f3 - f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH); //mEq/l + var Acid = Ct * ((f1g - f1) + (f3 - f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH); //mEq/l Acid += 0.01; // Add acid that would be required for distilled water. - if (dataRecord.sparge_acid_type < 0 || dataRecord.sparge_acid_type > 3) { + + //Step 8. Get the acid data. + var AT = dataRecord.sparge_acid_type; + if (AT < 0 || AT >= AcidTypeData.length) { + AT = 0; dataRecord.sparge_acid_type = 0; $('#sparge_acid_type').val(0); + dataRecord.sparge_acid_perc = AcidTypeData[0].AcidPrc; + $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc); } - - //Step 8. Get the acid data. - AT = dataRecord.sparge_acid_type; - result = GetAcidSpecs(AT); - pK1 = result.pK1; - pK2 = result.pK2; - pK3 = result.pK3; - MolWt = result.MolWt; - AcidSG = result.AcidSG; - AcidPrc = result.AcidPrc; - fract = CalcFrac(TargetpH, pK1, pK2, pK3); + var fract = CalcFrac(TargetpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); //Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid. Acid /= fract; //Step 10. Multiply by molecular weight of the acid - Acid *= MolWt; //mg - - Acid = Acid / AcidSG; //ml ; 88% lactic solution - f1 = dataRecord.sparge_acid_perc; - if (f1 <= 0.1) - f1 = AcidPrc; - Acid = Acid * AcidPrc / (f1 / 100); - - Acid *= dataRecord.sparge_volume; //ml lactic acid total + Acid *= AcidTypeData[AT].MolWt; //mg + + //Step 11. Divide by Specific Gravity and Percentage to get the final ml. + Acid = Acid / AcidTypeData[AT].AcidSG / (dataRecord.sparge_acid_perc / 100); //ml + Acid *= dataRecord.sparge_volume; //ml acid total Acid = Round(Acid, 2); dataRecord.sparge_acid_amount = Acid / 1000; $('#sparge_acid_amount').val(Acid); @@ -3923,20 +3868,25 @@ $('#wa_acid_name').on('select', function(event) { if (event.args) { var index = event.args.index; - console.log('wa_acid_name ' + index); + console.log('wa_acid_name ' + index + ' last_acid: ' + last_acid); setWaterAgent(last_acid, 0); last_acid = AcidTypeData[index].nl; + dataRecord.wa_acid_name = index; + dataRecord.wa_acid_perc = AcidTypeData[index].AcidPrc; + $('#wa_acid_perc').val(dataRecord.wa_acid_perc); + calcWater(); setWaterAgent(last_acid, parseFloat($('#wa_acid').jqxNumberInput('decimal'))); - dataRecord.wa_acid_name = index; - calcWater(); } }); $('#wa_acid').on('change', function(event) { - var name = AcidTypeData[$('#wa_acid_name').val()].nl; + var name = AcidTypeData[dataRecord.wa_acid_name].nl; setWaterAgent(name, parseFloat(event.args.value)); calcWater(); }); - $('#wa_acid_perc').on('change', function(event) { calcWater(); }); + $('#wa_acid_perc').on('change', function(event) { + dataRecord.wa_acid_perc = parseFloat(event.args.value); + calcWater(); + }); $('#color_method').on('select', function(event) { dataRecord.color_method = event.args.index; @@ -4034,6 +3984,8 @@ if (event.args) { dataRecord.sparge_acid_type = event.args.index; console.log('new sparge_acid_type: ' + dataRecord.sparge_acid_type); + dataRecord.sparge_acid_perc = AcidTypeData[event.args.index].AcidPrc; + $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc); calcSparge(); } }); @@ -5626,7 +5578,8 @@ calcWater(); } }); - $('#w1_amount,#w1_calcium,#w1_magnesium,#w1_sodium,#w1_total_alkalinity,#w1_chloride,#w1_sulfate,#w1_ph').jqxNumberInput(Show1wat); + $('#w1_amount,#w1_calcium,#w1_magnesium,#w1_sodium,#w1_total_alkalinity,#w1_chloride,#w1_sulfate').jqxNumberInput(Show1wat); + $('#w1_ph').jqxNumberInput(Show2wat); // Water source 2 $('#w2_name').jqxDropDownList({ placeHolder: 'Kies meng water:', @@ -5668,16 +5621,19 @@ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 94, height: 23, min: 0, max: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, readOnly: true }); - $('#w2_calcium,#w2_magnesium,#w2_sodium,#w2_total_alkalinity,#w2_chloride,#w2_sulfate,#w2_ph').jqxNumberInput(Show1wat); + $('#w2_calcium,#w2_magnesium,#w2_sodium,#w2_total_alkalinity,#w2_chloride,#w2_sulfate').jqxNumberInput(Show1wat); + $('#w2_ph').jqxNumberInput(Show2wat); // Water mixed - $('#wg_amount,#wg_calcium,#wg_magnesium,#wg_sodium,#wg_total_alkalinity,#wg_chloride,#wg_sulfate,#wg_ph').jqxNumberInput(Show1wat); + $('#wg_amount,#wg_calcium,#wg_magnesium,#wg_sodium,#wg_total_alkalinity,#wg_chloride,#wg_sulfate').jqxNumberInput(Show1wat); + $('#wg_ph').jqxNumberInput(Show2wat); // Water treated $('#wb_calcium').jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'}); $('#wb_magnesium').jqxTooltip({ content: 'De ideale hoeveelheid Magnesium is tusse 10 en 30.'}); $('#wb_sodium').jqxTooltip({ content: 'De ideale hoeveelheid Natrium is lager dan 150.'}); $('#wb_chloride').jqxTooltip({ content: 'De ideale hoeveelheid Chloride is tussen 50 en 100.'}); $('#wb_sulfate').jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is tussen 50 en 350.'}); - $('#wb_calcium,#wb_magnesium,#wb_sodium,#wb_total_alkalinity,#wb_chloride,#wb_sulfate,#wb_ph').jqxNumberInput(Show1wat); + $('#wb_calcium,#wb_magnesium,#wb_sodium,#wb_total_alkalinity,#wb_chloride,#wb_sulfate').jqxNumberInput(Show1wat); + $('#wb_ph').jqxNumberInput(Show2wat); // Water target profile $('#pr_name').jqxDropDownList({ placeHolder: 'Kies doel profiel:', @@ -5790,7 +5746,7 @@ $('#brew_sparge_volume').jqxTooltip({ content: 'Het spoelwater voorraad volume, in te stellen in de Water tab.' }); $('#brew_date_start,#brew_date_end').jqxDateTimeInput(DateTimeopts); $('#brew_date_start,#brew_date_end').on('close', function(event) { calcStage(); }); - $('#est_mash_ph').jqxNumberInput(Show1wat); + $('#est_mash_ph').jqxNumberInput(Show2wat); $('#brew_mash_ph,#brew_preboil_ph,#brew_aboil_ph').jqxNumberInput(SpinpH); $('#brew_mash_sg').on('valueChanged', function() { calcMashEfficiency(); }); $('#brew_preboil_sg').on('valueChanged', function(event) { diff -r 74b442eae07b -r 4bb005694ce7 www/js/profile_water.js --- a/www/js/profile_water.js Sun Apr 26 10:36:38 2020 +0200 +++ b/www/js/profile_water.js Fri May 01 11:56:24 2020 +0200 @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (C) 2014-2019 + * Copyright (C) 2014-2020 * * Michiel Broek * @@ -131,6 +131,7 @@ $('#magnesium').jqxTooltip({ content: 'Magnesium (Mg).' }); $('#ph').jqxTooltip({ content: 'De zuurgraad (pH).' }); $('#total_alkalinity').jqxTooltip({ content: 'Totale alkaliniteit. Berekend meteen de Bicarbonaat.' }); + $('#balance').jqxTooltip({ content: 'De ionen balans van het water. Ideaal minder dan 0.1 verschil tussen kationen en anionen. Meer dan 0.5 is een fout in het waterraport.' }); // initialize the input fields. $('#name').jqxInput({ theme: theme, width: 640, height: 23 }); @@ -143,6 +144,7 @@ $('#magnesium').jqxNumberInput(Spin1dec); $('#ph').jqxNumberInput(Spin2pH); $('#total_alkalinity').jqxNumberInput(Spin1dec); + $('#balance').jqxNumberInput(Show2dec); // initialize jqxGrid $('#jqxgrid').jqxGrid({ @@ -206,6 +208,7 @@ $('#ph').val(dataRecord.ph); $('#notes').val(dataRecord.notes); $('#total_alkalinity').val(dataRecord.total_alkalinity); + calcBalance(); // show the popup window. $('#popupWindow').jqxWindow('open'); } @@ -213,13 +216,50 @@ ] }); + function calcBalance() { + var cations = (dataRecord.calcium / 20.039) + (dataRecord.magnesium / 12.1525) + (dataRecord.sodium / 22.989); + var anions = (dataRecord.bicarbonate / 61.016) + (dataRecord.sulfate / 48.031) + (dataRecord.chloride / 35.4527); + var balance = Round(cations - anions, 2); + $('#balance').val(balance); + if (balance <= 0.1 && balance >= -0.1) + $('#wr_balance').html(""); + else if (balance <= 0.5 && balance >= -0.5) + $('#wr_balance').html(""); + else + $('#wr_balance').html(""); + } + + $('#calcium').on('change', function(event) { + dataRecord.calcium = parseFloat(event.args.value); + calcBalance(); + }); + $('#magnesium').on('change', function(event) { + dataRecord.magnesium = parseFloat(event.args.value); + calcBalance(); + }); + $('#sodium').on('change', function(event) { + dataRecord.sodium = parseFloat(event.args.value); + calcBalance(); + }); $('#total_alkalinity').on('change', function(event) { + dataRecord.total_alkalinity = parseFloat(event.args.value); dataRecord.bicarbonate = parseFloat(event.args.value) * 1.22; $('#bicarbonate').val(dataRecord.bicarbonate); + calcBalance(); }); $('#bicarbonate').on('change', function(event) { + dataRecord.bicarbonate = parseFloat(event.args.value); dataRecord.total_alkalinity = parseFloat(event.args.value) * 50 / 61; $('#total_alkalinity').val(dataRecord.total_alkalinity); + calcBalance(); + }); + $('#sulfate').on('change', function(event) { + dataRecord.sulfate = parseFloat(event.args.value); + calcBalance(); + }); + $('#chloride').on('change', function(event) { + dataRecord.chloride = parseFloat(event.args.value); + calcBalance(); }); // initialize the popup window and buttons. diff -r 74b442eae07b -r 4bb005694ce7 www/js/rec_edit.js --- a/www/js/rec_edit.js Sun Apr 26 10:36:38 2020 +0200 +++ b/www/js/rec_edit.js Fri May 01 11:56:24 2020 +0200 @@ -33,6 +33,7 @@ last_acid = '', Ka1 = 0.0000004445, Ka2 = 0.0000000000468, + error_count = 0, MMCa = 40.048, MMMg = 24.305, MMNa = 22.98976928, @@ -560,36 +561,45 @@ } +function BufferCapacity(di_ph, acid_to_ph_57, ebc, graintype) { + C1 = 0; + if ((di_ph != 5.7) && ((acid_to_ph_57 < - 0.1) || (acid_to_ph_57 > 0.1))) { + C1 = acid_to_ph_57 / (di_ph - 5.7); + } else { + // If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid. + switch (graintype) { + case 0: // Base, Special, Kilned + case 3: + case 5: C1 = 0.014 * ebc - 34.192; + break; + case 2: C1 = -0.0597 * ebc - 32.457; // Crystal + break; + case 1: C1 = 0.0107 * ebc - 54.768; // Roast + break; + case 4: C1 = -149; // Sour malt + break; + } + } + return C1; +} + + function ProtonDeficit(pHZ) { - var ebc, C1, i, rows, row, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal')); + var C1, i, rows, row, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal')); // proton deficit for the grist - rows = $('#fermentableGrid').jqxGrid('getrows'); - for (i = 0; i < rows.length; i++) { - row = rows[i]; - if (row.f_added == 0 && row.f_graintype != 6) { // Added == Mash && graintype != No Malt - // Check if acid is required - C1 = 0; - if ((row.f_di_ph != 5.7) && ((row.f_acid_to_ph_57 < - 0.1) || (row.f_acid_to_ph_57 > 0.1))) { - C1 = row.f_acid_to_ph_57 / (row.f_di_ph - 5.7); - } else { - // If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid. - ebc = row.f_color; - switch (row.f_graintype) { - case 0: // Base, Special, Kilned - case 3: - case 5: C1 = 0.014 * ebc - 34.192; - break; - case 2: C1 = -0.0597 * ebc - 32.457; // Crystal - break; - case 1: C1 = 0.0107 * ebc - 54.768; // Roast - break; - case 4: C1 = -149; // Sour malt - break; - } + if ((rows = $('#fermentableGrid').jqxGrid('getrows'))) { + for (i = 0; i < rows.length; i++) { + row = rows[i]; + if (row.f_added == 0 && row.f_graintype != 6) { // Added == Mash && graintype != No Malt + C1 = BufferCapacity(row.f_di_ph, row.f_acid_to_ph_57, row.f_color, row.f_graintype); + x = C1 * (pHZ - row.f_di_ph); // AcidRequired(ZpH) + Result += x * row.f_amount; } - x = C1 * (pHZ - row.f_di_ph); // AcidRequired(ZpH) - Result += x * row.f_amount; } + } else { + error_count++; + if (error_count < 5) + console.log('ProtonDeficit(' + pHZ + ') invalid grist, return ' + Result); } return Result; } @@ -609,24 +619,12 @@ pH += deltapH; pd = ProtonDeficit(pH); } + pH = Round(pH, 6); //console.log('MashpH() n: ' + n + ' pH: ' + pH); return pH; } -function GetAcidSpecs(AT) { - switch (AT) { - case 0: return { pK1: 3.86, pK2: 20, pK3: 20, MolWt: 90.08, AcidSG: 1214, AcidPrc: 0.88 }; // Melkzuur - case 1: return { pK1: -7, pK2: 20, pK3: 20, MolWt: 36.46, AcidSG: 1142, AcidPrc: 0.28 }; // Zoutzuur - case 2: return { pK1: 2.12, pK2: 7.20, pK3: 12.44, MolWt: 98.00, AcidSG: 1170, AcidPrc: 0.25 }; // Fosforzuur - case 3: return { pK1: -1, pK2: 1.92, pK3: 20, MolWt: 98.07, AcidSG: 1700, AcidPrc: 0.93 }; // Zwavelzuur - } -} - - - - - $(document).ready(function() { @@ -1899,11 +1897,10 @@ frac = 0, TpH = 0, protonDeficit = 0, - AT, BT, result, pK1, pK2, pK3, MolWt, AcidSG, AcidPrc, + AT, BT, r1d, r2d, f1d, f2d, f3d, deltapH, deltapd, pd, n, - Res, - wg_calcium, wg_sodium, wg_total_alkalinity, wg_chloride, wg_sulfate, wg_bicarbonate; + Res; if (dataRecord.w1_name == '') { return; @@ -1929,269 +1926,248 @@ total_alkalinity = dataRecord.w1_total_alkalinity; ph = dataRecord.w1_ph; } - $('#wg_amount').val(liters); - wg_calcium = calcium; - $('#wg_calcium').val(Math.round(calcium * 10) / 10); - //wg_magnesium = magnesium; - $('#wg_magnesium').val(Math.round(magnesium * 10) / 10); - wg_sodium = sodium; - $('#wg_sodium').val(Math.round(sodium * 10) / 10); - wg_total_alkalinity = total_alkalinity; - $('#wg_total_alkalinity').val(Math.round(total_alkalinity * 10) / 10); - wg_chloride = chloride; - $('#wg_chloride').val(Math.round(chloride * 10) / 10); - wg_sulfate = sulfate; - $('#wg_sulfate').val(Math.round(sulfate * 10) / 10); - // Note: brouwhulp has the malts included here in the result. - //wg_ph = ph; - $('#wg_ph').val(Round(ph, 1)); - $('#wb_ph').val(Round(MashpH(), 1)); - bicarbonate = total_alkalinity * 1.22; - wg_bicarbonate = bicarbonate; + var bicarbonate = total_alkalinity * 1.22; + + /* Save mixed water ions for later */ + var wg_calcium = calcium; + var wg_sodium = sodium; + var wg_total_alkalinity = total_alkalinity; + var wg_chloride = chloride; + var wg_sulfate = sulfate; + var wg_bicarbonate = bicarbonate; - // Noot: de volgende berekeningen geven bijna gelijke resultaten in Brun'water. - // Calculate Ca - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4; - calcium += 1000 * RA / liters; + $('#wg_amount').val(liters); + $('#wg_calcium').val(Math.round(calcium * 10) / 10); + $('#wg_magnesium').val(Math.round(magnesium * 10) / 10); + $('#wg_sodium').val(Math.round(sodium * 10) / 10); + $('#wg_total_alkalinity').val(Math.round(total_alkalinity * 10) / 10); + $('#wg_chloride').val(Math.round(chloride * 10) / 10); + $('#wg_sulfate').val(Math.round(sulfate * 10) / 10); + $('#wg_ph').val(Round(ph, 1)); - // Calculate Mg - RA = parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4; - magnesium += 1000 * RA / liters; + var mash_ph = Round(MashpH(), 3); + console.log('Distilled water mash pH: ' + mash_ph); - // Calculate Na - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl; - sodium += 1000 * RA / liters; + /* Calculate Salt additions */ + if (liters > 0) { + calcium += (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000) / liters; + magnesium += (parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4 * 1000) / liters; + sodium += (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters; + sulfate += (parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 * 1000 + + parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 * 1000) / liters; + chloride += (2 * parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 * 1000 + + parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl * 1000) / liters; + } - // Calculate SO4 - RA = parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 + - parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4; - sulfate += 1000 * RA / liters; - - // Calculate Cl - RA = 2 * parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 + - parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl; - chloride += 1000 * RA / liters; - // Einde noot. - - if ($('#wa_acid_name').val() < 0 || $('#wa_acid_name').val() > 3) { + if (dataRecord.wa_acid_name < 0 || dataRecord,wa_acid_name >= AcidTypeData.length) { $('#wa_acid_name').val(0); dataRecord.wa_acid_name = 0; + dataRecord.wa_acid_perc = AcidTypeData[0].AcidPrc; + $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc); } if (last_acid == '') - last_acid = AcidTypeData[$('#wa_acid_name').val()].nl; + last_acid = AcidTypeData[dataRecord.wa_acid_name].nl; - if ($('#wa_base_name').val() < 0 || $('#wa_base_name').val() > 3) { + if (parseFloat(dataRecord.wa_acid_perc) == 0) { + dataRecord.wa_acid_perc = AcidTypeData[AT].AcidPrc; + $('#wa_acid_perc').val(AcidTypeData[AT].AcidPrc); + } + + if (dataRecord.wa_base_name < 0 || dataRecord.wa_base_name > 3) { $('#wa_base_name').val(0); dataRecord.wa_base_name = 0; } if (last_base == '') - last_base = BaseTypeData[$('#wa_base_name').val()].nl; + last_base = BaseTypeData[dataRecord.wa_base_name].nl; AT = dataRecord.wa_acid_name; BT = dataRecord.wa_base_name; - result = GetAcidSpecs(AT); - pK1 = result.pK1; - pK2 = result.pK2; - pK3 = result.pK3; - MolWt = result.MolWt; - AcidSG = result.AcidSG; - AcidPrc = result.AcidPrc; + /* Note that the next calculations do not correct the pH change by the added salts. + This pH change is at most 0.1 pH and is a minor difference in Acid amount. */ if (dataRecord.calc_acid) { + $('.c_mashph').show(); + /* Auto calculate pH */ TpH = parseFloat(dataRecord.mash_ph); protonDeficit = ProtonDeficit(TpH); console.log('calc_acid tgt: ' + TpH + ' protonDeficit: ' + protonDeficit); if (protonDeficit > 0) { // Add acid $('#wa_base').val(0); setWaterAgent(last_base, 0); - frac = CalcFrac(TpH, pK1, pK2, pK3); + frac = CalcFrac(TpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); Acid = protonDeficit / frac; - Acid *= MolWt; // mg + Acid *= AcidTypeData[AT].MolWt; // mg Acidmg = Acid; - Acid = Acid / AcidSG; // ml - - if (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) == 0) - $('#wa_acid_perc').val(AcidPrc); - Acid = Acid * AcidPrc / (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) / 100); // ml - console.log('Final ml: ' + Acid); - $('#wa_acid').val(Math.round(Acid * 100) / 100); - setWaterAgent(AcidTypeData[AT].nl, Math.round(Acid * 100) / 100); + Acid = Acid / AcidTypeData[AT].AcidSG; // ml + Acid = Round(Acid / (parseFloat(dataRecord.wa_acid_perc) / 100), 2); // ml + console.log('Mash auto Acid final ml: ' + Acid); + $('#wa_acid').val(Acid); + setWaterAgent(AcidTypeData[AT].nl, Acid); bicarbonate = bicarbonate - protonDeficit * frac / liters; total_alkalinity = bicarbonate * 50 / 61; } else if (protonDeficit < 0) { //Add base $('#wa_acid').val(0); setWaterAgent(last_acid, 0); - r1d = Math.pow(10, (TpH - 6.38)); - r2d = Math.pow(10, (TpH - 10.38)); + r1d = Math.pow(10, (TpH - 6.35)); + r2d = Math.pow(10, (TpH - 10.33)); f1d = 1 / (1 + r1d + r1d * r2d); f2d = f1d * r1d; f3d = f2d * r2d; switch (BT) { - case 0: RA = -protonDeficit / (f1d - f3d); //Sodiumbicarbonate, mmol totaal - RA = RA * MMNaHCO3 / 1000; //gram - $('#wa_base').val(Round(RA, 2)); - setWaterAgent('NaHCO3', Round(RA, 2)); - if (liters > 0) { - // Na - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3; - RA = 1000 * RA / liters; - sodium = wg_sodium + RA; - // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - } - break; - case 1: RA = -protonDeficit / (2 * f1d + f2d); // Sodiumcarbonate, mmol totaal - RA = RA * MMNa2CO3 / 1000; //gram - $('#wa_base').val(Round(RA, 2)); - setWaterAgent('Na2CO3', Round(RA, 2)); - if (liters > 0) { - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3; - RA = 1000 * RA / liters; - sodium = wg_sodium + RA; - // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - } - break; - case 2: RA = -protonDeficit * (f1d - f3d); // Calciumcarbonate, mmol totaal - RA = RA * MMCaCO3 / 1000; //gram - //but only 1/3 is effective, so add 3 times as much - RA = 3 * RA; - $('#wa_base').val(Round(RA, 2)); - setWaterAgent('CaCO3', Round(RA, 2)); - if (liters > 0) { - //Bicarbonate - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - //Ca precipitates out as Ca10(PO4)6(OH)2 - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaCO3; - RA = 1000 * RA / liters; - calcium = wg_calcium + RA; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - } - break; - case 3: RA = -protonDeficit / 19.3; // Calciumhydroxide - $('#wa_base').val(Round(RA, 2)); - setWaterAgent('Ca(OH)2', Round(RA, 2)); - if (liters > 0) { - // Bicarbonate - RA = -protonDeficit / liters; - total_alkalinity = wg_total_alkalinity + RA; - bicarbonate = total_alkalinity * 61 / 50; - // Calcium - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaOH2; - RA = 1000 * RA / liters; - calcium = wg_calcium + RA; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - } - break; + case 0: + RA = -protonDeficit / (f1d - f3d); //Sodiumbicarbonate, mmol totaal + RA = RA * MMNaHCO3 / 1000; //gram + $('#wa_base').val(Round(RA, 2)); + setWaterAgent('NaHCO3', Round(RA, 2)); + if (liters > 0) { + // Na + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters; + sodium = wg_sodium + RA; + // HCO3 + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3 * 1000) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + } + break; + case 1: + RA = -protonDeficit / (2 * f1d + f2d); // Sodiumcarbonate, mmol totaal + RA = RA * MMNa2CO3 / 1000; //gram + $('#wa_base').val(Round(RA, 2)); + setWaterAgent('Na2CO3', Round(RA, 2)); + if (liters > 0) { + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3 * 1000) / liters; + sodium = wg_sodium + RA; + // HCO3 + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3 * 1000) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + } + break; + case 2: + RA = -protonDeficit * (f1d - f3d); // Calciumcarbonate, mmol totaal + RA = RA * MMCaCO3 / 1000; //gram + //but only 1/3 is effective, so add 3 times as much + RA = 3 * RA; + $('#wa_base').val(Round(RA, 2)); + setWaterAgent('CaCO3', Round(RA, 2)); + if (liters > 0) { + //Bicarbonate + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3 * 1000) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + //Ca precipitates out as Ca10(PO4)6(OH)2 + RA = (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaCO3 * 1000) / liters; + calcium = wg_calcium + RA; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + } + break; + case 3: + RA = -protonDeficit / 19.3; // Calciumhydroxide + $('#wa_base').val(Round(RA, 2)); + setWaterAgent('Ca(OH)2', Round(RA, 2)); + if (liters > 0) { + // Bicarbonate + RA = -protonDeficit / liters; + total_alkalinity = wg_total_alkalinity + RA; + bicarbonate = total_alkalinity * 61 / 50; + // Calcium + RA = (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaOH2 * 1000) / liters; + calcium = wg_calcium + RA; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + } + break; } } ph = TpH; - $('#wb_ph').val(Math.round(ph * 10) / 10); + $('#wb_ph').val(Round(ph, 2)); + $('#est_mash_ph').val(Round(ph, 2)); } else { // Manual + /* Manual calculate pH */ + $('.c_mashph').hide(); console.log('calc_acid no'); - // First add base salts - if (parseFloat($('#wa_base').jqxNumberInput('decimal')) > 0) { - if (liters > 0) { - switch (BT) { - case 0: // Sodiumbicarbonate, Na - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3; - RA = 1000 * RA / liters; - sodium = wg_sodium + RA; - // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - break; - case 1: // Sodiumcarbonate - RA = parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3; - RA = 1000 * RA / liters; - sodium = wg_sodium + RA; - // HCO3 - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - break; - case 2: // Calciumcarbonate: Bicarbonate - RA = parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3; - RA = 1000 * RA / liters; - bicarbonate = wg_bicarbonate + RA; - total_alkalinity = bicarbonate * 50 / 61; - RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); - // Ca - RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 + - parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 + - parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa / MMCaCO3; - RA = 1000 * RA / liters; - calcium = wg_calcium + RA; - break; - } + if (parseFloat($('#wa_base').jqxNumberInput('decimal')) > 0 && liters > 0) { + /* First add the base salts */ + switch (BT) { + case 0: // Sodiumbicarbonate, Na + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters; + sodium = wg_sodium + RA; + // HCO3 + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3 * 1000) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + break; + case 1: // Sodiumcarbonate + RA = (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3 * 1000) / liters; + sodium = wg_sodium + RA; + // HCO3 + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3 * 1000) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + break; + case 2: // Calciumcarbonate: Bicarbonate + RA = (parseFloat($('#wa_base').jqxNumberInput('decimal')) / 3 * MMHCO3 * 1000 / MMCaCO3) / liters; + bicarbonate = wg_bicarbonate + RA; + total_alkalinity = bicarbonate * 50 / 61; + RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium); + // Ca + RA = (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa * 1000 / MMCaCl2 + + parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa * 1000 / MMCaSO4 + + parseFloat($('#wa_base').jqxNumberInput('decimal')) * MMCa * 1000 / MMCaCO3) / liters; + calcium = wg_calcium + RA; + break; } } - TpH = parseFloat(dataRecord.mash_ph); - pHa = MashpH(); // This one is in demi water, should be in adjusted water??? - // Then calculate the new pH with added acids - if (parseFloat($('#wa_acid').jqxNumberInput('decimal')) > 0) { - console.log('TpH: ' + TpH + ' water: ' + pHa); - Acid = parseFloat($('#wa_acid').jqxNumberInput('decimal')); - if (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) == 0) - $('#wa_acid_perc').val(AcidPrc); - Acid = Acid / AcidPrc * (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) / 100); // ml - Acid *= AcidSG; // ml - Acid /= MolWt; // mg - Acidmg = Acid; + pHa = Round(ph, 3); // Adjusted water pH + // Then calculate the new pH with added acids and malts + console.log('Mash pH: ' + pHa); + Acid = AcidTypeData[AT].AcidSG * (parseFloat(dataRecord.wa_acid_perc) / 100); // ml + Acid *= parseFloat($('#wa_acid').jqxNumberInput('decimal')); + Acid /= AcidTypeData[AT].MolWt; // mg + Acidmg = Acid; - //find the pH where the protondeficit = protondeficit by the acid - frac = CalcFrac(pHa, pK1, pK2, pK3); - protonDeficit = Acid * frac; + //find the pH where the protondeficit = protondeficit by the acid + frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); + protonDeficit = Round(Acid * frac, 3); + //console.log('protonDeficit Acid: ' + protonDeficit + ' frac: ' + frac + ' pH: ' + pHa); - deltapH = 0.001; - deltapd = 0.1; - pd = ProtonDeficit(pHa); - n = 0; - while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 2000)) { + deltapH = 0.001; + deltapd = 0.1; + pd = Round(ProtonDeficit(pHa), 6); + n = 0; + while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 4000)) { n++; if (pd < (protonDeficit - deltapd)) pHa -= deltapH; else if (pd > (protonDeficit + deltapd)) pHa += deltapH; - frac = CalcFrac(pHa, pK1, pK2, pK3); + frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); protonDeficit = Acid * frac; pd = ProtonDeficit(pHa); - } - console.log('n: ' + n + ' pd: ' + pd + ' protonDeficit: ' + protonDeficit + ' frac: ' + frac + ' pHa: ' + pHa); - RA = wg_bicarbonate - protonDeficit * frac / liters; - bicarbonate = RA; - total_alkalinity = RA * 50 / 61; - ph = pHa; - $('#wb_ph').val(Round(ph, 1)); } + //console.log('n: ' + n + ' pd: ' + pd + ' protonDeficit: ' + protonDeficit + ' frac: ' + frac + ' pHa: ' + pHa); + RA = wg_bicarbonate - protonDeficit * frac / liters; + bicarbonate = RA; + total_alkalinity = RA * 50 / 61; + ph = pHa; + $('#wb_ph').val(Round(ph, 2)); + $('#est_mash_ph').val(Round(ph, 2)); } if ((AT == 3) && (liters > 0)) { // Sulfuctic / Zwavelzuur @@ -2305,14 +2281,11 @@ function calcSparge() { - var TargetpH, Source_pH, Source_alkalinity, r1, r2, d, f1, f3, - r143, r243, d43, f143, f343, alkalinity, Ct, r1g, r2g, dg, f1g, f3g, - Acid, AT, result, pK1, pK2, pK3, MolWt, AcidSG, AcidPrc, fract; - // Code from BrewBuddy/Brouwhulp, who got it from http://www.brewery.org/brewery/library/Acidi0,00fWaterAJD0497.html - TargetpH = dataRecord.sparge_ph; - Source_pH = dataRecord.w1_ph; - Source_alkalinity = dataRecord.w1_total_alkalinity; + /* Based on the work of ajDeLange. */ + var TargetpH = dataRecord.sparge_ph; + var Source_pH = dataRecord.w1_ph; + var Source_alkalinity = dataRecord.w1_total_alkalinity; // Select watersource or fallback to the first source. if (dataRecord.sparge_source == 1) { // Source 2 if (dataRecord.w2_ph > 0.0) { @@ -2333,63 +2306,54 @@ } // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH - r1 = Math.pow(10, Source_pH - 6.38); - r2 = Math.pow(10, Source_pH - 10.373); - d = 1 + r1 + r1 * r2; - f1 = 1 / d; - f3 = r1 * r2 / d; + var r1 = Math.pow(10, Source_pH - 6.35); + var r2 = Math.pow(10, Source_pH - 10.33); + var d = 1 + r1 + r1 * r2; + var f1 = 1 / d; + var f3 = r1 * r2 / d; //Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) - r143 = Math.pow(10, 4.3 - 6.38); - r243 = Math.pow(10, 4.3 - 10.373); - d43 = 1 + r143 + r143 * r243; - f143 = 1 / d43; - f343 = r143 * r243 / d43; + var r143 = Math.pow(10, 4.3 - 6.35); + var r243 = Math.pow(10, 4.3 - 10.33); + var d43 = 1 + r143 + r143 * r243; + var f143 = 1 / d43; + var f343 = r143 * r243 / d43; - //Step 3. Convert the sample alkalinity to milliequivalents/L - alkalinity = Source_alkalinity / 50; //Step 4. Solve - Ct = (alkalinity - 1000 * (Math.pow(10, -4.3) - Math.pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343)); + //var Ct = (alkalinity - 1000 * (Math.pow(10, -4.3) - Math.pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343)); + var Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); //Step 5. Compute mole fractions at desired pH - r1g = Math.pow(10, TargetpH - 6.38); - r2g = Math.pow(10, TargetpH - 10.373); - dg = 1 + r1g + r1g * r2g; - f1g = 1 / dg; - f3g = r1g * r2g / dg; + var r1g = Math.pow(10, TargetpH - 6.35); + var r2g = Math.pow(10, TargetpH - 10.33); + var dg = 1 + r1g + r1g * r2g; + var f1g = 1 / dg; + var f3g = r1g * r2g / dg; //Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) - Acid = Ct * ((f1g - f1) + (f3 - f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH); //mEq/l + var Acid = Ct * ((f1g - f1) + (f3 - f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH); //mEq/l Acid += 0.01; // Add acid that would be required for distilled water. - if (dataRecord.sparge_acid_type < 0 || dataRecord.sparge_acid_type > 3) { - dataRecord.sparge_acid_type = 0; - $('#sparge_acid_type').val(0); - } //Step 8. Get the acid data. AT = dataRecord.sparge_acid_type; - result = GetAcidSpecs(AT); - pK1 = result.pK1; - pK2 = result.pK2; - pK3 = result.pK3; - MolWt = result.MolWt; - AcidSG = result.AcidSG; - AcidPrc = result.AcidPrc; - fract = CalcFrac(TargetpH, pK1, pK2, pK3); + if (AT < 0 || AT >= AcidTypeData.length) { + AT = 0; + dataRecord.sparge_acid_type = 0; + $('#sparge_acid_type').val(0); + dataRecord.sparge_acid_perc = AcidTypeData[0].AcidPrc; + $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc); + } + var fract = CalcFrac(TargetpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); //Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid. Acid /= fract; //Step 10. Multiply by molecular weight of the acid - Acid *= MolWt; //mg + Acid *= AcidTypeData[AT].MolWt; //mg - Acid = Acid / AcidSG; //ml ; 88% lactic solution - f1 = dataRecord.sparge_acid_perc; - if (f1 <= 0.1) - f1 = AcidPrc; - Acid = Acid * AcidPrc / (f1 / 100); - - Acid *= dataRecord.sparge_volume; //ml lactic acid total + //Step 11. Divide by Specific Gravity and Percentage to get the final ml. + Acid = Acid / AcidTypeData[AT].AcidSG / (dataRecord.sparge_acid_perc / 100); //ml + Acid *= dataRecord.sparge_volume; //ml acid total Acid = Round(Acid, 2); dataRecord.sparge_acid_amount = Acid / 1000; $('#sparge_acid_amount').val(Acid); @@ -3750,8 +3714,7 @@ // Tab 7, Water $('#tgt_bu').jqxNumberInput(Show2wat); - $('#tgt_so4_cl').jqxNumberInput(Show1wat); - $('#got_so4_cl').jqxNumberInput(Show1wat); + $('#tgt_so4_cl,#got_so4_cl').jqxNumberInput(Show1wat); // Water source 1 $('#w1_name').jqxDropDownList({ @@ -3795,7 +3758,7 @@ $('#w1_total_alkalinity').jqxNumberInput(Show1wat); $('#w1_chloride').jqxNumberInput(Show1wat); $('#w1_sulfate').jqxNumberInput(Show1wat); - $('#w1_ph').jqxNumberInput(Show1wat); + $('#w1_ph').jqxNumberInput(Show2wat); // Water source 2 $('#w2_name').jqxDropDownList({ placeHolder: 'Kies meng water:', @@ -3840,7 +3803,7 @@ $('#w2_total_alkalinity').jqxNumberInput(Show1wat); $('#w2_chloride').jqxNumberInput(Show1wat); $('#w2_sulfate').jqxNumberInput(Show1wat); - $('#w2_ph').jqxNumberInput(Show1wat); + $('#w2_ph').jqxNumberInput(Show2wat); // Water mixed $('#wg_amount').jqxNumberInput(Show1wat); $('#wg_calcium').jqxNumberInput(Show1wat); @@ -3849,7 +3812,7 @@ $('#wg_total_alkalinity').jqxNumberInput(Show1wat); $('#wg_chloride').jqxNumberInput(Show1wat); $('#wg_sulfate').jqxNumberInput(Show1wat); - $('#wg_ph').jqxNumberInput(Show1wat); + $('#wg_ph').jqxNumberInput(Show2wat); // Water treated $('#wb_calcium').jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'}); $('#wb_calcium').jqxNumberInput(Show1wat); @@ -3862,7 +3825,7 @@ $('#wb_sulfate').jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is tussen 50 en 350.'}); $('#wb_sulfate').jqxNumberInput(Show1wat); $('#wb_total_alkalinity').jqxNumberInput(Show1wat); - $('#wb_ph').jqxNumberInput(Show1wat); + $('#wb_ph').jqxNumberInput(Show2wat); // Water target profile $('#pr_name').jqxDropDownList({ placeHolder: 'Kies doel profiel:', diff -r 74b442eae07b -r 4bb005694ce7 www/prod_edit.php --- a/www/prod_edit.php Sun Apr 26 10:36:38 2020 +0200 +++ b/www/prod_edit.php Fri May 01 11:56:24 2020 +0200 @@ -477,8 +477,8 @@ Calciumchloride (CaCl2) gr:
- Maish pH: -
+
Maish pH:
+
Spoelwater voorraad L:
diff -r 74b442eae07b -r 4bb005694ce7 www/profile_water.php --- a/www/profile_water.php Sun Apr 26 10:36:38 2020 +0200 +++ b/www/profile_water.php Fri May 01 11:56:24 2020 +0200 @@ -26,28 +26,37 @@   - Calcium (Ca) mg/l: + Calcium (Ca) mg/L:
- Chloride (Cl) mg/l: + Sulfaat (CaSO4) mg/L: +
+ + + Magnesium (Mg) mg/L: +
+ Chloride (Cl) mg/L:
- Magnesium (Mg) mg/l: -
- Sulfaat (CaSO4) mg/l: -
+ Natrium (Na) mg/L: +
+ Bicarbonaat (HCO3) mg/L: +
- Natrium (Na) mg/l: -
- Bicarbonaat (HCO3) mg/l: -
+ + + Totale alkaliteit (CaCO3) mg/L: +
+ + +   Zuurgraad (pH):
- Totale alkaliteit (CaCO3) mg/l: -
+ Ionen balans meq/L: +
diff -r 74b442eae07b -r 4bb005694ce7 www/rec_edit.php --- a/www/rec_edit.php Sun Apr 26 10:36:38 2020 +0200 +++ b/www/rec_edit.php Fri May 01 11:56:24 2020 +0200 @@ -296,8 +296,8 @@ Calciumchloride (CaCl2) gr:
- Maish pH: -
+
Maish pH:
+
Spoelwater liters: