# HG changeset patch # User Michiel Broek # Date 1657279485 -7200 # Node ID 67e645b9d23f1cbc34bf72c588e7c43b4723ee10 # Parent 0d14f1f565c4dd4a2806be144f84b32999272521 Removed old experiment to calculate flameout and whirlpool ibu's. Changed Tinseth calculation to use a time window so you can select the ibu contribution during a specified time. Added ibu_method Tinset++. Added calculation extensions for Tinseth++, this calculates the flameout and whirlpool effects on all hops. During cooling is to be done. diff -r 0d14f1f565c4 -r 67e645b9d23f src/Utils.cpp --- a/src/Utils.cpp Thu Jul 07 19:42:19 2022 +0200 +++ b/src/Utils.cpp Fri Jul 08 13:24:45 2022 +0200 @@ -391,7 +391,7 @@ } -double Utils::TinsethIBU(double SG, double Volume, double Amount, double Time, double Alpha) +double Utils::TinsethIBU(int Form, double SG, double Volume, double Amount, double T1, double T2, double Alpha) { double alpha = Alpha / 100.0; double mass = Amount * 1000.0; @@ -402,10 +402,26 @@ */ double AddedAlphaAcids = (alpha * mass * 1000) / Volume; double Bigness_factor = 1.65 * pow(0.000125, SG - 1); - double BoilTime_factor = ((1 - exp(-0.04 * Time)) / 4.15); - double ibu = Bigness_factor * BoilTime_factor * AddedAlphaAcids; + double BoilTime_factor1 = ((1 - exp(-0.04 * T1)) / 4.15); + double BoilTime_factor2 = ((1 - exp(-0.04 * T2)) / 4.15); + double ibu = Bigness_factor * (BoilTime_factor2 - BoilTime_factor1) * AddedAlphaAcids; - qDebug() << "boilIBU" << SG << Volume << Amount << Time << Alpha << "IBU:" << ibu; + /* + * Correction for hop forms + */ + if (Form == HOP_FORMS_PELLET) { + ibu *= (1 + my_factor_pellet / 100.0); + } else if (Form == HOP_FORMS_PLUG) { + ibu *= (1 + my_factor_plug / 100.0); + } else if (Form == HOP_FORMS_LEAF_WET) { + ibu *= (1 + my_factor_wethop / 100.0); // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py + } else if (Form == HOP_FORMS_CRYO) { + ibu *= (1 + my_factor_cryohop / 100.0); + } else if (Form == HOP_FORMS_EXTRACT) { + // Nothing for now. + } + + qDebug() << "boilIBU" << Form << SG << Volume << Amount << T1 << T2 << Alpha << "IBU:" << ibu; return ibu; } @@ -413,63 +429,21 @@ double Utils::toIBU(int Use, int Form, double SG, double Volume, double Amount, double Boiltime, double Alpha, int Method, double Whirlpool9, double Whirlpool7, double Whirlpool6, double Fulltime, int Cooltype, double Coolparm1, double Coolparm2) { - double steep_time = 0; /* Total time a hop in the kettle. */ double loss_boiltemp = 1.0; /* Loss due to the lower boil temperature at higher altitude. */ double ibu = 0.0, whirlibus = 0.0; double alpha = Alpha / 100.0; double mass = Amount * 1000.0; - // Ideas from Zymurgy March-April 2018. These are not exact formulas! - if (Use == HOP_USEAT_AROMA) { - if (Whirlpool9) { // Flameout hops are 2 minutes in this range. - whirlibus += (alpha * mass * 20) / Volume * (2.0 / 50.0); - } - if (Whirlpool7) { // Flameout hops are 4 minutes in this range. - whirlibus += (alpha * mass * 6) / Volume * (4.0 / 50.0); - } - // Experiment. -// double wibu = boilIBU(Form, SG, Volume, Amount, 6, Alpha, Method); // IBU's for 6 minutes full -// double fibu = wibu * 0.067 * IBU_reduction(94); // Add timed segments -// fibu += wibu * 0.1 * IBU_reduction(84); -// fibu += wibu * 0.167 * IBU_reduction(74); -// fibu += wibu * 0.250 * IBU_reduction(64); -// fibu += wibu * 0.417 * IBU_reduction(54); - //qDebug() << " 94" << wibu * 0.067 * IBU_reduction(94); - //qDebug() << " 84" << wibu * 0.1 * IBU_reduction(84); - //qDebug() << " 74" << wibu * 0.167 * IBU_reduction(74); - //qDebug() << " 64" << wibu * 0.250 * IBU_reduction(64); - //qDebug() << " 54" << wibu * 0.417 * IBU_reduction(54); -// qDebug() << "flamout" << wibu << fibu; - } - if (Use == HOP_USEAT_WHIRLPOOL) { // Flameout or any whirlpool - if (Whirlpool9) { - // 20 mg/l/50 min - whirlibus += (alpha * mass * 20) / Volume * (Whirlpool9 / 50.0); - //qDebug() << "Whirlpool9" << alpha * mass * 20 << " liter:" << liters << " time:" << Whirlpool9 << " ibu" << (alpha * mass * 20) / liters * (Whirlpool9 / 50.0); - } - if (Whirlpool7) { - // 6 mg/l/50 min - whirlibus += (alpha * mass * 6) / Volume * (Whirlpool7 / 50.0); - //qDebug() << "Whirlpool7" << alpha * mass * 6 << " liter:" << liters << " time:" << Whirlpool7 << " ibu" << (alpha * mass * 6) / liters * (Whirlpool7 / 50.0); - } - if (Whirlpool6) { - // 2 mg/l/50 min - whirlibus += (alpha * mass * 2) / Volume * (Whirlpool6 / 50.0); - } -// double wibu = boilIBU(Form, SG, Volume, Amount, Boiltime, Alpha, Method); -// qDebug() << "whirpool" << wibu << wibu * IBU_reduction(74); - } - - if ((Use == HOP_USEAT_MASH) || (Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { - steep_time += Boiltime; - } - /* * IBU's from hops during Mash, FWH and boil. */ if ((Use == HOP_USEAT_MASH) || (Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { - ibu = TinsethIBU(SG, Volume, Amount, Fulltime, Alpha); + double boil_time = Fulltime; + if (Use == HOP_USEAT_BOIL) + boil_time = Boiltime; + ibu = TinsethIBU(Form, SG, Volume, Amount, 0, boil_time, Alpha); + /* * Corrections for Mash and FWH */ @@ -480,38 +454,104 @@ ibu *= (1 + my_factor_fwh / 100.0); } - /* - * Correction for hop forms - */ - if (Form == HOP_FORMS_PELLET) { - ibu *= (1 + my_factor_pellet / 100.0); - } else if (Form == HOP_FORMS_PLUG) { - ibu *= (1 + my_factor_plug / 100.0); - } else if (Form == HOP_FORMS_LEAF_WET) { - ibu *= (1 + my_factor_wethop / 100.0); // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py - } else if (Form == HOP_FORMS_CRYO) { - ibu *= (1 + my_factor_cryohop / 100.0); - } else if (Form == HOP_FORMS_EXTRACT) { - // Nothing for now. + if (Method > 0) { + double nibu = ibu; + loss_boiltemp = IBU_reduction(boilPoint()); + nibu *= loss_boiltemp; + qDebug() << "ibu" << nibu << "loss_boiltemp" << loss_boiltemp; + + /* + * Flameout, currently fixed 1 minute. + */ + double flameout_time = 1; + double fibu = TinsethIBU(Form, SG, Volume, Amount, boil_time, boil_time + flameout_time, Alpha); + fibu *= IBU_reduction(98.0); + qDebug() << "during flameout" << fibu; + nibu += fibu; + + // Add this hop during cooling + /* + * Hopstands, this boil hop adds some IBU's too. + */ + if (Whirlpool9) { + double wibu9 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool9, Alpha); + wibu9 *= IBU_reduction(87.0); + //qDebug() << "during whirlpool9" << wibu9; + nibu += wibu9; + } + if (Whirlpool7) { + double wibu7 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool7, Alpha); + wibu7 *= IBU_reduction(74.0); + //qDebug() << "during whirlpool7" << wibu7; + nibu += wibu7; + } + if (Whirlpool6) { + double wibu6 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool6, Alpha); + wibu6 *= IBU_reduction(63.0); + //qDebug() << "during whirlpool6" << wibu6; + nibu += wibu6; + } + qDebug() << "Old IBU" << ibu << "New IBU" << nibu; + ibu = nibu; } - if (Method > 0) { - loss_boiltemp = IBU_reduction(boilPoint()); - ibu *= loss_boiltemp; - qDebug() << "ibu" << ibu << "loss_boiltemp" << loss_boiltemp; - } + } else if ((Use == HOP_USEAT_AROMA) && (Method > 0)) { + /* + * At flameout, and only using extended calculation. + */ + double flameout_time = 1; + ibu = TinsethIBU(Form, SG, Volume, Amount, 0, flameout_time, Alpha); + ibu *= IBU_reduction(98.0); + /* + * Hopstands, this flameout hop adds some IBU's too. + */ + if (Whirlpool9) { + double wibu9 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool9, Alpha); + wibu9 *= IBU_reduction(87.0); + //qDebug() << "during whirlpool9" << wibu9; + ibu += wibu9; + } + if (Whirlpool7) { + double wibu7 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool7, Alpha); + wibu7 *= IBU_reduction(74.0); + //qDebug() << "during whirlpool7" << wibu7; + ibu += wibu7; + } + if (Whirlpool6) { + double wibu6 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool6, Alpha); + wibu6 *= IBU_reduction(63.0); + //qDebug() << "during whirlpool6" << wibu6; + ibu += wibu6; + } -// } else if (Use == HOP_USEAT_AROMA) { + } else if ((Use == HOP_USEAT_WHIRLPOOL) && (Method > 0)) { /* - * At flameout. The cooling method is important. - * Emersion chiller, Counterflow chiller, Au bain marie or natural. - * Assume the hop is removed for all methods except Emersion chilling. - */ - } else { -// qDebug() << "whirlibus" << whirlibus << Use; + * Hopstands. + */ + if (Whirlpool9) { + double wibu9 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool9, Alpha); + wibu9 *= IBU_reduction(87.0); + //qDebug() << "during whirlpool9" << wibu9; + ibu = wibu9; + } + if (Whirlpool7) { + double wibu7 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool7, Alpha); + wibu7 *= IBU_reduction(74.0); + //qDebug() << "during whirlpool7" << wibu7; + ibu = wibu7; + } + if (Whirlpool6) { + double wibu6 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool6, Alpha); + wibu6 *= IBU_reduction(63.0); + //qDebug() << "during whirlpool6" << wibu6; + ibu = wibu6; + } } - return round((ibu + whirlibus) * 100.0) / 100.0; + double rc = round((ibu + whirlibus) * 100.0) / 100.0; + + qDebug() << "toIBU" << Use << Form << SG << Volume << Amount << Boiltime << Alpha << Method << Whirlpool9 << Whirlpool7 << Whirlpool6 << Fulltime << Cooltype << Coolparm1 << Coolparm2 << "rc:" << rc; + return rc; } diff -r 0d14f1f565c4 -r 67e645b9d23f src/Utils.h --- a/src/Utils.h Thu Jul 07 19:42:19 2022 +0200 +++ b/src/Utils.h Fri Jul 08 13:24:45 2022 +0200 @@ -55,14 +55,19 @@ /** * @brief Calculate IBU's of a hop at 100°C using the Tinseth formula. + * Use a time window, for example T1 = 60, T2 = 70 gives the ibus increment for + * a hop that is already 60 minutes in the wort for the next 10 minutes time. + * This is useful for calculating flamout, chilling and hopstands. + * @param Form HOP_FORMS_PELLET HOP_FORMS_PLUG HOP_FORMS_LEAF HOP_FORMS_LEAF_WET HOP_FORMS_CRYO HOP_FORMS_EXTRACT * @param SG the density * @param Volume in liters * @param Amount in kilograms - * @param Time in minutes + * @param T1 in minutes start of time window + * @param T2 in minutes end of time window * @param Alpha in procent * @return The calculated IBU's */ - double TinsethIBU(double SG, double Volume, double Amount, double Time, double Alpha); + double TinsethIBU(int Form, double SG, double Volume, double Amount, double T1, double T2, double Alpha); /** * @brief Calculate IBU's of a hop during the whole production process.