www/js/rec_edit.js

changeset 154
ef298b5aa994
parent 150
159d7a89fcef
child 155
041af8a82d77
--- a/www/js/rec_edit.js	Tue Dec 25 15:06:39 2018 +0100
+++ b/www/js/rec_edit.js	Thu Dec 27 22:30:26 2018 +0100
@@ -198,6 +198,52 @@
 		}
 	}
 
+/*	function GetBUGUMin() {
+
+		var Result = 0;
+
+		if (((dataRecord.st_og_max + dataRecord.st_og_min) > 0) && ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) > 0)) {
+			var G = (dataRecord.st_og_max - dataRecord.st_og_min) / ((dataRecord.st_og_max + dataRecord.st_og_min) / 2);
+			var B = (dataRecord.st_ibu_max - dataRecord.st_ibu_min) / ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) / 2);
+			if (G > B)
+				Result = ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) / 2) / (1000 * (dataRecord.st_og_max - 1));
+			else
+				Result = dataRecord.st_ibu_min / (1000 * (((dataRecord.st_og_max + dataRecord.st_og_min) / 2) - 1));
+		}
+		console.log("GetBUGUMin(): "+Result);
+		return Result;
+	}
+
+	function GetBUGUMax() {
+
+		var Result = 0;
+
+		if (((dataRecord.st_og_max + dataRecord.st_og_min) > 0) && ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) > 0) && (dataRecord.st_og_min > 0)) {
+			var G = (dataRecord.st_og_max - dataRecord.st_og_min) / ((dataRecord.st_og_max + dataRecord.st_og_min) / 2);
+			var B = (dataRecord.st_ibu_max - dataRecord.st_ibu_min) / ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) / 2);
+			if (G > B)
+				Result = ((dataRecord.st_ibu_min + dataRecord.st_ibu_max) / 2) / (1000 * (dataRecord.st_og_min - 1));
+			else
+				Result = dataRecord.st_ibu_max / (1000 * (((dataRecord.st_og_max + dataRecord.st_og_min) / 2) - 1));
+
+		}
+		console.log("GetBUGUMax(): "+Result);
+		return Result;
+	} */
+
+	function GetBUGU() {
+		var gu = (dataRecord.est_og - 1) * 1000;
+		if (gu > 0)
+			return dataRecord.est_ibu / gu;
+		else
+			return 0.5;
+	}
+
+	function GetOptClSO4ratio() {
+		var BUGU = GetBUGU();
+		return (-1.2 * BUGU + 1.4);
+	}
+
 	function setWaterAgent(name, amount) {
 		console.log("setWaterAgent(" + name + ", " + amount + ")");
 		var rows = $('#miscGrid').jqxGrid('getrows');
@@ -281,9 +327,9 @@
 	}
 
 	//Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH)
-	function ZAlkalinity(pHZ) {
+	function ZAlkalinity(pHZ, WpH) {
 		var C43 = Charge(4.3);
-		var Cw = Charge(parseFloat(dataRecord.mash_ph));
+		var Cw = Charge(WpH);
 		var Cz = Charge(pHZ);
 		var DeltaCNaught = -C43+Cw;
 		var CT = parseFloat($("#wg_total_alkalinity").jqxNumberInput('decimal')) / 50 / DeltaCNaught;
@@ -291,21 +337,108 @@
 		return CT * DeltaCZ;
 	}
 
-	function ZRA(pHZ) {
+	//Z Residual alkalinity is the amount of acid (in mEq/l) needed to bring the water in the mash to the target pH (Z pH)
+	function ZRA(pHZ, WpH) {
 
 		var Calc = parseFloat($("#wg_calcium").jqxNumberInput('decimal')) / (MMCa / 2);
 		var Magn = parseFloat($("#wg_magnesium").jqxNumberInput('decimal')) / (MMMg / 2);
-		var Z = ZAlkalinity(pHZ);
+		var Z = ZAlkalinity(pHZ, WpH);
 		return Z - (Calc / 3.5 + Magn / 7);
 	}
 
 	function ProtonDeficit(pHZ) {
 
-		var Result = ZRA(pHZ) * parseFloat($("#wg_amount").jqxNumberInput('decimal'));
-
+		var Result = ZRA(pHZ, parseFloat($("#wg_ph").jqxNumberInput('decimal'))) * parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		var rows = $('#fermentableGrid').jqxGrid('getrows');
+		for (var i = 0; i < rows.length; i++) {
+			var row = rows[i];
+			if (row.f_added == 'Mash' && row.f_graintype != 'No malt') {
+				// Check if acid is required
+				var 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);
+	//				console.log("formula C1: "+C1);
+				} else {
+					// If the acid_to_ph_5.7 is unknown from the malter, guess the required acid.
+					var ebc = row.f_color;
+					switch (row.f_graintype) {
+						case 'Base':
+						case 'Special':
+						case 'Kilned':	C1 = 0.014 * ebc - 34.192;
+								break;
+						case 'Crystal':	C1 = -0.0597 * ebc - 32.457;
+								break;
+						case 'Roast':	C1 = 0.0107 * ebc - 54.768;
+								break;
+						case 'Sour':	C1 = -149;
+								break;
+					}
+	//				console.log("Logic C1: "+C1);
+				}
+				x = C1 * (pHZ - row.f_di_ph);	// AcidRequired(ZpH)
+	//			console.log(row.f_name+" C1: "+C1+" ZpH: "+pHZ+" di_ph: "+row.f_di_ph+" acid rquired: "+x);
+				Result += x * row.f_amount;
+			}
+		}
+	//	console.log("Final: "+Result);
 		return Result;
 	}
 
