www/js/rec_edit.js

changeset 156
35860890224c
parent 155
041af8a82d77
child 158
3b9e4dfb0476
--- a/www/js/rec_edit.js	Fri Dec 28 23:18:42 2018 +0100
+++ b/www/js/rec_edit.js	Sun Dec 30 18:18:22 2018 +0100
@@ -306,6 +306,11 @@
 		return 0;
 	}
 
+	// mg/l as CaCO3
+	function ResidualAlkalinity(total_alkalinity, calcium, magnesium) {
+		return total_alkalinity - (calcium / 1.4 + magnesium / 1.7);
+	}
+
 	var Ka1 = 0.0000004445;
 	var Ka2 = 0.0000000000468;
 
@@ -324,9 +329,10 @@
 	}
 
 	//Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH)
-	function ZAlkalinity(pHZ, WpH) {
+	function ZAlkalinity(pHZ) {
 		var C43 = Charge(4.3);
-		var Cw = Charge(WpH);
+		//var Cw = Charge(parseFloat($("#wg_ph").jqxNumberInput('decimal')));
+		var Cw = Charge(7.0);
 		var Cz = Charge(pHZ);
 		var DeltaCNaught = -C43+Cw;
 		var CT = parseFloat($("#wg_total_alkalinity").jqxNumberInput('decimal')) / 50 / DeltaCNaught;
@@ -335,17 +341,18 @@
 	}
 
 	//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) {
+	function ZRA(pHZ) {
 
 		var Calc = parseFloat($("#wg_calcium").jqxNumberInput('decimal')) / (MMCa / 2);
 		var Magn = parseFloat($("#wg_magnesium").jqxNumberInput('decimal')) / (MMMg / 2);
-		var Z = ZAlkalinity(pHZ, WpH);
+		var Z = ZAlkalinity(pHZ);
 		return Z - (Calc / 3.5 + Magn / 7);
 	}
 
 	function ProtonDeficit(pHZ) {
 
-		var Result = ZRA(pHZ, parseFloat($("#wg_ph").jqxNumberInput('decimal'))) * parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		var Result = ZRA(pHZ) * parseFloat($("#wg_amount").jqxNumberInput('decimal'));
+		// proton deficit for the grist
 		var rows = $('#fermentableGrid').jqxGrid('getrows');
 		for (var i = 0; i < rows.length; i++) {
 			var row = rows[i];
@@ -354,9 +361,8 @@
 				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.
+					// If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid.
 					var ebc = row.f_color;
 					switch (row.f_graintype) {
 						case 'Base':
@@ -370,7 +376,6 @@
 						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);
@@ -502,7 +507,6 @@
 		// Note: brouwhulp has the malts included here in the result.
 		var wg_ph = ph;
 		$('#wg_ph').val(Math.round(ph * 10) / 10);
-//		$('#wb_ph').val(Math.round(ph * 10) / 10);
 		$('#wb_ph').val(Math.round(MashpH() * 10) / 10);
 		bicarbonate = total_alkalinity * 1.22;
 		var wg_bicarbonate = bicarbonate;
@@ -562,10 +566,8 @@
 				setWaterAgent(last_base, 0);
 				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)
@@ -601,6 +603,7 @@
 								RA = 1000 * RA / liters;
 								bicarbonate = wg_bicarbonate + RA;
 								total_alkalinity = bicarbonate * 50 / 61;
+								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
 							}
 							break;
 					case 'Na2CO3':	base = -protonDeficit / (2 * f1d + f2d); //mmol totaal
@@ -617,6 +620,7 @@
 								RA = 1000 * RA / liters;
 								bicarbonate = wg_bicarbonate + RA;
 								total_alkalinity = bicarbonate * 50 / 61;
+								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
 							}
 							break;
 					case 'CaCO3':	base = -protonDeficit * (f1d - f3d); //mmol totaal
@@ -637,6 +641,7 @@
 								     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 'Ca(OH)2':	base = -protonDeficit / 19.3; // g
@@ -653,6 +658,7 @@
 								     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;
 				}
@@ -663,7 +669,46 @@
 			console.log("calc_acid no");
 			// First add base salts
 			if (parseFloat($("#wa_base").jqxNumberInput('decimal')) > 0) {
-
+				if (liters > 0) {
+					switch (BT) {
+						case 'NaHCO3':	// 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 'Na2CO3':	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 'CaCO3':	// 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;
+					}
+				}
 			}
 
 			TpH = parseFloat(dataRecord.mash_ph);
@@ -724,8 +769,18 @@
 			chloride = wg_chloride + RA;
 		}
 
+		// 2:1 Sulfate to Chroride IPA's, Pale Ales.
+		// 1:1 Sulfate to Chloride Balanced
+		// 1:2 Sulfate to Chloride Malty
 		$('#tgt_bu').val(Math.round(GetBUGU() * 100) / 100);
 		$('#tgt_cl_so4').val(Math.round(GetOptClSO4ratio() * 10) / 10);	// Show real value too
