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:',