+	function MashpH() {
+		var n = 0;
+		var pH = 5.4;
+		var deltapH = 0.001;
+		var deltapd = 0.1;
+		var pd = ProtonDeficit(pH);
+		while (((pd < -deltapd) || (pd > deltapd)) && (n < 1000)) {
+			n++;
+			if (pd < -deltapd)
+				pH -= deltapH;
+			else if (pd > deltapd)
+				pH += deltapH;
+			pd = ProtonDeficit(pH);
+		}
+		console.log("MashpH() n: "+n+" pH: "+pH);
+	}
+
+	function GetAcidSpecs(AT) {
+		switch(AT) {
+			case 'Melkzuur':	return {
+							pK1: 3.08,
+							pK2: 20,
+							pK3: 20,
+							MolWt: 90.08,
+							AcidSG: 1214,
+							AcidPrc: 0.88
+						};
+			case 'Zoutzuur':	return {
+							pK1: -10,
+							pK2: 20,
+							pK3: 20,
+							MolWt: 36.46,
+							AcidSG: 1142,
+							AcidPrc: 0.28
+						};
+			case 'Fosforzuur':	return {
+							pK1: 2.12,
+							pK2: 7.20,
+							pK3: 12.44,
+							MolWt: 98.00,
+							AcidSG: 1170,
+							AcidPrc: 0.25
+						};
+			case 'Zwavelzuur':	return {
+							pK1: -10,
+							pK2: 1.92,
+							pK3: 20,
+							MolWt: 98.07,
+							AcidSG: 1700,
+							AcidPrc: 0.93
+						};
+		}
+	}
+
+	// Procedure TFrmWaterAdjustment.CalcWater2;
 	function calcWater() {
 
 		console.log("calcWater()");
@@ -314,6 +447,7 @@
 		var magnesium = 0;
 		var sodium = 0;
 		var total_alkalinity = 0;
+		var bicarbonate = 0;
 		var chloride = 0;
 		var sulfate = 0;
 		var ph = 0;
@@ -329,27 +463,30 @@
 		var AcidPrc = 0;
 		var protonDeficit = 0;
 
-//		console.log((dataRecord.w1_name != "") + " " + (dataRecord.w2_name != ""));
-		if (dataRecord.w1_name != "") {
-			if (dataRecord.w2_name != "") {
-				liters = dataRecord.w1_amount + dataRecord.w2_amount;
-				calcium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_calcium, dataRecord.w2_calcium);
-				magnesium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_magnesium, dataRecord.w2_magnesium);
-				sodium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sodium, dataRecord.w2_sodium);
-				chloride = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_chloride, dataRecord.w2_chloride);
-				sulfate = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sulfate, dataRecord.w2_sulfate);
-				total_alkalinity = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_total_alkalinity, dataRecord.w2_total_alkalinity);
-				ph = -Math.log10(((Math.pow(10, -dataRecord.w1_ph) * dataRecord.w1_amount) + (Math.pow(10, -dataRecord.w2_ph) * dataRecord.w2_amount))  / liters);
-			} else {
-				liters = dataRecord.w1_amount;
-				calcium = dataRecord.w1_calcium;
-				magnesium = dataRecord.w1_magnesium;
-				sodium = dataRecord.w1_sodium;
-				chloride = dataRecord.w1_chloride;
-				sulfate = dataRecord.w1_sulfate;
-				total_alkalinity = dataRecord.w1_total_alkalinity;
-				ph = dataRecord.w1_ph;
-			}
+		if (dataRecord.w1_name == "") {
+			return;
+		}
+		// Check for a recipe too. Fermentables, Mash schedule?
+
+		// If there is a dillute water source, mix the waters.
+		if (dataRecord.w2_name != "") {
+			liters = dataRecord.w1_amount + dataRecord.w2_amount;
+			calcium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_calcium, dataRecord.w2_calcium);
+			magnesium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_magnesium, dataRecord.w2_magnesium);
+			sodium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sodium, dataRecord.w2_sodium);
+			chloride = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_chloride, dataRecord.w2_chloride);
+			sulfate = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sulfate, dataRecord.w2_sulfate);
+			total_alkalinity = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_total_alkalinity, dataRecord.w2_total_alkalinity);
+			ph = -Math.log10(((Math.pow(10, -dataRecord.w1_ph) * dataRecord.w1_amount) + (Math.pow(10, -dataRecord.w2_ph) * dataRecord.w2_amount))  / liters);
+		} else {
+			liters = dataRecord.w1_amount;
+			calcium = dataRecord.w1_calcium;
+			magnesium = dataRecord.w1_magnesium;
+			sodium = dataRecord.w1_sodium;
+			chloride = dataRecord.w1_chloride;
+			sulfate = dataRecord.w1_sulfate;
+			total_alkalinity = dataRecord.w1_total_alkalinity;
+			ph = dataRecord.w1_ph;
 		}
 		$('#wg_amount').val(liters);
 		$('#wg_calcium').val(Math.round(calcium * 10) / 10);
