diff -r d9c78eb19728 -r b02aca4e926c src/Utils.cpp --- a/src/Utils.cpp Tue Jul 26 19:46:44 2022 +0200 +++ b/src/Utils.cpp Fri Jul 29 13:12:26 2022 +0200 @@ -391,10 +391,51 @@ } -double Utils::TinsethIBU(int Form, double SG, double Volume, double Amount, double T1, double T2, double Alpha) +double Utils::TinsethIBU(int Form, double SG, double Volume, double Amount, double T1, double T2, double Alpha, double Utilisation, double BU_factor) { double alpha = Alpha / 100.0; double mass = Amount * 1000.0; + double ibu = 0.0; + + /* + * Most suppliers of CO2 hop extract (not isomerized) use formulas + * like this. For now, manual set a utilisation factor of 35%. + */ + if (Form == HOP_FORMS_CO2EXTRACT) { + double utilisation = 0.35; // 35% + double factor_sg = 1.0; + double factor_bt = 1.0; + double bt = T2 - T1; + + /* Table from Wildabouthops */ + if (SG >= 1.150) + factor_sg = 1.3; + else if (SG >= 1.110) + factor_sg = 1.2; + else if (SG >= 1.080) + factor_sg = 1.1; + + if (bt > 60) { + factor_bt = ((bt - 60) / 300) + 1.0; + } else { + factor_bt = (bt / 60.0); + } + qDebug() << "factor_sg" << factor_sg << "factor_bt" << factor_bt; + + ibu = ((Utilisation / 100.0) * alpha * mass * 1000.0) / Volume; + ibu = (ibu * factor_bt) / factor_sg; + qDebug() << "TinsethIBU CO2" << Amount << Alpha << Volume << Utilisation << BU_factor << "SG" << SG << "T" << T1 << T2 << "ibu:" << ibu; + return ibu; + } + + if (Form == HOP_FORMS_ISOEXTRACT) { + + double dosageHl = ((100.0 / Utilisation) * (100.0 / Alpha) * 0.001) / BU_factor; // IBU per liter + qDebug() << (100 / Utilisation) << (100 / Alpha) << dosageHl << dosageHl * Volume << mass / (dosageHl * Volume); + ibu = mass / (dosageHl * Volume); + qDebug() << "TinsethIBU ISO" << Amount << Alpha << Volume << Utilisation << BU_factor << "ibu:" << ibu; + return ibu; + } /* * Basic Tinseth formula. @@ -404,7 +445,8 @@ double Bigness_factor = 1.65 * pow(0.000125, SG - 1); 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; + ibu = Bigness_factor * (BoilTime_factor2 - BoilTime_factor1) * AddedAlphaAcids; + qDebug() << "TinsethIBU nor" << SG << Amount << Alpha << Volume << Bigness_factor * (BoilTime_factor2 - BoilTime_factor1) << "ibu:" << ibu; /* * Correction for hop forms @@ -417,17 +459,16 @@ 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; + qDebug() << "boilIBU" << Form << SG << Volume << Amount << BoilTime_factor2 << BoilTime_factor1 << Alpha << "IBU:" << ibu; return ibu; } -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 Utils::toIBU(int Use, int Form, double preSG, double postSG, 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 Utilisation, double BU_factor) { double ibu = 0.0; @@ -440,7 +481,7 @@ * * http://scottjanish.com/the-locksmith-utilizing-bioengineered-yeast-and-high-bound-thiol-precersour-hops-and-phantasm-powder-to-thiol-drive-beer/ */ - ibu = TinsethIBU(Form, SG, Volume, Amount, 0, 60, Alpha) * (1 + my_factor_mashhop / 100.0); + ibu = TinsethIBU(Form, preSG, Volume, Amount, 0, 60, Alpha, Utilisation, BU_factor) * (1 + my_factor_mashhop / 100.0); } else if ((Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { /* @@ -449,10 +490,12 @@ double boil_time = Fulltime; if (Use == HOP_USEAT_BOIL) boil_time = Boiltime; - ibu = TinsethIBU(Form, SG, Volume, Amount, 0, boil_time, Alpha); + double fromSG = postSG - ((boil_time / Fulltime) * (postSG - preSG)); /* SG when this hop addition starts */ + double avgSG = (postSG + fromSG) / 2; /* Average SG during this addition */ + ibu = TinsethIBU(Form, avgSG, Volume, Amount, 0, boil_time, Alpha, Utilisation, BU_factor); /* - * Corrections for Mash and FWH + * Correction for FWH */ if (Use == HOP_USEAT_FWH) { ibu *= (1 + my_factor_fwh / 100.0); @@ -466,7 +509,7 @@ * Flameout, currently fixed 1 minute. */ double flameout_time = 1; - double fibu = TinsethIBU(Form, SG, Volume, Amount, boil_time, boil_time + flameout_time, Alpha); + double fibu = TinsethIBU(Form, postSG, Volume, Amount, boil_time, boil_time + flameout_time, Alpha, Utilisation, BU_factor); fibu *= IBU_reduction(98.0); //qDebug() << "during flameout" << fibu; nibu += fibu; @@ -476,19 +519,19 @@ * 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); + double wibu9 = TinsethIBU(Form, postSG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool9, Alpha, Utilisation, BU_factor); 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); + double wibu7 = TinsethIBU(Form, postSG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool7, Alpha, Utilisation, BU_factor); 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); + double wibu6 = TinsethIBU(Form, postSG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool6, Alpha, Utilisation, BU_factor); wibu6 *= IBU_reduction(63.0); //qDebug() << "during whirlpool6" << wibu6; nibu += wibu6; @@ -502,25 +545,25 @@ * At flameout, and only using extended calculation. */ double flameout_time = 1; - ibu = TinsethIBU(Form, SG, Volume, Amount, 0, flameout_time, Alpha); + ibu = TinsethIBU(Form, postSG, Volume, Amount, 0, flameout_time, Alpha, Utilisation, BU_factor); 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); + double wibu9 = TinsethIBU(Form, postSG, Volume, Amount, flameout_time, flameout_time + Whirlpool9, Alpha, Utilisation, BU_factor); 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); + double wibu7 = TinsethIBU(Form, postSG, Volume, Amount, flameout_time, flameout_time + Whirlpool7, Alpha, Utilisation, BU_factor); 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); + double wibu6 = TinsethIBU(Form, postSG, Volume, Amount, flameout_time, flameout_time + Whirlpool6, Alpha, Utilisation, BU_factor); wibu6 *= IBU_reduction(63.0); //qDebug() << "during whirlpool6" << wibu6; ibu += wibu6; @@ -531,33 +574,39 @@ * Hopstands. */ if (Whirlpool9) { - double wibu9 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool9, Alpha); + double wibu9 = TinsethIBU(Form, postSG, Volume, Amount, 0, Whirlpool9, Alpha, Utilisation, BU_factor); wibu9 *= IBU_reduction(87.0); //qDebug() << "during whirlpool9" << wibu9; ibu = wibu9; } if (Whirlpool7) { - double wibu7 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool7, Alpha); + double wibu7 = TinsethIBU(Form, postSG, Volume, Amount, 0, Whirlpool7, Alpha, Utilisation, BU_factor); wibu7 *= IBU_reduction(74.0); //qDebug() << "during whirlpool7" << wibu7; ibu = wibu7; } if (Whirlpool6) { - double wibu6 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool6, Alpha); + double wibu6 = TinsethIBU(Form, postSG, Volume, Amount, 0, Whirlpool6, Alpha, Utilisation, BU_factor); wibu6 *= IBU_reduction(63.0); //qDebug() << "during whirlpool6" << wibu6; ibu = wibu6; } + } else if (Use == HOP_USEAT_BOTTLING) { + /* + * Isomerized hop extracts to use at bottling. + * Assume 10% volume is lost during fermentation and transfers. + */ + ibu = TinsethIBU(Form, postSG, Volume * 0.9, Amount, 0, 0, Alpha, Utilisation, BU_factor); } double rc = round(ibu * 1000.0) / 1000.0; - qDebug() << "toIBU" << Use << Form << SG << Volume << Amount << Boiltime << Alpha << Method << Whirlpool9 << Whirlpool7 << Whirlpool6 << Fulltime << Cooltype << Coolparm1 << Coolparm2 << "rc:" << rc; + qDebug() << "toIBU" << Use << Form << preSG << postSG << Volume << Amount << Boiltime << Alpha << Method << Whirlpool9 << Whirlpool7 << Whirlpool6 << Fulltime << Cooltype << Coolparm1 << Coolparm2 << Utilisation << BU_factor << "rc:" << rc; return rc; } -double Utils::hopFlavourContribution(double bt, double vol, int use, double amount) +double Utils::hopFlavourContribution(double bt, double vol, int use, double amount, int form) { double result; @@ -576,9 +625,13 @@ } -double Utils::hopAromaContribution(double bt, double vol, int use, double amount) +double Utils::hopAromaContribution(double bt, double vol, int use, double amount, int form) { double result = 0.0; + double factor = 1.0; + + if (form == HOP_FORMS_CRYO) + factor = 2.0; if (use == HOP_USEAT_DRY_HOP) { result = 1.33; @@ -595,7 +648,7 @@ } else if (use == HOP_USEAT_AROMA) { // Aroma result = 1.2; } - return (result * amount * 1000.0) / vol; + return (result * amount * factor * 1000.0) / vol; }