418 double Source_pH = recipe->w1_ph; |
427 double Source_pH = recipe->w1_ph; |
419 double Source_alkalinity = recipe->w1_total_alkalinity; |
428 double Source_alkalinity = recipe->w1_total_alkalinity; |
420 |
429 |
421 qDebug() << "calcSparge()"; |
430 qDebug() << "calcSparge()"; |
422 |
431 |
423 const QSignalBlocker blocker1(ui->sp_sourceEdit); |
432 const QSignalBlocker blocker2(ui->w1_spButton); |
|
433 const QSignalBlocker blocker3(ui->w2_spButton); |
|
434 const QSignalBlocker blocker4(ui->wg_spButton); |
424 |
435 |
425 // Select watersource or fallback to the first source. |
436 // Select watersource or fallback to the first source. |
426 if (recipe->sparge_source == 1) { // Source 2 |
437 if (recipe->sparge_source == 1) { // Source 2 |
427 if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { |
438 if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { |
428 Source_pH = recipe->w2_ph; |
439 Source_pH = recipe->w2_ph; |
429 Source_alkalinity = recipe->w2_total_alkalinity; |
440 Source_alkalinity = recipe->w2_total_alkalinity; |
|
441 ui->sp_caEdit->setValue(recipe->w2_calcium); |
|
442 ui->sp_mgEdit->setValue(recipe->w2_magnesium); |
|
443 ui->sp_hco3Edit->setValue(Utils::Bicarbonate(recipe->w2_total_alkalinity, recipe->w2_ph)); |
|
444 ui->sp_caco3Edit->setValue(recipe->w2_total_alkalinity); |
|
445 ui->sp_naEdit->setValue(recipe->w2_sodium); |
|
446 ui->sp_clEdit->setValue(recipe->w2_chloride); |
|
447 ui->sp_so4Edit->setValue(recipe->w2_sulfate); |
|
448 ui->sp_phShow->setValue(recipe->w2_ph); |
|
449 ui->sp_hardnessEdit->setValue(Utils::Hardness(recipe->w2_calcium, recipe->w2_magnesium)); |
|
450 ui->sp_raEdit->setValue(Utils::ResidualAlkalinity(recipe->w2_total_alkalinity, recipe->w2_calcium, recipe->w2_magnesium)); |
|
451 ui->w2_spButton->setChecked(true); |
430 } else { |
452 } else { |
431 recipe->sparge_source = 0; // Source 1 |
453 recipe->sparge_source = 0; // Fallback to source 1 |
432 ui->sp_sourceEdit->setCurrentIndex(0); |
454 ui->w1_spButton->setChecked(true); |
433 } |
455 } |
434 } else if (recipe->sparge_source == 2) { // Mixed |
456 } else if (recipe->sparge_source == 2) { // Mixed |
435 if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { |
457 if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { |
436 Source_pH = recipe->wg_ph; |
458 Source_pH = recipe->wg_ph; |
437 Source_alkalinity = recipe->wg_total_alkalinity; |
459 Source_alkalinity = recipe->wg_total_alkalinity; |
|
460 ui->sp_caEdit->setValue(recipe->wg_calcium); |
|
461 ui->sp_mgEdit->setValue(recipe->wg_magnesium); |
|
462 ui->sp_hco3Edit->setValue(Utils::Bicarbonate(recipe->wg_total_alkalinity, recipe->wg_ph)); |
|
463 ui->sp_caco3Edit->setValue(recipe->wg_total_alkalinity); |
|
464 ui->sp_naEdit->setValue(recipe->wg_sodium); |
|
465 ui->sp_clEdit->setValue(recipe->wg_chloride); |
|
466 ui->sp_so4Edit->setValue(recipe->wg_sulfate); |
|
467 ui->sp_phShow->setValue(recipe->wg_ph); |
|
468 ui->sp_hardnessEdit->setValue(Utils::Hardness(recipe->wg_calcium, recipe->wg_magnesium)); |
|
469 ui->sp_raEdit->setValue(Utils::ResidualAlkalinity(recipe->wg_total_alkalinity, recipe->wg_calcium, recipe->wg_magnesium)); |
|
470 ui->wg_spButton->setChecked(true); |
438 } else { |
471 } else { |
439 recipe->sparge_source = 0; // Source 1 |
472 recipe->sparge_source = 0; // Fallback to source 1 |
440 ui->sp_sourceEdit->setCurrentIndex(0); |
473 ui->w1_spButton->setChecked(true); |
441 } |
474 } |
442 } |
475 } |
443 |
476 if (recipe->sparge_source == 0) { |
444 // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH |
477 ui->sp_caEdit->setValue(recipe->w1_calcium); |
445 double r1 = pow(10, Source_pH - 6.35); |
478 ui->sp_mgEdit->setValue(recipe->w1_magnesium); |
446 double r2 = pow(10, Source_pH - 10.33); |
479 ui->sp_hco3Edit->setValue(Utils::Bicarbonate(recipe->w1_total_alkalinity, recipe->w1_ph)); |
447 double d = 1 + r1 + r1 * r2; |
480 ui->sp_caco3Edit->setValue(recipe->w1_total_alkalinity); |
448 double f1 = 1 / d; |
481 ui->sp_naEdit->setValue(recipe->w1_sodium); |
449 double f3 = r1 * r2 / d; |
482 ui->sp_clEdit->setValue(recipe->w1_chloride); |
450 |
483 ui->sp_so4Edit->setValue(recipe->w1_sulfate); |
451 // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) |
484 ui->sp_phShow->setValue(recipe->w1_ph); |
452 double r143 = pow(10, 4.3 - 6.35); |
485 ui->sp_hardnessEdit->setValue(Utils::Hardness(recipe->w1_calcium, recipe->w1_magnesium)); |
453 double r243 = pow(10, 4.3 - 10.33); |
486 ui->sp_raEdit->setValue(Utils::ResidualAlkalinity(recipe->w1_total_alkalinity, recipe->w1_calcium, recipe->w1_magnesium)); |
454 double d43 = 1 + r143 + r143 * r243; |
487 ui->w1_spButton->setChecked(true); |
455 double f143 = 1 / d43; |
488 } |
456 double f343 = r143 * r243 / d43; |
489 // The spargewater is set. |
457 |
490 |
458 // Step 4. Solve |
|
459 //double Ct = (Source_alkalinity - 1000 * (pow(10, -4.3) - pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343)); |
|
460 double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); |
|
461 |
|
462 // Step 5. Compute mole fractions at desired pH |
|
463 double r1g = pow(10, TargetpH - 6.35); |
|
464 double r2g = pow(10, TargetpH - 10.33); |
|
465 double dg = 1 + r1g + r1g * r2g; |
|
466 double f1g = 1 / dg; |
|
467 double f3g = r1g * r2g / dg; |
|
468 |
|
469 // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) |
|
470 double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l |
|
471 Acid += 0.01; // Add acid that would be required for distilled water. |
|
472 |
|
473 //Step 8. Get the acid data. |
|
474 int AT = recipe->sparge_acid_type; |
491 int AT = recipe->sparge_acid_type; |
475 if (AT < 0 || AT >= my_acids.size()) { |
492 if (AT < 0 || AT >= my_acids.size()) { |
476 AT = 0; |
493 AT = 0; |
477 recipe->sparge_acid_type = 0; |
494 recipe->sparge_acid_type = 0; |
478 ui->sp_acidtypeEdit->setCurrentIndex(0); |
495 ui->sp_acidtypeEdit->setCurrentIndex(0); |
479 recipe->sparge_acid_perc = my_acids[0].AcidPrc; |
496 recipe->sparge_acid_perc = my_acids[0].AcidPrc; |
480 ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); |
497 ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); |
481 } |
498 } |
482 double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); |
499 |
483 |
500 /* |
484 // Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid. |
501 * Auto calculate the required acid |
485 Acid /= fract; |
502 */ |
486 |
503 if (recipe->calc_acid) { |
487 // Step 10. Multiply by molecular weight of the acid |
504 // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH |
488 Acid *= my_acids[AT].MolWt; //mg |
505 double r1 = pow(10, Source_pH - 6.35); |
489 |
506 double r2 = pow(10, Source_pH - 10.33); |
490 // Step 11. Divide by Specific Gravity and Percentage to get the final ml. |
507 double d = 1 + r1 + r1 * r2; |
491 Acid = Acid / my_acids[AT].AcidSG / (recipe->sparge_acid_perc / 100); //ml |
508 double f1 = 1 / d; |
492 Acid *= recipe->sparge_volume; //ml acid total |
509 double f3 = r1 * r2 / d; |
493 Acid = round(Acid * 100.0) / 100.0; |
510 |
494 recipe->sparge_acid_amount = Acid / 1000; |
511 // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) |
495 ui->sp_acidvolEdit->setValue(Acid); |
512 double r143 = pow(10, 4.3 - 6.35); |
496 |
513 double r243 = pow(10, 4.3 - 10.33); |
|
514 double d43 = 1 + r143 + r143 * r243; |
|
515 double f143 = 1 / d43; |
|
516 double f343 = r143 * r243 / d43; |
|
517 |
|
518 // Step 4. Solve |
|
519 double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); |
|
520 |
|
521 // Step 5. Compute mole fractions at desired pH |
|
522 double r1g = pow(10, TargetpH - 6.35); |
|
523 double r2g = pow(10, TargetpH - 10.33); |
|
524 double dg = 1 + r1g + r1g * r2g; |
|
525 double f1g = 1 / dg; |
|
526 double f3g = r1g * r2g / dg; |
|
527 |
|
528 // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) |
|
529 double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l |
|
530 Acid += 0.01; // Add acid that would be required for distilled water. |
|
531 |
|
532 // Step 8. Get the acid data. |
|
533 double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); |
|
534 |
|
535 // Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid. |
|
536 Acid /= fract; |
|
537 |
|
538 // Step 10. Multiply by molecular weight of the acid |
|
539 Acid *= my_acids[AT].MolWt; //mg |
|
540 |
|
541 // Step 11. Divide by Specific Gravity and Percentage to get the final ml. |
|
542 double RealSG = round(((my_acids[AT].AcidSG - 1000) * (recipe->sparge_acid_perc / 100)) + 1000); |
|
543 Acid = Acid / RealSG; //ml |
|
544 Acid *= recipe->sparge_volume; //ml acid total at 100% |
|
545 Acid /= my_acids[AT].AcidPrc / 100; //ml acid at supplied strength |
|
546 Acid = round(Acid * 100.0) / 100.0; |
|
547 recipe->sparge_acid_amount = Acid / 1000; |
|
548 QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl; |
|
549 brewing_salt_sub(w, Acid, MISC_USES_SPARGE); // Put it in the miscs table. |
|
550 ui->sp_acidvolEdit->setValue(Acid); |
|
551 } |
|
552 |
|
553 ui->sp_phShow->setValue(recipe->sparge_ph); |
497 // Finally calculate the estimate preboil pH |
554 // Finally calculate the estimate preboil pH |
498 recipe->preboil_ph = -log10(((pow(10, -recipe->mash_ph) * recipe->wg_amount) + (pow(10, -recipe->sparge_ph) * recipe->sparge_volume)) / |
555 recipe->preboil_ph = -log10(((pow(10, -recipe->mash_ph) * recipe->wg_amount) + (pow(10, -recipe->sparge_ph) * recipe->sparge_volume)) / |
499 (recipe->wg_amount + recipe->sparge_volume)); |
556 (recipe->wg_amount + recipe->sparge_volume)); |
500 ui->preboil_phEdit->setValue(recipe->preboil_ph); |
557 ui->preboil_phEdit->setValue(recipe->preboil_ph); |
501 } |
558 } |
502 |
559 |
503 |
560 |
504 void EditRecipe::sp_source_changed(int val) |
561 void EditRecipe::sp_group_changed(int val) |
505 { |
562 { |
506 recipe->sparge_source = val; |
563 if (val != recipe->sparge_source) { |
507 calcSparge(); |
564 qDebug() << "sp_group_changed" << val; |
508 is_changed(); |
565 recipe->sparge_source = val; |
|
566 calcSparge(); |
|
567 is_changed(); |
|
568 } |
509 } |
569 } |
510 |
570 |
511 |
571 |
512 void EditRecipe::sp_type_changed(int val) |
572 void EditRecipe::sp_type_changed(int val) |
513 { |
573 { |
|
574 if (val == recipe->sparge_acid_type) |
|
575 return; |
|
576 |
|
577 qDebug() << "sp_type_changed" << val << "old" << recipe->sparge_acid_type; |
|
578 /* |
|
579 * First remove current acid. |
|
580 */ |
|
581 QString w = my_acids[recipe->sparge_acid_type].name_en + ' ' + my_acids[recipe->sparge_acid_type].name_nl; |
|
582 brewing_salt_sub(w, 0, MISC_USES_SPARGE); |
|
583 |
514 recipe->sparge_acid_type = val; |
584 recipe->sparge_acid_type = val; |
|
585 w = my_acids[recipe->sparge_acid_type].name_en + ' ' + my_acids[recipe->sparge_acid_type].name_nl; |
|
586 |
515 recipe->sparge_acid_perc = my_acids[val].AcidPrc; |
587 recipe->sparge_acid_perc = my_acids[val].AcidPrc; |
516 ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); |
588 ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); |
|
589 brewing_salt_sub(w, ui->sp_acidvolEdit->value(), MISC_USES_SPARGE); // For now, set old amount. |
|
590 |
517 calcSparge(); |
591 calcSparge(); |
518 is_changed(); |
592 is_changed(); |
519 } |
593 } |
520 |
594 |
521 |
595 |
522 void EditRecipe::sp_ph_changed(double val) |
596 void EditRecipe::sp_ph_changed(double val) |
523 { |
597 { |
524 recipe->sparge_ph = val; |
598 recipe->sparge_ph = val; |
525 calcSparge(); |
599 calcSparge(); |
526 is_changed(); |
600 is_changed(); |
|
601 } |
|
602 |
|
603 |
|
604 void EditRecipe::sp_acid_changed(double val) |
|
605 { |
|
606 if (recipe->calc_acid) |
|
607 return; |
|
608 |
|
609 qDebug() << "sp_acid_changed" << val << recipe->sparge_acid_amount * 1000.0; |
|
610 |
|
611 double TargetpH = recipe->sparge_ph; |
|
612 double Source_pH = recipe->w1_ph; |
|
613 double Source_alkalinity = recipe->w1_total_alkalinity; |
|
614 |
|
615 if (recipe->sparge_source == 1) { // Source 2 |
|
616 if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { |
|
617 Source_pH = recipe->w2_ph; |
|
618 Source_alkalinity = recipe->w2_total_alkalinity; |
|
619 } |
|
620 } else if (recipe->sparge_source == 2) { // Mixed |
|
621 if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { |
|
622 Source_pH = recipe->wg_ph; |
|
623 Source_alkalinity = recipe->wg_total_alkalinity; |
|
624 } |
|
625 } |
|
626 |
|
627 int AT = recipe->sparge_acid_type; |
|
628 if (AT < 0 || AT >= my_acids.size()) { |
|
629 AT = 0; |
|
630 recipe->sparge_acid_type = 0; |
|
631 ui->sp_acidtypeEdit->setCurrentIndex(0); |
|
632 recipe->sparge_acid_perc = my_acids[0].AcidPrc; |
|
633 ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); |
|
634 } |
|
635 |
|
636 bool go_up = (val < (recipe->sparge_acid_amount * 1000.0)); |
|
637 bool loop = true; |
|
638 |
|
639 while (loop) { |
|
640 |
|
641 if (go_up) |
|
642 TargetpH += 0.001; |
|
643 else |
|
644 TargetpH -= 0.001; |
|
645 //qDebug() << " TargetpH" << TargetpH << "up" << go_up; |
|
646 |
|
647 // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH |
|
648 double r1 = pow(10, Source_pH - 6.35); |
|
649 double r2 = pow(10, Source_pH - 10.33); |
|
650 double d = 1 + r1 + r1 * r2; |
|
651 double f1 = 1 / d; |
|
652 double f3 = r1 * r2 / d; |
|
653 |
|
654 // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) |
|
655 double r143 = pow(10, 4.3 - 6.35); |
|
656 double r243 = pow(10, 4.3 - 10.33); |
|
657 double d43 = 1 + r143 + r143 * r243; |
|
658 double f143 = 1 / d43; |
|
659 double f343 = r143 * r243 / d43; |
|
660 |
|
661 // Step 4. Solve |
|
662 double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); |
|
663 |
|
664 // Step 5. Compute mole fractions at desired pH |
|
665 double r1g = pow(10, TargetpH - 6.35); |
|
666 double r2g = pow(10, TargetpH - 10.33); |
|
667 double dg = 1 + r1g + r1g * r2g; |
|
668 double f1g = 1 / dg; |
|
669 double f3g = r1g * r2g / dg; |
|
670 |
|
671 // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) |
|
672 double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l |
|
673 Acid += 0.01; // Add acid that would be required for distilled water. |
|
674 |
|
675 // Step 7. There is no step 7. |
|
676 |
|
677 // Step 8. Get the acid data. |
|
678 double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); |
|
679 |
|
680 // Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid. |
|
681 Acid /= fract; |
|
682 |
|
683 // Step 10. Multiply by molecular weight of the acid |
|
684 Acid *= my_acids[AT].MolWt; //mg |
|
685 |
|
686 // Step 11. Divide by Specific Gravity and Percentage to get the final ml. |
|
687 double RealSG = round(((my_acids[AT].AcidSG - 1000) * (recipe->sparge_acid_perc / 100)) + 1000); |
|
688 Acid = Acid / RealSG; //ml |
|
689 Acid *= recipe->sparge_volume; //ml acid total at 100% |
|
690 Acid /= my_acids[AT].AcidPrc / 100; //ml acid at supplied strength |
|
691 Acid = round(Acid * 100.0) / 100.0; |
|
692 recipe->sparge_acid_amount = Acid / 1000; |
|
693 //qDebug() << " acid" << recipe->sparge_acid_amount; |
|
694 |
|
695 if (go_up && (val > (recipe->sparge_acid_amount * 1000.0))) |
|
696 loop = false; |
|
697 else if (! go_up && (val < (recipe->sparge_acid_amount * 1000.0))) |
|
698 loop = false; |
|
699 |
|
700 //qDebug() << " test" << loop << go_up << val << recipe->sparge_acid_amount * 1000.0; |
|
701 } |
|
702 |
|
703 const QSignalBlocker blocker1(ui->sp_phEdit); |
|
704 recipe->sparge_ph = round(TargetpH * 100) / 100; |
|
705 ui->sp_phEdit->setValue(recipe->sparge_ph); |
|
706 ui->sp_phShow->setValue(recipe->sparge_ph); |
|
707 |
|
708 QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl; |
|
709 set_brewing_salt(w, val, MISC_USES_SPARGE); |
|
710 //qDebug() << " new" << recipe->sparge_ph << val; |
527 } |
711 } |
528 |
712 |
529 |
713 |
530 double EditRecipe::GetBUGU() |
714 double EditRecipe::GetBUGU() |
531 { |
715 { |