src/EditRecipeTab7.cpp

changeset 135
e68b27ad8a40
parent 134
5099df8ba6c6
child 136
17030224d919
equal deleted inserted replaced
134:5099df8ba6c6 135:e68b27ad8a40
27 //ui->mw_acidpercEdit->setValue(recipe-> 27 //ui->mw_acidpercEdit->setValue(recipe->
28 28
29 } 29 }
30 30
31 31
32 /*
33 * Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH)
34 */
35 double EditRecipe::ZAlkalinity(double pHZ)
36 {
37 double C43 = Utils::Charge(4.3);
38 double Cw = Utils::Charge(recipe->wg_ph);
39 double Cz = Utils::Charge(pHZ);
40 double DeltaCNaught = -C43 + Cw;
41 double CT = recipe->wg_total_alkalinity / 50 / DeltaCNaught;
42 double DeltaCZ = -Cz + Cw;
43 return CT * DeltaCZ;
44 }
45
46
47 /*
48 * Z Residual alkalinity is the amount of acid (in mEq/l) needed
49 * to bring the water in the mash to the target pH (Z pH).
50 */
51 double EditRecipe::ZRA(double pHZ)
52 {
53 double Calc = recipe->wg_calcium / (MMCa / 2);
54 double Magn = recipe->wg_magnesium / (MMMg / 2);
55 double Z = ZAlkalinity(pHZ);
56 return Z - (Calc / 3.5 + Magn / 7);
57 }
58
59
60 double EditRecipe::BufferCapacity(Fermentables F)
61 {
62 double C1 = 0;
63
64 if ((F.f_di_ph != 5.7) && ((F.f_acid_to_ph_57 < - 0.1) || (F.f_acid_to_ph_57 > 0.1))) {
65 C1 = F.f_acid_to_ph_57 / (F.f_di_ph - 5.7);
66 } else {
67 /*
68 * If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid.
69 */
70 switch (F.f_graintype) {
71 case 0: // Base, Special, Kilned
72 case 3:
73 case 5: C1 = 0.014 * F.f_color - 34.192;
74 break;
75 case 2: C1 = -0.0597 * F.f_color - 32.457; // Crystal
76 break;
77 case 1: C1 = 0.0107 * F.f_color - 54.768; // Roast
78 break;
79 case 4: C1 = -149; // Sour malt
80 break;
81 }
82 }
83 return C1;
84 }
85
86
87 double EditRecipe::AcidRequired(double ZpH, Fermentables F)
88 {
89 double C1 = BufferCapacity(F);
90 double x = F.f_di_ph;
91 return C1 * (ZpH - x);
92 }
93
94
95 double EditRecipe::ProtonDeficit(double pHZ)
96 {
97 double C1, x;
98 int i, error_count = 0;
99 double Result = ZRA(pHZ) * recipe->wg_amount;
100 Fermentables F;
101
102 /*
103 * proton deficit for the grist
104 */
105 if (recipe->fermentables.size()) {
106 for (i = 0; i < recipe->fermentables.size(); i++) {
107 F = recipe->fermentables.at(i);
108 if (F.f_added == 0 && F.f_graintype != 6) { // Added == Mash && graintype != No Malt
109 x = AcidRequired(pHZ, F) * F.f_amount;
110 Result += x;
111 }
112 }
113 } else {
114 error_count++;
115 if (error_count < 5)
116 qDebug() << "ProtonDeficit" << pHZ << "invalid grist, return" << Result;
117 }
118 return Result;
119 }
120
121
122 double EditRecipe::MashpH()
123 {
124 int n = 0;
125 double pH = 5.4;
126 double deltapH = 0.001;
127 double deltapd = 0.1;
128 double pd = ProtonDeficit(pH);
129
130 while (((pd < -deltapd) || (pd > deltapd)) && (n < 2000)) {
131 n++;
132 if (pd < -deltapd)
133 pH -= deltapH;
134 else if (pd > deltapd)
135 pH += deltapH;
136 pd = ProtonDeficit(pH);
137 }
138 pH = round(pH * 1000000) / 1000000.0;
139 qDebug() << "MashpH() n:" << n << "pH:" << pH;
140 return pH;
141 }
142
143
32 void EditRecipe::calcWater() 144 void EditRecipe::calcWater()
33 { 145 {
34 double liters = 0; 146 double liters = 0;
35 double calcium = 0; 147 double calcium = 0;
36 double magnesium = 0; 148 double magnesium = 0;
38 double total_alkalinity = 0; 150 double total_alkalinity = 0;
39 double bicarbonate = 0; 151 double bicarbonate = 0;
40 double chloride = 0; 152 double chloride = 0;
41 double sulfate = 0; 153 double sulfate = 0;
42 double ph = 0; 154 double ph = 0;
155 double TpH = 0;
156 double frac;
157 double protonDeficit = 0;
158 double Acid = 0, Acidmg = 0;
159 int AT;
43 160
44 qDebug() << "calcWater"; 161 qDebug() << "calcWater";
45 162
46 /* 163 /*
47 * If there is a dilute water source, mix the waters. 164 * If there is a dilute water source, mix the waters.
71 recipe->wg_magnesium = round(magnesium * 10.0) / 10.0; 188 recipe->wg_magnesium = round(magnesium * 10.0) / 10.0;
72 recipe->wg_sodium = round(sodium * 10.0) / 10.0; 189 recipe->wg_sodium = round(sodium * 10.0) / 10.0;
73 recipe->wg_chloride = round(chloride * 10.0) / 10.0; 190 recipe->wg_chloride = round(chloride * 10.0) / 10.0;
74 recipe->wg_sulfate = round(sulfate * 10.0) / 10.0; 191 recipe->wg_sulfate = round(sulfate * 10.0) / 10.0;
75 recipe->wg_total_alkalinity = round(total_alkalinity * 10.0) / 10.0; 192 recipe->wg_total_alkalinity = round(total_alkalinity * 10.0) / 10.0;
193 recipe->wg_ph = ph;
76 194
77 ui->wg_volEdit->setValue(liters); 195 ui->wg_volEdit->setValue(liters);
78 ui->wg_caEdit->setValue(calcium); 196 ui->wg_caEdit->setValue(calcium);
79 ui->wg_mgEdit->setValue(magnesium); 197 ui->wg_mgEdit->setValue(magnesium);
80 ui->wg_hco3Edit->setValue(total_alkalinity * 1.22); 198 ui->wg_hco3Edit->setValue(total_alkalinity * 1.22);
91 double wg_total_alkalinity = total_alkalinity; 209 double wg_total_alkalinity = total_alkalinity;
92 double wg_chloride = chloride; 210 double wg_chloride = chloride;
93 double wg_sulfate = sulfate; 211 double wg_sulfate = sulfate;
94 double wg_bicarbonate = bicarbonate; 212 double wg_bicarbonate = bicarbonate;
95 213
214 double mash_ph = MashpH();
215 qDebug() << "Distilled water mash pH:" << mash_ph;
216
217 /* Calculate Salt additions */
218 if (liters > 0) {
219 calcium += ( ui->bs_cacl2Edit->value() * MMCa / MMCaCl2 * 1000 + ui->bs_caso4Edit->value() * MMCa / MMCaSO4 * 1000 +
220 ui->bs_caco3Edit->value() * MMCa / MMCaCO3 * 1000) / liters;
221 magnesium += (ui->bs_mgso4Edit->value() * MMMg / MMMgSO4 * 1000 + ui->bs_mgcl2Edit->value() * MMMg / MMMgCl2 * 1000) / liters;
222 sodium += (ui->bs_naclEdit->value() * MMNa / MMNaCl * 1000 + ui->bs_nahco3Edit->value() * MMNa / MMNaHCO3 * 1000) / liters;
223 sulfate += (ui->bs_caso4Edit->value() * MMSO4 / MMCaSO4 * 1000 + ui->bs_mgso4Edit->value() * MMSO4 / MMMgSO4 * 1000) / liters;
224 chloride += (2 * ui->bs_cacl2Edit->value() * MMCl / MMCaCl2 * 1000 + ui->bs_naclEdit->value() * MMCl / MMNaCl * 1000 +
225 ui->bs_mgcl2Edit->value() * MMCl / MMMgCl2 * 1000) / liters;
226 bicarbonate += (ui->bs_nahco3Edit->value() * MMHCO3 / MMNaHCO3 * 1000 + ui->bs_caco3Edit->value() / 3 * MMHCO3 / MMCaCO3 * 1000) / liters;
227 }
228
229 if (recipe->wa_acid_name < 0 || recipe->wa_acid_name >= my_acids.size()) {
230 recipe->wa_acid_name = 0;
231 recipe->wa_acid_perc = my_acids.at(0).AcidPrc;
232 this->ignoreChanges = true;
233 ui->mw_acidPick->setCurrentIndex(0);
234 ui->mw_acidpercEdit->setValue(my_acids.at(0).AcidPrc);
235 this->ignoreChanges = false;
236 }
237 AT = recipe->wa_acid_name;
238
239 /*
240 * Note that the next calculations do not correct the pH change by the added salts.
241 * This pH change is at most 0.1 pH and is a minor difference in Acid amount.
242 */
243 if (recipe->calc_acid) {
244 /*
245 * Auto calculate the needed acid.
246 */
247 TpH = recipe->mash_ph;
248 protonDeficit = ProtonDeficit(TpH);
249 qDebug() << "calc_acid tgt:" << TpH << "protonDeficit:" << protonDeficit;
250 if (protonDeficit > 0) {
251 qDebug() << "pkn:" << AT << my_acids[AT].pK1 << my_acids[AT].pK2 << my_acids[AT].pK3;
252 frac = Utils::CalcFrac(TpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
253 Acid = protonDeficit / frac;
254 qDebug() << "1" << frac << Acid << protonDeficit;
255 Acid *= my_acids[AT].MolWt; // mg.
256 Acidmg = Acid;
257 Acid = Acid / my_acids[AT].AcidSG;
258 Acid = round((Acid / (recipe->wa_acid_perc / 100.0)) * 100.0) / 100.0;
259 qDebug() << "Mash auto Acid final ml:" << Acid;
260
261 for (int i = 0; i < recipe->miscs.size(); i++) {
262 qDebug() << i << recipe->miscs.at(i).m_name << my_acids[AT].name_en;
263 if (recipe->miscs.at(i).m_name == my_acids[AT].name_en || recipe->miscs.at(i).m_name == my_acids[AT].name_nl) {
264 qDebug() << "found at" << i << recipe->miscs.at(i).m_amount << Acid / 1000.0;
265 recipe->miscs[i].m_amount = Acid / 1000.0;
266 QTableWidgetItem *item = new QTableWidgetItem(QString("%1 ml").arg(Acid, 3, 'f', 2, '0'));
267 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
268 ui->miscsTable->setItem(i, 4, item);
269 this->ignoreChanges = true;
270 ui->mw_acidvolEdit->setValue(Acid);
271 this->ignoreChanges = false;
272 break;
273 }
274 }
275 bicarbonate = bicarbonate - protonDeficit * frac / liters;
276 total_alkalinity = bicarbonate * 50 / 61;
277 }
278 ph = TpH;
279 ui->wb_phEdit->setValue(ph);
280
281 //recipe->est_mash_ph = ph
282 } else { // Manual
283 /*
284 * Manual adjust acid, calculate resulting pH.
285 */
286 }
287
288 ui->wb_caEdit->setValue(calcium);
289 ui->wb_mgEdit->setValue(magnesium);
290 ui->wb_hco3Edit->setValue(bicarbonate);
291 ui->wb_caco3Edit->setValue(total_alkalinity);
292 ui->wb_naEdit->setValue(sodium);
293 ui->wb_clEdit->setValue(chloride);
294 ui->wb_so4Edit->setValue(sulfate);
295
296 ui->wb_caEdit->setStyleSheet((calcium < 40 || calcium > 150) ? "background-color: red":"background-color: green");
297 ui->wb_mgEdit->setStyleSheet((magnesium < 5 || magnesium > 40) ? "background-color: red":"background-color: green");
298 ui->wb_naEdit->setStyleSheet((sodium > 150) ? "background-color: red":"background-color: green");
299 /*
300 * Both chloride and sulfate should be above 50 according to
301 * John Palmer. So the Cl/SO4 ratio calculation will work.
302 */
303 ui->wb_clEdit->setStyleSheet((chloride <= 50 || chloride > 150) ? "background-color: red":"background-color: green");
304 ui->wb_so4Edit->setStyleSheet((sulfate <= 50 || sulfate > 400) ? "background-color: red":"background-color: green");
305 /*
306 * (cloride + sulfate) > 500 is too high
307 */
308 if ((chloride + sulfate) > 500) {
309 ui->wb_clEdit->setStyleSheet("background-color: red");
310 ui->wb_so4Edit->setStyleSheet("background-color: red");
311 }
312 ui->wb_phEdit->setStyleSheet((ph < 5.2 || ph > 5.6) ? "background-color: red":"background-color: green");
313 ui->wb_hco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green");
314 ui->wb_caco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green");
315
316 // calcSparge();
317 }
318
319
320 void EditRecipe::on_calc_acid_clicked()
321 {
322 recipe->calc_acid = ! recipe->calc_acid;
323 ui->mw_autoEdit->setChecked(recipe->calc_acid);
324 ui->mw_phEdit->setReadOnly(! recipe->calc_acid);
325 ui->mw_phEdit->setButtonSymbols(recipe->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons);
326 ui->mw_acidvolEdit->setReadOnly(recipe->calc_acid);
327 ui->mw_acidvolEdit->setButtonSymbols(recipe->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
328 is_changed();
329 calcWater();
96 } 330 }
97 331
98 332
99 void EditRecipe::on_w2_vol_changed(double val) 333 void EditRecipe::on_w2_vol_changed(double val)
100 { 334 {

mercurial