@@ -360,39 +497,211 @@
 		$('#wg_sulfate').val(Math.round(sulfate * 10) / 10);
 		// Note: brouwhulp has the malts included here in the result.
 		$('#wg_ph').val(Math.round(ph * 10) / 10);
+		$('#wb_ph').val(Math.round(ph * 10) / 10);
+		bicarbonate = total_alkalinity * 1.22;
 
 		// 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 / parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		calcium += 1000 * RA / liters;
 
 		// Calculate Mg
 		RA = parseFloat($("#wa_mgso4").jqxNumberInput('decimal')) * MMMg / MMMgSO4;
-		magnesium += 1000 * RA / parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		magnesium += 1000 * RA / liters;
 
 		// Calculate Na
-		RA = parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMNa / MMNaCl +
-		     parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMNa / MMNaHCO3;
-		sodium += 1000 * RA / parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		RA = parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMNa / MMNaCl;
+		sodium += 1000 * RA / liters;
 
 		// Calculate SO4
 		RA = parseFloat($("#wa_caso4").jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 +
 		     parseFloat($("#wa_mgso4").jqxNumberInput('decimal')) * MMSO4 / MMMgSO4;
-		sulfate += 1000 * RA / parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		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 / parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		chloride += 1000 * RA / liters;
 		// Einde noot.
 
+		var AT = $("#wa_acid_name").val();
+		var BT = $("#wa_base_name").val();
+
+		var result = GetAcidSpecs(AT);
+		pK1 = result.pK1;
+		pK2 = result.pK2;
+		pK3 = result.pK3;
+		MolWt = result.MolWt;
+		AcidSG = result.AcidSG;
+		AcidPrc = result.AcidPrc;
+		console.log(AT+" pK1: "+pK1+" pK2: "+pK2+" pK3: "+pK3+" MolWt: "+MolWt+" AcidSG: "+AcidSG+" AcidPrc: "+AcidPrc);
+
+		if (dataRecord.calc_acid) {
+			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);
+				if ($("#wa_acid_name").val() == "") {
+					$("#wa_acid_name").val('Melkzuur');
+					last_acid = 'Melkzuur';
+				}
+				frac = CalcFrac(TpH, pK1, pK2, pK3);
+				Acid = protonDeficit / frac;
+				console.log("Required moles: "+Acid);
+				Acid *= MolWt; // mg
+	//			Acidmg = Acid;
+	//			console.log("Required mg: "+Acidmg);
+				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(AT, Math.round(Acid * 100) / 100);
+
+				bicarbonate = bicarbonate - protonDeficit * frac / liters;
+				total_alkalinity = bicarbonate * 50 / 61;
+			} else if (protonDeficit < 0) { //Add base
+				$("#wa_acid").val(0);
+				setWaterAgent(last_acid, 0);
+				if ($("#wa_base_name").val() == "") {
+					$("#wa_base_name").val('NaHCO3');
+					last_base = 'NaHCO3';
+				}
+				r1d = Math.pow(10, (TpH - 6.38));
+				r2d = Math.pow(10, (TpH - 10.38));
+				f1d = 1 / (1 + r1d + r1d * r2d);
+				f2d = f1d * r1d;
+				f3d = f2d * r2d;
+				switch (BT) {
+					case 'NaHCO3':	base = -protonDeficit / (f1d - f3d); //mmol totaal
+							base = base * MMNaHCO3/1000; //gram
+							$("#wa_base").val(Math.round(base * 100) / 100);
+							setWaterAgent(BT, Math.round(base * 100) / 100);
+							if (liters > 0) {
+								// Na
+								RA = parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMNa / MMNaCl +
+								     parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMNa / MMNaHCO3;
+								RA = 1000 * RA / liters;
+								sodium = parseFloat($('#wg_sodium').jqxNumberInput('decimal')) + RA;
+								// HCO3
+								RA = parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3;
+								RA = 1000 * RA / liters;
+								bicarbonate = (parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')) * 1.22) + RA;
+								total_alkalinity = bicarbonate * 50 / 61;
+							}
+							break;
+					case 'Na2CO3':	base = -protonDeficit / (2 * f1d + f2d); //mmol totaal
+							base = base * MMNa2CO3/1000; //gram
+							$("#wa_base").val(Math.round(base * 100) / 100);
+							setWaterAgent(BT, Math.round(base * 100) / 100);
+							if (liters > 0) {
+								RA = parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMNa / MMNaCl +
+								     parseFloat($("#wa_base").jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3;
+								RA = 1000 * RA / liters;
+								sodium = parseFloat($('#wg_sodium').jqxNumberInput('decimal')) + RA;
+								// HCO3
+								RA = parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3;
+								RA = 1000 * RA / liters;
+								bicarbonate = (parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')) * 1.22) + RA;
+								total_alkalinity = bicarbonate * 50 / 61;
+							}
+							break;
+					case 'CaCO3':	base = -protonDeficit * (f1d - f3d); //mmol totaal
+							base = base * MMCaCO3/1000; //gram
+							//but only 1/3 is effective, so add 3 times as much
+							base = 3 * base;
+							$("#wa_base").val(Math.round(base * 100) / 100);
+							setWaterAgent(BT, Math.round(base * 100) / 100);
+							if (liters > 0) {
+								//Bicarbonate
+								RA = parseFloat($("#wa_base").jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3;
+								RA = 1000 * RA / liters;
+								bicarbonate = (parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')) * 1.22) + 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 = parseFloat($('#wg_calcium').jqxNumberInput('decimal')) + RA;
+							}
+							break;
+					case 'Ca(OH)2':	base = -protonDeficit / 19.3; // g
+							$("#wa_base").val(Math.round(base * 100) / 100);
+							setWaterAgent(BT, Math.round(base * 100) / 100);
+							if (liters > 0) {
+								// Bicarbonate
+								RA = -protonDeficit / liters;
+								total_alkalinity = parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')) + 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 = parseFloat($('#wg_calcium').jqxNumberInput('decimal')) + RA;
+							}
+							break;
+				}
+			}
+			ph = TpH;
+			$('#wb_ph').val(Math.round(ph * 10) / 10);
+		} else { // Manual
+			console.log("calc_acid no");
+			// First add base salts
+			if (parseFloat($("#wa_base").jqxNumberInput('decimal')) > 0) {
+
+			}
+
+			TpH = parseFloat(dataRecord.mash_ph);
+			pHa = parseFloat($('#wg_ph').jqxNumberInput('decimal'));
+			//pHa = parseFloat(dataRecord.mash_ph);
+			// 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 = Acid * AcidSG; // ml
+				Acid = Acid / MolWt;  // mg
+
+				//find the pH where the protondeficit = protondeficit by the acid
+				frac = CalcFrac(pHa, pK1, pK2, pK3);
+				protonDeficit = Acid * frac;
+
+				deltapH = 0.01;
+				deltapd = 0.1;
+				pd = ProtonDeficit(TpH);
+				n = 0;
+				console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
+
+				while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 1000)) {
+					n++;
+					if (pd < (protonDeficit-deltapd))
+						pHa = pHa - deltapH;
+					else if (pd > (protonDeficit+deltapd))
+						pHa = pHa + deltapH;
+					frac = CalcFrac(pHa, pK1, pK2, pK3);
+					protonDeficit = Acid * frac;
+					pd = ProtonDeficit(pHa);
+			//		console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
+				}
+				console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
+
+			}
+		} 
+/*
 		TpH = parseFloat(dataRecord.mash_ph);
 		if (TpH < 5.0 || TpH > 6.0) {
 			TpH = 5.4;
 			dataRecord.mash_ph = 5.4;
 			$("#mash_ph").val(5.4);
-			$("#tgt_mash_ph").val(5.4);
 		}
 		var acid_amount = parseFloat($("#wa_acid").jqxNumberInput('decimal'));
 		var acid_perc = parseFloat($("#wa_acid_perc").jqxNumberInput('decimal'));
@@ -443,9 +752,58 @@
 						sulfate += Acidmg / 1000 * MMSO4 / (MMSO4 + 2);
 						break;
 		}
-		protonDeficit = ProtonDeficit(TpH);
-		console.log("frac: "+frac+"  acid: "+acid+"  protonDeficit: "+protonDeficit);
+
+		if (dataRecord.calc_acid) {
+		} else if (liters > 0) {  // not calc_acid
+			// First add base salts
+			if (parseFloat($("#wa_base").jqxNumberInput('decimal')) > 0) {
+
+			}
+
+			pHa = parseFloat($("#wb_ph").jqxNumberInput('decimal'));
+			console.log("Adjusted water mash pH: "+pHa);
+			// Then calculate the new pH with added acids
+			if (parseFloat($("#wa_acid").jqxNumberInput('decimal')) > 0) {
+				acid = parseFloat($("#wa_acid").jqxNumberInput('decimal'));
+				if (parseFloat($("#wa_acid_perc").jqxNumberInput('decimal')) == 0)
+					$("#wa_acid_perc").val(AcidPrc);
+				console.log("screen value: "+acid);
+				acid = acid / AcidPrc * (parseFloat($("#wa_acid_perc").jqxNumberInput('decimal')) / 100); // ml
+				console.log("acid ml: "+acid);
+				acid = acid * AcidSG // ml
+				console.log("acid ml: "+acid);
+				acid = acid / MolWt;  // mg
+				console.log("acid mg: "+acid);
+				var Acidmg = acid;
+
+				frac = CalcFrac(pHa, pK1, pK2, pK3);
+				protonDeficit = acid * frac;
+
+				deltapH = 0.001;
+				deltapd = 0.1;
+				pd = ProtonDeficit(pHa);
+				n = 0;
+				console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
+
+				while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 1000)) {
+					n++;
+					if (pd < (protonDeficit-deltapd))
+						pHa = pHa - deltapH;
+					else if (pd > (protonDeficit+deltapd))
+						pHa = pHa + deltapH;
+					frac = CalcFrac(pHa, pK1, pK2, pK3);
+					protonDeficit = acid * frac;
+					pd = ProtonDeficit(pHa);
+					console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
+				}
+				console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
+			}
+		}
 		total_alkalinity -= 50 / 61 * protonDeficit * frac / liters;
+		MashpH();
+*/
+		$('#tgt_bu').val(Math.round(GetBUGU() * 100) / 100);
+		$('#tgt_cl_so4').val(Math.round(GetOptClSO4ratio() * 10) / 10);
 
                 $('#wb_calcium').val(Math.round(calcium * 10) / 10);
                 $('#wb_magnesium').val(Math.round(magnesium * 10) / 10);
@@ -481,6 +839,13 @@
 		} else {
 			setRangeIndicator("sulfate", "high");
 		}