+		if (sulfate > 0)
+			RA = chloride / sulfate;
+		else
+			RA = 10;
+		var piCLSO4_low = 0.8 * GetOptClSO4ratio();
+		var piCLSO4_high = 1.2 * GetOptClSO4ratio();
+		console.log("low: "+piCLSO4_low+" val: "+RA+" high: "+piCLSO4_high);
 
                 $('#wb_calcium').val(Math.round(calcium * 10) / 10);
                 $('#wb_magnesium').val(Math.round(magnesium * 10) / 10);
@@ -768,6 +823,98 @@
 		} else {
 			setRangeIndicator("ph", "normal");
 		}
+		calcSparge();
+	}
+
+	function calcSparge() {
+
+		// Code from BrewBuddy/Brouwhulp, who got it from http://www.brewery.org/brewery/library/Acidi0,00fWaterAJD0497.html
+		var TargetpH = dataRecord.sparge_ph;
+		var Source_pH = dataRecord.w1_ph;
+		var Source_alkalinity = dataRecord.w1_total_alkalinity;
+		if (dataRecord.sparge_source == 'Bron 2') {
+			if (dataRecord.w2_ph > 0.0) {
+				Source_pH = dataRecord.w2_ph;
+				Source_alkalinity = dataRecord.w2_total_alkalinity;
+			} else {
+				dataRecord.sparge_source = 'Bron 1';
+				$("#sparge_source").val('Bron 1');
+			}
+		} else if (dataRecord.sparge_source == 'Gemengd') {
+			if (dataRecord.w2_ph > 0.0) {
+				Source_pH = parseFloat($("#wg_ph").jqxNumberInput('decimal'));
+				Source_alkalinity = parseFloat($("#wg_total_alkalinity").jqxNumberInput('decimal'));
+			} else {
+				dataRecord.sparge_source = 'Bron 1';
+				$("#sparge_source").val('Bron 1');
+			}
+		}
+
+		console.log("calcSparge() target pH: "+TargetpH+" Source: "+Source_pH+" alkalinity: "+Source_alkalinity);
+
+		// 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.33);
+		d = 1 + r1 + r1*r2;
+		f1 = 1/d;
+		f2 = r1/d;
+		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.33);
+		d43 = 1 + r143 + r143*r243;
+		f143 = 1/d43;
+		f243 = r143 / d43;
+		f343 = r143 * r243 / d43;
+
+		//Step 3. Convert the sample alkalinity to milliequivalents/L
+		alkalinity = Source_alkalinity / 50;
+		//Step 4. Solve
+		alkalinity = alkalinity / ((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.33);
+		dg = 1 + r1g + r1g*r2g;
+		f1g = 1/dg;
+		f2g = r1g / dg;
+		f3g = r1g * r2g / dg;
+
+		//Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L)
+		Acid = alkalinity * ((f1g-f1)+(f3-f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH);  //mEq/l
+
+		if ($("#sparge_acid_type").val() == "") {
+			$("#sparge_acid_type").val('Melkzuur');
+			dataRecord.sparge_acid_type = 'Melkzuur';
+		}
+		AT = dataRecord.sparge_acid_type;
+		var 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);
+
+		//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 = Math.round(Acid * 100) / 100;
+		dataRecord.sparge_acid_amount = Acid / 1000;
+		$("#sparge_acid_amount").val(Acid);
+		console.log("acid needed: "+Acid);
 	}
 
 	function calcFermentablesFromOG(OG) {
@@ -929,6 +1076,28 @@
 			dataRecord.mash_ph = parseFloat(event.args.value);
 			calcWater();
 		});
