--- a/src/EditRecipeTab7.cpp Sun Jul 17 22:18:48 2022 +0200 +++ b/src/EditRecipeTab7.cpp Mon Jul 18 17:04:02 2022 +0200 @@ -265,10 +265,17 @@ Acidmg = Acid; Acid = Acid / my_acids[AT].AcidSG; Acid = round((Acid / (recipe->wa_acid_perc / 100.0)) * 100.0) / 100.0; + qDebug() << " Mash auto Acid final old ml:" << Acid; + Acid = Acidmg; + + double RealSG = round(((my_acids[AT].AcidSG - 1000) * (recipe->wa_acid_perc / 100)) + 1000); + Acid /= RealSG; + Acid /= my_acids[AT].AcidPrc / 100; + Acid = round(Acid * 100.0) / 100.0; qDebug() << " Mash auto Acid final ml:" << Acid; QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl; - brewing_salt_sub(w, Acid); + brewing_salt_sub(w, Acid, MISC_USES_MASH); ui->mw_acidvolEdit->setValue(Acid); bicarbonate = bicarbonate - protonDeficit * frac / liters; @@ -282,10 +289,12 @@ * Manual adjust acid, calculate resulting pH. */ double pHa = ph; // Mixed water pH. + double RealSG = round(((my_acids[AT].AcidSG - 1000) * (recipe->wa_acid_perc / 100)) + 1000); // Then calculate the new pH with added acids and malts qDebug() << " Mash pH:" << pHa; - Acid = my_acids[AT].AcidSG * (recipe->wa_acid_perc / 100.0); // ml + Acid = RealSG; Acid *= ui->mw_acidvolEdit->value(); + Acid *= my_acids[AT].AcidPrc / 100; Acid /= my_acids[AT].MolWt; // mg; Acidmg = Acid; @@ -420,80 +429,128 @@ qDebug() << "calcSparge()"; - const QSignalBlocker blocker1(ui->sp_sourceEdit); + const QSignalBlocker blocker2(ui->w1_spButton); + const QSignalBlocker blocker3(ui->w2_spButton); + const QSignalBlocker blocker4(ui->wg_spButton); // Select watersource or fallback to the first source. if (recipe->sparge_source == 1) { // Source 2 if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { Source_pH = recipe->w2_ph; Source_alkalinity = recipe->w2_total_alkalinity; + ui->sp_caEdit->setValue(recipe->w2_calcium); + ui->sp_mgEdit->setValue(recipe->w2_magnesium); + ui->sp_hco3Edit->setValue(Utils::Bicarbonate(recipe->w2_total_alkalinity, recipe->w2_ph)); + ui->sp_caco3Edit->setValue(recipe->w2_total_alkalinity); + ui->sp_naEdit->setValue(recipe->w2_sodium); + ui->sp_clEdit->setValue(recipe->w2_chloride); + ui->sp_so4Edit->setValue(recipe->w2_sulfate); + ui->sp_phShow->setValue(recipe->w2_ph); + ui->sp_hardnessEdit->setValue(Utils::Hardness(recipe->w2_calcium, recipe->w2_magnesium)); + ui->sp_raEdit->setValue(Utils::ResidualAlkalinity(recipe->w2_total_alkalinity, recipe->w2_calcium, recipe->w2_magnesium)); + ui->w2_spButton->setChecked(true); } else { - recipe->sparge_source = 0; // Source 1 - ui->sp_sourceEdit->setCurrentIndex(0); + recipe->sparge_source = 0; // Fallback to source 1 + ui->w1_spButton->setChecked(true); } } else if (recipe->sparge_source == 2) { // Mixed if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { Source_pH = recipe->wg_ph; Source_alkalinity = recipe->wg_total_alkalinity; + ui->sp_caEdit->setValue(recipe->wg_calcium); + ui->sp_mgEdit->setValue(recipe->wg_magnesium); + ui->sp_hco3Edit->setValue(Utils::Bicarbonate(recipe->wg_total_alkalinity, recipe->wg_ph)); + ui->sp_caco3Edit->setValue(recipe->wg_total_alkalinity); + ui->sp_naEdit->setValue(recipe->wg_sodium); + ui->sp_clEdit->setValue(recipe->wg_chloride); + ui->sp_so4Edit->setValue(recipe->wg_sulfate); + ui->sp_phShow->setValue(recipe->wg_ph); + ui->sp_hardnessEdit->setValue(Utils::Hardness(recipe->wg_calcium, recipe->wg_magnesium)); + ui->sp_raEdit->setValue(Utils::ResidualAlkalinity(recipe->wg_total_alkalinity, recipe->wg_calcium, recipe->wg_magnesium)); + ui->wg_spButton->setChecked(true); } else { - recipe->sparge_source = 0; // Source 1 - ui->sp_sourceEdit->setCurrentIndex(0); + recipe->sparge_source = 0; // Fallback to source 1 + ui->w1_spButton->setChecked(true); } } + if (recipe->sparge_source == 0) { + ui->sp_caEdit->setValue(recipe->w1_calcium); + ui->sp_mgEdit->setValue(recipe->w1_magnesium); + ui->sp_hco3Edit->setValue(Utils::Bicarbonate(recipe->w1_total_alkalinity, recipe->w1_ph)); + ui->sp_caco3Edit->setValue(recipe->w1_total_alkalinity); + ui->sp_naEdit->setValue(recipe->w1_sodium); + ui->sp_clEdit->setValue(recipe->w1_chloride); + ui->sp_so4Edit->setValue(recipe->w1_sulfate); + ui->sp_phShow->setValue(recipe->w1_ph); + ui->sp_hardnessEdit->setValue(Utils::Hardness(recipe->w1_calcium, recipe->w1_magnesium)); + ui->sp_raEdit->setValue(Utils::ResidualAlkalinity(recipe->w1_total_alkalinity, recipe->w1_calcium, recipe->w1_magnesium)); + ui->w1_spButton->setChecked(true); + } + // The spargewater is set. + + int AT = recipe->sparge_acid_type; + if (AT < 0 || AT >= my_acids.size()) { + AT = 0; + recipe->sparge_acid_type = 0; + ui->sp_acidtypeEdit->setCurrentIndex(0); + recipe->sparge_acid_perc = my_acids[0].AcidPrc; + ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); + } - // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH - double r1 = pow(10, Source_pH - 6.35); - double r2 = pow(10, Source_pH - 10.33); - double d = 1 + r1 + r1 * r2; - double f1 = 1 / d; - double f3 = r1 * r2 / d; + /* + * Auto calculate the required acid + */ + if (recipe->calc_acid) { + // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH + double r1 = pow(10, Source_pH - 6.35); + double r2 = pow(10, Source_pH - 10.33); + double d = 1 + r1 + r1 * r2; + double f1 = 1 / d; + double f3 = r1 * r2 / d; - // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) - double r143 = pow(10, 4.3 - 6.35); - double r243 = pow(10, 4.3 - 10.33); - double d43 = 1 + r143 + r143 * r243; - double f143 = 1 / d43; - double f343 = r143 * r243 / d43; + // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) + double r143 = pow(10, 4.3 - 6.35); + double r243 = pow(10, 4.3 - 10.33); + double d43 = 1 + r143 + r143 * r243; + double f143 = 1 / d43; + double f343 = r143 * r243 / d43; - // Step 4. Solve - //double Ct = (Source_alkalinity - 1000 * (pow(10, -4.3) - pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343)); - double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); - - // Step 5. Compute mole fractions at desired pH - double r1g = pow(10, TargetpH - 6.35); - double r2g = pow(10, TargetpH - 10.33); - double dg = 1 + r1g + r1g * r2g; - double f1g = 1 / dg; - double f3g = r1g * r2g / dg; + // Step 4. Solve + double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); - // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) - double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l - Acid += 0.01; // Add acid that would be required for distilled water. + // Step 5. Compute mole fractions at desired pH + double r1g = pow(10, TargetpH - 6.35); + double r2g = pow(10, TargetpH - 10.33); + double dg = 1 + r1g + r1g * r2g; + double f1g = 1 / dg; + double f3g = r1g * r2g / dg; + + // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) + double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l + Acid += 0.01; // Add acid that would be required for distilled water. + + // Step 8. Get the acid data. + double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); - //Step 8. Get the acid data. - int AT = recipe->sparge_acid_type; - if (AT < 0 || AT >= my_acids.size()) { - AT = 0; - recipe->sparge_acid_type = 0; - ui->sp_acidtypeEdit->setCurrentIndex(0); - recipe->sparge_acid_perc = my_acids[0].AcidPrc; - ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); + // 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 *= my_acids[AT].MolWt; //mg + + // Step 11. Divide by Specific Gravity and Percentage to get the final ml. + double RealSG = round(((my_acids[AT].AcidSG - 1000) * (recipe->sparge_acid_perc / 100)) + 1000); + Acid = Acid / RealSG; //ml + Acid *= recipe->sparge_volume; //ml acid total at 100% + Acid /= my_acids[AT].AcidPrc / 100; //ml acid at supplied strength + Acid = round(Acid * 100.0) / 100.0; + recipe->sparge_acid_amount = Acid / 1000; + QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl; + brewing_salt_sub(w, Acid, MISC_USES_SPARGE); // Put it in the miscs table. + ui->sp_acidvolEdit->setValue(Acid); } - double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].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 *= my_acids[AT].MolWt; //mg - - // Step 11. Divide by Specific Gravity and Percentage to get the final ml. - Acid = Acid / my_acids[AT].AcidSG / (recipe->sparge_acid_perc / 100); //ml - Acid *= recipe->sparge_volume; //ml acid total - Acid = round(Acid * 100.0) / 100.0; - recipe->sparge_acid_amount = Acid / 1000; - ui->sp_acidvolEdit->setValue(Acid); - + ui->sp_phShow->setValue(recipe->sparge_ph); // Finally calculate the estimate preboil pH recipe->preboil_ph = -log10(((pow(10, -recipe->mash_ph) * recipe->wg_amount) + (pow(10, -recipe->sparge_ph) * recipe->sparge_volume)) / (recipe->wg_amount + recipe->sparge_volume)); @@ -501,19 +558,36 @@ } -void EditRecipe::sp_source_changed(int val) +void EditRecipe::sp_group_changed(int val) { - recipe->sparge_source = val; - calcSparge(); - is_changed(); + if (val != recipe->sparge_source) { + qDebug() << "sp_group_changed" << val; + recipe->sparge_source = val; + calcSparge(); + is_changed(); + } } void EditRecipe::sp_type_changed(int val) { + if (val == recipe->sparge_acid_type) + return; + + qDebug() << "sp_type_changed" << val << "old" << recipe->sparge_acid_type; + /* + * First remove current acid. + */ + QString w = my_acids[recipe->sparge_acid_type].name_en + ' ' + my_acids[recipe->sparge_acid_type].name_nl; + brewing_salt_sub(w, 0, MISC_USES_SPARGE); + recipe->sparge_acid_type = val; + w = my_acids[recipe->sparge_acid_type].name_en + ' ' + my_acids[recipe->sparge_acid_type].name_nl; + recipe->sparge_acid_perc = my_acids[val].AcidPrc; ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); + brewing_salt_sub(w, ui->sp_acidvolEdit->value(), MISC_USES_SPARGE); // For now, set old amount. + calcSparge(); is_changed(); } @@ -527,6 +601,116 @@ } +void EditRecipe::sp_acid_changed(double val) +{ + if (recipe->calc_acid) + return; + + qDebug() << "sp_acid_changed" << val << recipe->sparge_acid_amount * 1000.0; + + double TargetpH = recipe->sparge_ph; + double Source_pH = recipe->w1_ph; + double Source_alkalinity = recipe->w1_total_alkalinity; + + if (recipe->sparge_source == 1) { // Source 2 + if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { + Source_pH = recipe->w2_ph; + Source_alkalinity = recipe->w2_total_alkalinity; + } + } else if (recipe->sparge_source == 2) { // Mixed + if (recipe->w2_ph > 0.0 && recipe->w2_amount > 0) { + Source_pH = recipe->wg_ph; + Source_alkalinity = recipe->wg_total_alkalinity; + } + } + + int AT = recipe->sparge_acid_type; + if (AT < 0 || AT >= my_acids.size()) { + AT = 0; + recipe->sparge_acid_type = 0; + ui->sp_acidtypeEdit->setCurrentIndex(0); + recipe->sparge_acid_perc = my_acids[0].AcidPrc; + ui->sp_acidpercEdit->setValue(recipe->sparge_acid_perc); + } + + bool go_up = (val < (recipe->sparge_acid_amount * 1000.0)); + bool loop = true; + + while (loop) { + + if (go_up) + TargetpH += 0.001; + else + TargetpH -= 0.001; + //qDebug() << " TargetpH" << TargetpH << "up" << go_up; + + // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH + double r1 = pow(10, Source_pH - 6.35); + double r2 = pow(10, Source_pH - 10.33); + double d = 1 + r1 + r1 * r2; + double f1 = 1 / d; + double f3 = r1 * r2 / d; + + // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) + double r143 = pow(10, 4.3 - 6.35); + double r243 = pow(10, 4.3 - 10.33); + double d43 = 1 + r143 + r143 * r243; + double f143 = 1 / d43; + double f343 = r143 * r243 / d43; + + // Step 4. Solve + double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); + + // Step 5. Compute mole fractions at desired pH + double r1g = pow(10, TargetpH - 6.35); + double r2g = pow(10, TargetpH - 10.33); + double dg = 1 + r1g + r1g * r2g; + double f1g = 1 / dg; + double f3g = r1g * r2g / dg; + + // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) + double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l + Acid += 0.01; // Add acid that would be required for distilled water. + + // Step 7. There is no step 7. + + // Step 8. Get the acid data. + double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].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 *= my_acids[AT].MolWt; //mg + + // Step 11. Divide by Specific Gravity and Percentage to get the final ml. + double RealSG = round(((my_acids[AT].AcidSG - 1000) * (recipe->sparge_acid_perc / 100)) + 1000); + Acid = Acid / RealSG; //ml + Acid *= recipe->sparge_volume; //ml acid total at 100% + Acid /= my_acids[AT].AcidPrc / 100; //ml acid at supplied strength + Acid = round(Acid * 100.0) / 100.0; + recipe->sparge_acid_amount = Acid / 1000; + //qDebug() << " acid" << recipe->sparge_acid_amount; + + if (go_up && (val > (recipe->sparge_acid_amount * 1000.0))) + loop = false; + else if (! go_up && (val < (recipe->sparge_acid_amount * 1000.0))) + loop = false; + + //qDebug() << " test" << loop << go_up << val << recipe->sparge_acid_amount * 1000.0; + } + + const QSignalBlocker blocker1(ui->sp_phEdit); + recipe->sparge_ph = round(TargetpH * 100) / 100; + ui->sp_phEdit->setValue(recipe->sparge_ph); + ui->sp_phShow->setValue(recipe->sparge_ph); + + QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl; + set_brewing_salt(w, val, MISC_USES_SPARGE); + //qDebug() << " new" << recipe->sparge_ph << val; +} + + double EditRecipe::GetBUGU() { double gu = (recipe->est_og - 1) * 1000; @@ -555,6 +739,10 @@ ui->mw_phEdit->setButtonSymbols(recipe->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons); ui->mw_acidvolEdit->setReadOnly(recipe->calc_acid); ui->mw_acidvolEdit->setButtonSymbols(recipe->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows); + ui->sp_phEdit->setReadOnly(! recipe->calc_acid); + ui->sp_phEdit->setButtonSymbols(recipe->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons); + ui->sp_acidvolEdit->setReadOnly(recipe->calc_acid); + ui->sp_acidvolEdit->setButtonSymbols(recipe->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows); is_changed(); calcWater(); } @@ -581,7 +769,7 @@ qDebug() << "on_mw_acid_changed" << val; QString w = my_acids[recipe->wa_acid_name].name_en + ' ' + my_acids[recipe->wa_acid_name].name_nl; - set_brewing_salt(w, val); + set_brewing_salt(w, val, MISC_USES_MASH); } @@ -595,14 +783,14 @@ * First remove current acid. */ QString w = my_acids[recipe->wa_acid_name].name_en + ' ' + my_acids[recipe->wa_acid_name].name_nl; - brewing_salt_sub(w, 0); + brewing_salt_sub(w, 0, MISC_USES_MASH); recipe->wa_acid_name = val; w = my_acids[recipe->wa_acid_name].name_en + ' ' + my_acids[recipe->wa_acid_name].name_nl; recipe->wa_acid_perc = my_acids.at(val).AcidPrc; ui->mw_acidpercEdit->setValue(my_acids.at(val).AcidPrc); - brewing_salt_sub(w, ui->mw_acidvolEdit->value()); // For now, set old amount. + brewing_salt_sub(w, ui->mw_acidvolEdit->value(), MISC_USES_MASH); // For now, set old amount. is_changed(); calcWater(); @@ -812,12 +1000,17 @@ } -void EditRecipe::wb_cacl2_changed(double val) { set_brewing_salt("CaCl2", val); } -void EditRecipe::wb_caso4_changed(double val) { set_brewing_salt("CaSO4", val); } -void EditRecipe::wb_mgso4_changed(double val) { set_brewing_salt("MgSO4", val); } -void EditRecipe::wb_nacl_changed(double val) { set_brewing_salt("NaCl", val); } -void EditRecipe::wb_mgcl2_changed(double val) { set_brewing_salt("MgCl2", val); } -void EditRecipe::wb_nahco3_changed(double val) { set_brewing_salt("NaHCO3", val); } -void EditRecipe::wb_caco3_changed(double val) { set_brewing_salt("CaCO3", val); } +void EditRecipe::wb_cacl2_changed(double val) { set_brewing_salt("CaCl2", val, MISC_USES_MASH); } +void EditRecipe::wb_caso4_changed(double val) { set_brewing_salt("CaSO4", val, MISC_USES_MASH); } +void EditRecipe::wb_mgso4_changed(double val) { set_brewing_salt("MgSO4", val, MISC_USES_MASH); } +void EditRecipe::wb_nacl_changed(double val) { set_brewing_salt("NaCl", val, MISC_USES_MASH); } +void EditRecipe::wb_mgcl2_changed(double val) { set_brewing_salt("MgCl2", val, MISC_USES_MASH); } +void EditRecipe::wb_nahco3_changed(double val) { set_brewing_salt("NaHCO3", val, MISC_USES_MASH); } +void EditRecipe::wb_caco3_changed(double val) { set_brewing_salt("CaCO3", val, MISC_USES_MASH); } +void EditRecipe::sp_cacl2_changed(double val) { set_brewing_salt("CaCl2", val, MISC_USES_SPARGE); } +void EditRecipe::sp_caso4_changed(double val) { set_brewing_salt("CaSO4", val, MISC_USES_SPARGE); } +void EditRecipe::sp_mgso4_changed(double val) { set_brewing_salt("MgSO4", val, MISC_USES_SPARGE); } +void EditRecipe::sp_nacl_changed(double val) { set_brewing_salt("NaCl", val, MISC_USES_SPARGE); } +void EditRecipe::sp_mgcl2_changed(double val) { set_brewing_salt("MgCl2", val, MISC_USES_SPARGE); }