+		if (ph < 5.2) {
+			setRangeIndicator("ph", "low");
+		} else if (ph > 5.6) {
+			setRangeIndicator("ph", "high");
+		} else {
+			setRangeIndicator("ph", "normal");
+		}
 	}
 
 	function calcFermentablesFromOG(OG) {
@@ -520,6 +885,14 @@
 	function calcInit () {
 		console.log("calc.init()");
 
+		$("#calc_acid").on('checked', function (event) {
+			dataRecord.calc_acid = true;
+			calcWater();
+		});
+		$("#calc_acid").on('unchecked', function (event) {
+			dataRecord.calc_acid = false;
+			calcWater();
+		});
 		$("#w1_name").jqxDropDownList('selectItem', dataRecord.w1_name);
 		$("#w2_name").jqxDropDownList('selectItem', dataRecord.w2_name);
 		// Fix tap water if zero using mash infuse amount.
@@ -632,7 +1005,6 @@
 		});
 		$('#mash_ph').on('change', function (event) {
 			dataRecord.mash_ph = parseFloat(event.args.value);
-			$("#tgt_mash_ph").val(parseFloat(event.args.value));
 			calcWater();
 		});
 	};
@@ -715,6 +1087,7 @@
 	$("#st_abv_max").jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'});
 	$("#st_carb_min").jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'});
 	$("#st_carb_max").jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'});