+
+		$('#sparge_ph').on('change', function (event) {
+			dataRecord.sparge_ph = parseFloat(event.args.value);
+			calcSparge();
+		});
+		$('#sparge_volume').on('change', function (event) {
+			dataRecord.sparge_volume = parseFloat(event.args.value);
+			calcSparge();
+		});
+		$('#sparge_source').on('change', function (event) {
+			dataRecord.sparge_source= event.args.item.value;
+			calcSparge();
+		});
+		$('#sparge_acid_type').on('change', function (event) {
+			dataRecord.sparge_acid_type = event.args.item.value;
+			console.log("new sparge_acid_type: "+dataRecord.sparge_acid_type);
+			calcSparge();
+		});
+		$('#sparge_acid_perc').on('change', function (event) {
+			dataRecord.sparge_acid_perc = parseFloat(event.args.value);
+			calcSparge();
+		});
 	};
 
 	$("#styleSelect").jqxDropDownList({
@@ -1064,6 +1233,7 @@
 			{ name: 'sparge_temp', type: 'float' },
 			{ name: 'sparge_ph', type: 'float' },
 			{ name: 'sparge_volume', type: 'float' },
+			{ name: 'sparge_source', type: 'string' },
 			{ name: 'sparge_acid_type', type: 'string' },
 			{ name: 'sparge_acid_perc', type: 'float' },
 			{ name: 'sparge_acid_amount', type: 'float' },
@@ -1145,9 +1315,10 @@
 			$("#sparge_temp").val(dataRecord.sparge_temp);
 			$("#sparge_ph").val(dataRecord.sparge_ph);
 			$("#sparge_volume").val(dataRecord.sparge_volume);
+			$("#sparge_source").val(dataRecord.sparge_source);
 			$("#sparge_acid_type").val(dataRecord.sparge_acid_type);
 			$("#sparge_acid_perc").val(dataRecord.sparge_acid_perc);
-			$("#sparge_acid_amount").val(dataRecord.sparge_acid_amount);
+			$("#sparge_acid_amount").val(dataRecord.sparge_acid_amount * 1000);
 			$("#calc_acid").val(dataRecord.calc_acid);
 			$("#w1_name").val(dataRecord.w1_name);
 			$("#w1_amount").val(dataRecord.w1_amount);
@@ -2262,6 +2433,7 @@
 	var srcIBU = [ "Tinseth", "Rager", "Daniels" ];	// Only these are supported at this time.
 	var srcBase = [ "NaHCO3", "Na2CO3", "CaCO3", "Ca(OH)2" ];
 	var srcAcid = [ "Melkzuur", "Zoutzuur", "Fosforzuur", "Zwavelzuur" ];
+	var srcSource = [ "Bron 1", "Bron 2", "Gemengd" ];
 	$("#name").jqxInput({ theme: theme, width: 640, height: 23 });
 	$("#notes").jqxInput({ theme: theme, width: 960, height: 200 });
 	$("#st_name").jqxInput({ theme: theme, width: 250, height: 23 });
@@ -2306,7 +2478,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 });
-	$("#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 });
 	$("#hop_aroma").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
@@ -2457,15 +2628,19 @@
 	$("#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 });
+	$("#wa_base_name").jqxDropDownList({ theme: theme, source: srcBase, width: 100, height: 23, dropDownHeight: 128 });
 	$("#wa_base").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 2, spinButtons: true, spinButtonsStep: 0.05, symbol: ' gr', symbolPosition: 'right' });
-	$("#wa_acid_name").jqxDropDownList({ theme: theme, source: srcAcid, width: 125, height: 23, dropDownHeight: 128 })
+	$("#wa_acid_name").jqxDropDownList({ theme: theme, source: srcAcid, width: 100, height: 23, dropDownHeight: 128 });
 	$("#wa_acid").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 2, spinButtons: true, spinButtonsStep: 0.05, symbol: ' ml', symbolPosition: 'right' });
 	$("#wa_acid_perc").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 80, height: 23, min: 0, max: 100, decimalDigits: 0, spinButtons: true, symbol: '%', symbolPosition: 'right' });
 
+	$("#sparge_temp").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 70, max: 98, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.5 });
 	$("#sparge_volume").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
 	$("#sparge_ph").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
-	$("#sparge_acid_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 5, readOnly: true });
+	$("#sparge_source").jqxDropDownList({ theme: theme, source: srcSource, width: 100, height: 23, dropDownHeight: 95 });
+	$("#sparge_acid_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 2, readOnly: true, symbol: ' ml', symbolPosition: 'right' });
+	$("#sparge_acid_type").jqxDropDownList({ theme: theme, source: srcAcid, width: 100, height: 23, dropDownHeight: 128 });
+	$("#sparge_acid_perc").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, spinButtons: true, decimalDigits: 0, symbol: '%', symbolPosition: 'right' });
 
 	// Tabs inside the popup window.
 	$('#jqxTabs').jqxTabs({
@@ -2557,9 +2732,10 @@
 			sparge_temp: parseFloat($("#sparge_temp").jqxNumberInput('decimal')),
 			sparge_ph: parseFloat($("#sparge_ph").jqxNumberInput('decimal')),
 			sparge_volume: parseFloat($("#sparge_volume").jqxNumberInput('decimal')),
-		//	sparge_acid_type: $("#sparge_acid_type").val(),
-		//	sparge_acid_perc: parseFloat($("#sparge_acid_perc").jqxNumberInput('decimal')),
-		//	sparge_acid_amount: parseFloat($("#sparge_acid_amount").jqxNumberInput('decimal')),
+			sparge_source: $("#sparge_source").val(),
+			sparge_acid_type: $("#sparge_acid_type").val(),
+			sparge_acid_perc: parseFloat($("#sparge_acid_perc").jqxNumberInput('decimal')),
+			sparge_acid_amount: dataRecord.sparge_acid_amount,
 			calc_acid: $("#calc_acid").val(),
 			w1_name: $("#w1_name").val(),
 			w1_amount: parseFloat($("#w1_amount").jqxNumberInput('decimal')),

mercurial