src/EditProductTab8.cpp

changeset 175
f1ed3a2a94e9
child 185
405bb68c1ea4
equal deleted inserted replaced
174:ceb8aa4ebd25 175:f1ed3a2a94e9
1 /**
2 * EditProduct.cpp is part of bmsapp.
3 *
4 * tab 8, water.
5 *
6 * bmsapp is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * bmsapp is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21 void EditProduct::refreshWaters()
22 {
23
24 // calc_acid
25 ui->mw_phEdit->setValue(product->mash_ph);
26 // mash_name
27 //ui->mw_acidpercEdit->setValue(product->
28
29 }
30
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 EditProduct::ZAlkalinity(double pHZ)
36 {
37 double C43 = Utils::Charge(4.3);
38 double Cw = Utils::Charge(product->wg_ph);
39 double Cz = Utils::Charge(pHZ);
40 double DeltaCNaught = -C43 + Cw;
41 double CT = product->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 EditProduct::ZRA(double pHZ)
52 {
53 double Calc = product->wg_calcium / (MMCa / 2);
54 double Magn = product->wg_magnesium / (MMMg / 2);
55 double Z = ZAlkalinity(pHZ);
56 return Z - (Calc / 3.5 + Magn / 7);
57 }
58
59
60 double EditProduct::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 EditProduct::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 EditProduct::ProtonDeficit(double pHZ)
96 {
97 double C1, x;
98 int i, error_count = 0;
99 double Result = ZRA(pHZ) * product->wg_amount;
100 Fermentables F;
101
102 /*
103 * proton deficit for the grist
104 */
105 if (product->fermentables.size()) {
106 for (i = 0; i < product->fermentables.size(); i++) {
107 F = product->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 EditProduct::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
144 void EditProduct::calcWater()
145 {
146 double liters = 0;
147 double calcium = 0;
148 double magnesium = 0;
149 double sodium = 0;
150 double total_alkalinity = 0;
151 double bicarbonate = 0;
152 double chloride = 0;
153 double sulfate = 0;
154 double ph = 0;
155 double TpH = 0;
156 double frac, RA;
157 double protonDeficit = 0;
158 double Acid = 0, Acidmg = 0;
159 int AT;
160
161 qDebug() << "calcWater()";
162
163 /*
164 * If there is a dilute water source, mix the waters.
165 */
166 if (product->w2_name != "") {
167 liters = product->w1_amount + product->w2_amount;
168 calcium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_calcium, product->w2_calcium);
169 magnesium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_magnesium, product->w2_magnesium);
170 sodium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_sodium, product->w2_sodium);
171 chloride = Utils::mix(product->w1_amount, product->w2_amount, product->w1_chloride, product->w2_chloride);
172 sulfate = Utils::mix(product->w1_amount, product->w2_amount, product->w1_sulfate, product->w2_sulfate);
173 total_alkalinity = Utils::mix(product->w1_amount, product->w2_amount, product->w1_total_alkalinity, product->w2_total_alkalinity);
174 ph = -log10(((pow(10, -product->w1_ph) * product->w1_amount) + (pow(10, -product->w2_ph) * product->w2_amount)) / liters);
175 } else {
176 liters = product->w1_amount;
177 calcium = product->w1_calcium;
178 magnesium = product->w1_magnesium;
179 sodium = product->w1_sodium;
180 chloride = product->w1_chloride;
181 sulfate = product->w1_sulfate;
182 total_alkalinity = product->w1_total_alkalinity;
183 ph = product->w1_ph;
184 }
185
186 product->wg_amount = liters;
187 product->wg_calcium = round(calcium * 10.0) / 10.0;
188 product->wg_magnesium = round(magnesium * 10.0) / 10.0;
189 product->wg_sodium = round(sodium * 10.0) / 10.0;
190 product->wg_chloride = round(chloride * 10.0) / 10.0;
191 product->wg_sulfate = round(sulfate * 10.0) / 10.0;
192 product->wg_total_alkalinity = round(total_alkalinity * 10.0) / 10.0;
193 product->wg_ph = ph;
194
195 ui->wg_volEdit->setValue(liters);
196 ui->wg_caEdit->setValue(calcium);
197 ui->wg_mgEdit->setValue(magnesium);
198 ui->wg_hco3Edit->setValue(total_alkalinity * 1.22);
199 ui->wg_caco3Edit->setValue(total_alkalinity);
200 ui->wg_naEdit->setValue(sodium);
201 ui->wg_clEdit->setValue(chloride);
202 ui->wg_so4Edit->setValue(sulfate);
203 ui->wg_phEdit->setValue(ph);
204 bicarbonate = total_alkalinity * 1.22;
205
206 /* Save mixed water ions for later */
207 double wg_calcium = calcium;
208 double wg_sodium = sodium;
209 double wg_total_alkalinity = total_alkalinity;
210 double wg_chloride = chloride;
211 double wg_sulfate = sulfate;
212 double wg_bicarbonate = bicarbonate;
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 const QSignalBlocker blocker1(ui->mw_acidPick);
230 const QSignalBlocker blocker2(ui->mw_acidpercEdit);
231 const QSignalBlocker blocker3(ui->mw_acidvolEdit);
232 const QSignalBlocker blocker4(ui->wb_phEdit);
233 const QSignalBlocker blocker5(ui->mw_phEdit);
234
235 if (product->wa_acid_name < 0 || product->wa_acid_name >= my_acids.size()) {
236 product->wa_acid_name = 0;
237 product->wa_acid_perc = my_acids.at(0).AcidPrc;
238 ui->mw_acidPick->setCurrentIndex(0);
239 ui->mw_acidpercEdit->setValue(my_acids.at(0).AcidPrc);
240 }
241 AT = product->wa_acid_name;
242
243 /*
244 * Note that the next calculations do not correct the pH change by the added salts.
245 * This pH change is at most 0.1 pH and is a minor difference in Acid amount.
246 */
247 if (product->calc_acid) {
248 /*
249 * Auto calculate the needed acid.
250 */
251 TpH = product->mash_ph;
252 protonDeficit = ProtonDeficit(TpH);
253 qDebug() << " calc_acid tgt:" << TpH << "protonDeficit:" << protonDeficit;
254 if (protonDeficit > 0) {
255 frac = Utils::CalcFrac(TpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
256 Acid = protonDeficit / frac;
257 Acid *= my_acids[AT].MolWt; // mg.
258 Acidmg = Acid;
259 Acid = Acid / my_acids[AT].AcidSG;
260 Acid = round((Acid / (product->wa_acid_perc / 100.0)) * 100.0) / 100.0;
261 qDebug() << " Mash auto Acid final ml:" << Acid;
262
263 QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl;
264 brewing_salt_sub(w, Acid);
265 ui->mw_acidvolEdit->setValue(Acid);
266
267 bicarbonate = bicarbonate - protonDeficit * frac / liters;
268 total_alkalinity = bicarbonate * 50 / 61;
269 }
270 ph = TpH;
271 ui->wb_phEdit->setValue(ph);
272 product->mash_ph = ph;
273 } else { // Manual
274 /*
275 * Manual adjust acid, calculate resulting pH.
276 */
277 double pHa = ph; // Mixed water pH.
278 // Then calculate the new pH with added acids and malts
279 qDebug() << " Mash pH:" << pHa;
280 Acid = my_acids[AT].AcidSG * (product->wa_acid_perc / 100.0); // ml
281 Acid *= ui->mw_acidvolEdit->value();
282 Acid /= my_acids[AT].MolWt; // mg;
283 Acidmg = Acid;
284
285 //find the pH where the protondeficit = protondeficit by the acid
286 frac = Utils::CalcFrac(pHa, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
287 protonDeficit = Acid * frac;
288 //qDebug() << " protonDeficit Acid:" << protonDeficit << "frac:" << frac << "pH:" << pHa;
289
290 double deltapH = 0.001;
291 double deltapd = 0.1;
292 double pd = round(ProtonDeficit(pHa) * 1000000.0) / 1000000.0;
293 int n = 0;
294 while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 4000)) {
295 n++;
296 if (pd < (protonDeficit - deltapd))
297 pHa -= deltapH;
298 else if (pd > (protonDeficit + deltapd))
299 pHa += deltapH;
300 frac = Utils::CalcFrac(pHa, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
301 protonDeficit = Acid * frac;
302 pd = ProtonDeficit(pHa);
303 }
304 //qDebug() << " n:" << n << "pd:" << pd << "protonDeficit:" << protonDeficit << "frac:" << frac << "pHa:" << pHa;
305
306 bicarbonate = wg_bicarbonate - protonDeficit * frac / liters;
307 total_alkalinity = bicarbonate * 50 / 61;
308 ph = pHa;
309 ui->wb_phEdit->setValue(ph);
310 ui->mw_phEdit->setValue(ph);
311 product->mash_ph = ph;
312 }
313
314 if ((AT == 3) && (liters > 0)) { // Sulfuctic / Zwavelzuur
315 RA = ui->bs_caso4Edit->value() * MMSO4 / MMCaSO4 + ui->bs_mgso4Edit->value() * MMSO4 / MMMgSO4 + Acidmg / 1000 * MMSO4 / (MMSO4 + 2);
316 RA = 1000 * RA / liters;
317 sulfate = wg_sulfate + RA; // Not add to sulfate??
318 } else if ((AT == 1) && (liters > 0)) { // Hydrochloric, Zoutzuur
319 RA = ui->bs_cacl2Edit->value() * MMCl / MMCaCl2 + ui->bs_naclEdit->value() * MMCl / MMNaCl + Acidmg / 1000 * MMCl / (MMCl + 1);
320 RA = 1000 * RA / liters;
321 chloride = wg_chloride + RA;
322 }
323
324 double BUGU = GetBUGU();
325 ui->buguEdit->setValue(BUGU);
326 if (BUGU < 0.32)
327 ui->buguResult->setText(tr("Very malty and sweet"));
328 else if (BUGU < 0.43)
329 ui->buguResult->setText(tr("Malty, sweet"));
330 else if (BUGU < 0.52)
331 ui->buguResult->setText(tr("Balanced"));
332 else if (BUGU < 0.63)
333 ui->buguResult->setText(tr("Hoppy, bitter"));
334 else
335 ui->buguResult->setText(tr("Very hoppy, very bitter"));
336
337 double OptSO4Clratio = GetOptSO4Clratio();
338 ui->so4clEdit->setValue(OptSO4Clratio);
339 ui->cur_so4clResult->setRange(0.7 * OptSO4Clratio, 1.3 * OptSO4Clratio);
340 if (OptSO4Clratio < 0.4)
341 ui->so4clResult->setText(tr("Too malty"));
342 else if (OptSO4Clratio < 0.6)
343 ui->so4clResult->setText(tr("Very malty"));
344 else if (OptSO4Clratio < 0.8)
345 ui->so4clResult->setText(tr("Malty"));
346 else if (OptSO4Clratio < 1.5)
347 ui->so4clResult->setText(tr("Balanced"));
348 else if (OptSO4Clratio < 2.0)
349 ui->so4clResult->setText(tr("Little bitter"));
350 else if (OptSO4Clratio < 4.0)
351 ui->so4clResult->setText(tr("Bitter"));
352 else if (OptSO4Clratio < 9.0)
353 ui->so4clResult->setText(tr("Very bitter"));
354 else
355 ui->so4clResult->setText(tr("Too bitter"));
356 if (chloride > 0)
357 RA = sulfate / chloride;
358 else
359 RA = 10;
360 ui->cur_so4clEdit->setValue(RA);
361 ui->cur_so4clResult->setValue(RA);
362
363 product->wb_calcium = calcium;
364 product->wb_magnesium = magnesium;
365 product->wb_total_alkalinity = total_alkalinity;
366 product->wb_sodium = sodium;
367 product->wb_chloride = chloride;
368 product->wb_sulfate = sulfate;
369 product->wb_ph = ph;
370
371 ui->wb_caEdit->setValue(calcium);
372 ui->wb_mgEdit->setValue(magnesium);
373 ui->wb_hco3Edit->setValue(bicarbonate);
374 ui->wb_caco3Edit->setValue(total_alkalinity);
375 ui->wb_naEdit->setValue(sodium);
376 ui->wb_clEdit->setValue(chloride);
377 ui->wb_so4Edit->setValue(sulfate);
378
379 ui->wb_caEdit->setStyleSheet((calcium < 40 || calcium > 150) ? "background-color: red":"background-color: green");
380 ui->wb_mgEdit->setStyleSheet((magnesium < 5 || magnesium > 40) ? "background-color: red":"background-color: green");
381 ui->wb_naEdit->setStyleSheet((sodium > 150) ? "background-color: red":"background-color: green");
382 /*
383 * Both chloride and sulfate should be above 50 according to
384 * John Palmer. So the Cl/SO4 ratio calculation will work.
385 */
386 ui->wb_clEdit->setStyleSheet((chloride <= 50 || chloride > 150) ? "background-color: red":"background-color: green");
387 ui->wb_so4Edit->setStyleSheet((sulfate <= 50 || sulfate > 400) ? "background-color: red":"background-color: green");
388 /*
389 * (cloride + sulfate) > 500 is too high
390 */
391 if ((chloride + sulfate) > 500) {
392 ui->wb_clEdit->setStyleSheet("background-color: red");
393 ui->wb_so4Edit->setStyleSheet("background-color: red");
394 }
395 ui->wb_phEdit->setStyleSheet((ph < 5.2 || ph > 5.6) ? "background-color: red":"background-color: green");
396 ui->wb_hco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green");
397 ui->wb_caco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green");
398
399 calcSparge();
400 }
401
402
403 /*
404 * Based on the work of ajDeLange.
405 */
406 void EditProduct::calcSparge()
407 {
408 double TargetpH = product->sparge_ph;
409 double Source_pH = product->w1_ph;
410 double Source_alkalinity = product->w1_total_alkalinity;
411
412 qDebug() << "calcSparge()";
413
414 const QSignalBlocker blocker1(ui->sp_sourceEdit);
415
416 // Select watersource or fallback to the first source.
417 if (product->sparge_source == 1) { // Source 2
418 if (product->w2_ph > 0.0 && product->w2_amount > 0) {
419 Source_pH = product->w2_ph;
420 Source_alkalinity = product->w2_total_alkalinity;
421 } else {
422 product->sparge_source = 0; // Source 1
423 ui->sp_sourceEdit->setCurrentIndex(0);
424 }
425 } else if (product->sparge_source == 2) { // Mixed
426 if (product->w2_ph > 0.0 && product->w2_amount > 0) {
427 Source_pH = product->wg_ph;
428 Source_alkalinity = product->wg_total_alkalinity;
429 } else {
430 product->sparge_source = 0; // Source 1
431 ui->sp_sourceEdit->setCurrentIndex(0);
432 }
433 }
434
435 // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH
436 double r1 = pow(10, Source_pH - 6.35);
437 double r2 = pow(10, Source_pH - 10.33);
438 double d = 1 + r1 + r1 * r2;
439 double f1 = 1 / d;
440 double f3 = r1 * r2 / d;
441
442 // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity)
443 double r143 = pow(10, 4.3 - 6.35);
444 double r243 = pow(10, 4.3 - 10.33);
445 double d43 = 1 + r143 + r143 * r243;
446 double f143 = 1 / d43;
447 double f343 = r143 * r243 / d43;
448
449 // Step 4. Solve
450 //double Ct = (Source_alkalinity - 1000 * (pow(10, -4.3) - pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343));
451 double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343));
452
453 // Step 5. Compute mole fractions at desired pH
454 double r1g = pow(10, TargetpH - 6.35);
455 double r2g = pow(10, TargetpH - 10.33);
456 double dg = 1 + r1g + r1g * r2g;
457 double f1g = 1 / dg;
458 double f3g = r1g * r2g / dg;
459
460 // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L)
461 double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l
462 Acid += 0.01; // Add acid that would be required for distilled water.
463
464 //Step 8. Get the acid data.
465 int AT = product->sparge_acid_type;
466 if (AT < 0 || AT >= my_acids.size()) {
467 AT = 0;
468 product->sparge_acid_type = 0;
469 ui->sp_acidtypeEdit->setCurrentIndex(0);
470 product->sparge_acid_perc = my_acids[0].AcidPrc;
471 ui->sp_acidpercEdit->setValue(product->sparge_acid_perc);
472 }
473 double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
474
475 // Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid.
476 Acid /= fract;
477
478 // Step 10. Multiply by molecular weight of the acid
479 Acid *= my_acids[AT].MolWt; //mg
480
481 // Step 11. Divide by Specific Gravity and Percentage to get the final ml.
482 Acid = Acid / my_acids[AT].AcidSG / (product->sparge_acid_perc / 100); //ml
483 Acid *= product->sparge_volume; //ml acid total
484 Acid = round(Acid * 100.0) / 100.0;
485 product->sparge_acid_amount = Acid / 1000;
486 ui->sp_acidvolEdit->setValue(Acid);
487 }
488
489
490 void EditProduct::sp_source_changed(int val)
491 {
492 product->sparge_source = val;
493 calcSparge();
494 is_changed();
495 }
496
497
498 void EditProduct::sp_type_changed(int val)
499 {
500 product->sparge_acid_type = val;
501 product->sparge_acid_perc = my_acids[val].AcidPrc;
502 ui->sp_acidpercEdit->setValue(product->sparge_acid_perc);
503 calcSparge();
504 is_changed();
505 }
506
507
508 void EditProduct::sp_ph_changed(double val)
509 {
510 product->sparge_ph = val;
511 calcSparge();
512 is_changed();
513 }
514
515
516 double EditProduct::GetBUGU()
517 {
518 double gu = (product->est_og - 1) * 1000;
519 if (gu > 0)
520 return product->est_ibu / gu;
521 return 0.5;
522 }
523
524
525 double EditProduct::GetOptSO4Clratio()
526 {
527 if (ui->wt_so4Edit->value() > 0 && ui->wt_clEdit->value()) {
528 /* If target water is selected .. */
529 return (ui->wt_so4Edit->value() / ui->wt_clEdit->value());
530 }
531 double BUGU = GetBUGU();
532 return (-1.2 * BUGU + 1.4);
533 }
534
535
536 void EditProduct::mw_calc_acid_clicked()
537 {
538 product->calc_acid = ! product->calc_acid;
539 ui->mw_autoEdit->setChecked(product->calc_acid);
540 ui->mw_phEdit->setReadOnly(! product->calc_acid);
541 ui->mw_phEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons);
542 ui->mw_acidvolEdit->setReadOnly(product->calc_acid);
543 ui->mw_acidvolEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
544 is_changed();
545 calcWater();
546 }
547
548
549 void EditProduct::mw_ph_changed(double val)
550 {
551 if (! product->calc_acid)
552 return;
553
554 if (product->mash_ph != val) {
555 qDebug() << "mw_ph_changed" << val << product->mash_ph;
556 product->mash_ph = val;
557 is_changed();
558 calcWater();
559 }
560 }
561
562
563 void EditProduct::mw_acid_changed(double val)
564 {
565 if (product->calc_acid)
566 return;
567
568 qDebug() << "on_mw_acid_changed" << val;
569 QString w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl;
570 set_brewing_salt(w, val);
571 }
572
573
574 void EditProduct::mw_type_changed(int val)
575 {
576 if (val == product->wa_acid_name)
577 return;
578
579 qDebug() << "on_mw_type_changed" << val << "old" << product->wa_acid_name;
580 /*
581 * First remove current acid.
582 */
583 QString w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl;
584 brewing_salt_sub(w, 0);
585
586 product->wa_acid_name = val;
587 w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl;
588
589 product->wa_acid_perc = my_acids.at(val).AcidPrc;
590 ui->mw_acidpercEdit->setValue(my_acids.at(val).AcidPrc);
591 brewing_salt_sub(w, ui->mw_acidvolEdit->value()); // For now, set old amount.
592
593 is_changed();
594 calcWater();
595 }
596
597
598 void EditProduct::w2_volume_changed(double val)
599 {
600 qDebug() << "w2_vol_changed" << val;
601
602 if (product->w2_total_alkalinity && product->w2_sulfate) {
603 /*
604 * Seems a valid water, but don't go over the total.
605 */
606 if (val < (product->w1_amount + product->w2_amount)) {
607 product->w1_amount -= val - product->w2_amount;
608 product->w2_amount = val;
609 ui->w1_volEdit->setValue(product->w1_amount);
610 }
611 } else {
612 /*
613 * Invalid water, block changes.
614 */
615 product->w2_amount = 0;
616 }
617 ui->w2_volEdit->setValue(product->w2_amount);
618
619 calcWater();
620 is_changed();
621 }
622
623
624 void EditProduct::w1_name_changed(int val)
625 {
626 QSqlQuery query;
627
628 qDebug() << "w1_name_changed" << val;
629 const QSignalBlocker blocker1(ui->w1_nameEdit);
630 if (val == 0) {
631 /*
632 * If no water is selected, take the default water.
633 */
634 val = my_default_water;
635 ui->w1_nameEdit->setCurrentIndex(val);
636 }
637
638 query.prepare("SELECT * FROM inventory_waters ORDER BY record");
639 query.exec();
640 query.first();
641 for (int i = 0; i < (val - 1); i++) {
642 query.next();
643 }
644 qDebug() << "set water" << query.value(1).toString();
645
646 product->w1_name = query.value(1).toString();
647 product->w1_calcium = query.value(3).toDouble();
648 product->w1_magnesium = query.value(8).toDouble();
649 product->w1_total_alkalinity = query.value(11).toDouble();
650 product->w1_sodium = query.value(7).toDouble();
651 product->w1_chloride = query.value(6).toDouble();
652 product->w1_sulfate = query.value(5).toDouble();
653 product->w1_ph = query.value(9).toDouble();
654
655 ui->w1_caEdit->setValue(product->w1_calcium);
656 ui->w1_mgEdit->setValue(product->w1_magnesium);
657 ui->w1_hco3Edit->setValue(product->w1_total_alkalinity * 1.22);
658 ui->w1_caco3Edit->setValue(product->w1_total_alkalinity);
659 ui->w1_naEdit->setValue(product->w1_sodium);
660 ui->w1_clEdit->setValue(product->w1_chloride);
661 ui->w1_so4Edit->setValue(product->w1_sulfate);
662 ui->w1_phEdit->setValue(product->w1_ph);
663
664 is_changed();
665 calcWater();
666 }
667
668
669 void EditProduct::w2_name_changed(int val)
670 {
671 QSqlQuery query;
672
673 qDebug() << "w2_name_changed" << val;
674
675 if (val == 0) { // Clear water 2.
676 product->w2_name = "";
677 product->w2_calcium = 0;
678 product->w2_magnesium = 0;
679 product->w2_total_alkalinity = 0;
680 product->w2_sodium = 0;
681 product->w2_chloride = 0;
682 product->w2_sulfate = 0;
683 product->w2_ph = 0;
684 product->w1_amount += product->w2_amount;
685 product->w2_amount = 0;
686 } else {
687 query.prepare("SELECT * FROM inventory_waters ORDER BY record");
688 query.exec();
689 query.first();
690 for (int i = 0; i < (val - 1); i++) {
691 query.next();
692 }
693 qDebug() << "set water" << query.value(1).toString();
694
695 product->w2_name = query.value(1).toString();
696 product->w2_calcium = query.value(3).toDouble();
697 product->w2_magnesium = query.value(8).toDouble();
698 product->w2_total_alkalinity = query.value(11).toDouble();
699 product->w2_sodium = query.value(7).toDouble();
700 product->w2_chloride = query.value(6).toDouble();
701 product->w2_sulfate = query.value(5).toDouble();
702 product->w2_ph = query.value(9).toDouble();
703 }
704 ui->w1_volEdit->setValue(product->w1_amount);
705 ui->w2_volEdit->setValue(product->w2_amount);
706 ui->w2_caEdit->setValue(product->w2_calcium);
707 ui->w2_mgEdit->setValue(product->w2_magnesium);
708 ui->w2_hco3Edit->setValue(product->w2_total_alkalinity * 1.22);
709 ui->w2_caco3Edit->setValue(product->w2_total_alkalinity);
710 ui->w2_naEdit->setValue(product->w2_sodium);
711 ui->w2_clEdit->setValue(product->w2_chloride);
712 ui->w2_so4Edit->setValue(product->w2_sulfate);
713 ui->w2_phEdit->setValue(product->w2_ph);
714
715 is_changed();
716 calcWater();
717 }
718
719
720 void EditProduct::wt_target_changed(int val)
721 {
722 QSqlQuery query;
723
724 if (val == 0) {
725 /* Clear values */
726 ui->wt_caEdit->setValue(0);
727 ui->wt_mgEdit->setValue(0);
728 ui->wt_hco3Edit->setValue(0);
729 ui->wt_caco3Edit->setValue(0);
730 ui->wt_naEdit->setValue(0);
731 ui->wt_clEdit->setValue(0);
732 ui->wt_so4Edit->setValue(0);
733 } else {
734 query.prepare("SELECT * FROM profile_water ORDER BY name");
735 query.exec();
736 query.first();
737 for (int i = 0; i < (val - 1); i++) {
738 query.next();
739 }
740 ui->wt_caEdit->setValue(query.value(2).toDouble());
741 ui->wt_mgEdit->setValue(query.value(7).toDouble());
742 ui->wt_hco3Edit->setValue(query.value(3).toDouble());
743 ui->wt_caco3Edit->setValue(query.value(10).toDouble());
744 ui->wt_naEdit->setValue(query.value(6).toDouble());
745 ui->wt_clEdit->setValue(query.value(5).toDouble());
746 ui->wt_so4Edit->setValue(query.value(4).toDouble());
747 }
748 calcWater();
749 }
750
751
752 void EditProduct::adjustWaters(double factor)
753 {
754 int i;
755 double amount;
756
757 if (product->mashs.size() == 0)
758 return;
759
760 double mash_infuse = 0;
761 for (i = 0; i < product->mashs.size(); i++) {
762 if (product->mashs.at(i).step_type == 0) { // Infusion
763 amount = round(product->mashs.at(i).step_infuse_amount * factor * 1000000.0) / 1000000.0;
764 product->mashs[i].step_infuse_amount = amount;
765 mash_infuse += amount;
766 product->mashs[i].step_volume = mash_infuse;
767 }
768 }
769
770 const QSignalBlocker blocker1(ui->w1_volEdit);
771 const QSignalBlocker blocker2(ui->w2_volEdit);
772
773 if (product->w2_amount == 0) {
774 product->w1_amount = mash_infuse;
775 ui->w1_volEdit->setValue(mash_infuse);
776 } else {
777 double w1 = (product->w1_amount / (product->w1_amount + product->w2_amount)) * mash_infuse;
778 double w2 = (product->w2_amount / (product->w1_amount + product->w2_amount)) * mash_infuse;
779 product->w1_amount = w1;
780 product->w2_amount = w2;
781 ui->w1_volEdit->setValue(product->w1_amount);
782 ui->w2_volEdit->setValue(product->w2_amount);
783 }
784 product->wg_amount = mash_infuse;
785 ui->wg_volEdit->setValue(mash_infuse);
786 }
787
788
789 void EditProduct::wb_cacl2_changed(double val) { set_brewing_salt("CaCl2", val); }
790 void EditProduct::wb_caso4_changed(double val) { set_brewing_salt("CaSO4", val); }
791 void EditProduct::wb_mgso4_changed(double val) { set_brewing_salt("MgSO4", val); }
792 void EditProduct::wb_nacl_changed(double val) { set_brewing_salt("NaCl", val); }
793 void EditProduct::wb_mgcl2_changed(double val) { set_brewing_salt("MgCl2", val); }
794 void EditProduct::wb_nahco3_changed(double val) { set_brewing_salt("NaHCO3", val); }
795 void EditProduct::wb_caco3_changed(double val) { set_brewing_salt("CaCO3", val); }
796
797

mercurial