+	$("#mash_ph").jqxTooltip({ content: 'Maisch pH tussen 5.2 en 5.6. Gebruik 5.2 voor lichte en 5.5 voor donkere bieren.'});
 	$("#wa_cacl2").jqxTooltip({ content: 'Voor het maken van een ander waterprofiel. Voegt calcium en chloride toe. Voor het verbeteren van zoetere bieren.'});
 	$("#wa_caso4").jqxTooltip({ content: 'Gips. Voor het maken van een ander waterprofiel. Voegt calcium en sulfaat toe. Voor het verbeteren van bittere bieren.'});
 	$("#wa_mgso4").jqxTooltip({ content: 'Epsom zout. Voor het maken van een ander waterprofiel. Voegt magnesium en sulfaat toe. Gebruik spaarzaam!'});
@@ -847,7 +1220,6 @@
 			$("#st_carb_max").val(dataRecord.st_carb_max);
 			$("#mash_name").val(dataRecord.mash_name);
 			$("#mash_ph").val(dataRecord.mash_ph);
-			$("#tgt_mash_ph").val(dataRecord.mash_ph);
 			$("#sparge_temp").val(dataRecord.sparge_temp);
 			$("#sparge_ph").val(dataRecord.sparge_ph);
 			$("#sparge_volume").val(dataRecord.sparge_volume);
@@ -916,7 +1288,8 @@
 				{ name: 'f_add_after_boil', type: 'bool' },
 				{ name: 'f_adjust_to_total_100', type: 'bool' },
 				{ name: 'f_percentage', type: 'float' },
-				{ name: 'f_di_ph', type: 'float' }
+				{ name: 'f_di_ph', type: 'float' },
+				{ name: 'f_acid_to_ph_57', type: 'float' }
 			],
 			addrow: function (rowid, rowdata, position, commit) {
 				commit(true);
@@ -998,6 +1371,7 @@
 							row["f_percentage"] = 0;
 						}
 						row["f_di_ph"] = datarecord.di_ph;
+						row["f_acid_to_ph_57"] = datarecord.acid_to_ph_57;
 						var commit = $("#fermentableGrid").jqxGrid('addrow', null, row);
 					}
 				});
@@ -2010,7 +2384,6 @@
 
 	$("#mash_name").jqxInput({ theme: theme, width: 320, height: 23 });
 	$("#mash_ph").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 4, max: 8, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
-	$("#tgt_mash_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, readOnly: true });
 	$("#sparge_temp").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 70, max: 98, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.5 });
 	// Several gauges
 	$("#hop_flavour").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
@@ -2153,10 +2526,13 @@
 	$("#pr_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
 	$("#pr_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
 
-	$("#wa_cacl2").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
-	$("#wa_caso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
-	$("#wa_mgso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
-	$("#wa_nacl").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+	$("#tgt_bu").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 2, readOnly: true });
+	$("#tgt_cl_so4").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+
+	$("#wa_cacl2").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+	$("#wa_caso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+	$("#wa_mgso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+	$("#wa_nacl").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
 
 	$("#calc_acid").jqxCheckBox({ theme: theme, width: 120, height: 23 });
 	$("#wa_base_name").jqxDropDownList({ theme: theme, source: srcBase, width: 125, height: 23, dropDownHeight: 128 });

mercurial