--- a/src/Utils.cpp Tue Jul 05 14:31:39 2022 +0200 +++ b/src/Utils.cpp Thu Jul 07 08:51:57 2022 +0200 @@ -360,129 +360,163 @@ return round((og * 1000 - fg * 1000) * factor * 100) / 100; } + +double Utils::brewery_hPa() +{ + return Seapressure * exp( - MolMassAir * Gravacc * my_height / (Gasconst * (20 + Kelvin))); +} + + +double Utils::boilPoint() +{ + double P2 = brewery_hPa(); + + return (1 / (1/(100 + Kelvin) - Gasconst * log(P2 / Seapressure) / EoVwater)) - Kelvin; +} + + /* + * Formula is from the 'Mash Made Easy' spreadsheet. + * https://mashmadeeasy.yolasite.com/ + * https://www.homebrewtalk.com/threads/a-rather-simplified-whirlpool-hop-ibu-computation-method.701093/ + */ +double Utils::IBU_reduction(double Tc) +{ + return (2.39 * pow(10, 11) * pow(2.71828182846, - (9773 / (Tc + Kelvin))) ) * 1/1.009231744; +} + + double Utils::boilIBU(int Form, double SG, double Volume, double Amount, double Time, double Alpha, int Method) { + double ibu = 0.0, sgfactor, boilfactor; + + double alpha = Alpha / 100.0; + double mass = Amount * 1000.0; + if (Method == 0) { // Tinseth -*/ /* http://realbeer.com/hops/research.html */ -/* AddedAlphaAcids = (alpha * mass * 1000) / liters; - Bigness_factor = 1.65 * pow(0.000125, gravity - 1); - BoilTime_factor = ((1 - exp(-0.04 * time)) / 4.15); - utiisation = Bigness_factor * BoilTime_factor; - ibu = round((utiisation * AddedAlphaAcids * fmoment * pfactor + whirlibus) * 100) / 100; + /* http://realbeer.com/hops/research.html */ + 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); + ibu = Bigness_factor * BoilTime_factor * AddedAlphaAcids; } if (Method == 2) { // Daniels if (Form == 2) // Leaf - boilfactor = -(0.0041 * time * time) + (0.6162 * time) + 1.5779; + boilfactor = -(0.0041 * Time * Time) + (0.6162 * Time) + 1.5779; else - boilfactor = -(0.0051 * time * time) + (0.7835 * time) + 1.9348; - if (gravity < 1050) + boilfactor = -(0.0051 * Time * Time) + (0.7835 * Time) + 1.9348; + if (SG < 1.050) sgfactor = 0; else - sgfactor = (gravity - 1050) / 200; - ibu = round((fmoment * ((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor))) + whirlibus) * 100) / 100; + sgfactor = ((SG * 1000) - 1050) / 200; + ibu = (mass * (alpha * 100) * boilfactor * 0.1) / (Volume * (1 + sgfactor)); } if (Method == 1) { // Rager - boilfactor = fmoment * 18.11 + 13.86 * tanh((time * 31.32) / 18.27); - if (gravity < 1050) + boilfactor = 18.11 + 13.86 * tanh((Time * 31.32) / 18.27); + if (SG < 1.050) sgfactor = 0; else - sgfactor = (gravity - 1050) / 200; - ibu = round(((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor)) + whirlibus) * 100) / 100; + sgfactor = ((SG * 1000) - 1050) / 200; + ibu = (mass * (alpha * 100) * boilfactor * 0.1) / (Volume * (1 + sgfactor)); } + + //qDebug() << "boilIBU" << Form << SG << Volume << Amount << Time << Alpha << Method << "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) { - double fmoment = 1.0, pfactor = 1.0, ibu = 0, boilfactor; - double sgfactor, AddedAlphaAcids, Bigness_factor, BoilTime_factor, utiisation; - - double gravity = SG; - double liters = Volume; + double ibu = 0.0, whirlibus = 0.0; double alpha = Alpha / 100.0; double mass = Amount * 1000.0; - double time = Boiltime; - - if ((Use == HOP_USEAT_AROMA) || (Use == HOP_USEAT_WHIRLPOOL) || (Use == HOP_USEAT_DRY_HOP) || (Use == HOP_USEAT_BOTTLING)) { - fmoment = 0.0; - } else if (Use == HOP_USEAT_MASH) { - fmoment += my_factor_mashhop / 100.0; // Brouwhulp - time = Fulltime; // Take the full boiltime - } else if (Use == HOP_USEAT_FWH) { - fmoment += my_factor_fwh / 100.0; // Brouwhulp, Louis, Ozzie - time = Fulltime; - } - - if (Form == HOP_FORMS_PELLET) { - pfactor += my_factor_pellet / 100.0; - } else if (Form == HOP_FORMS_PLUG) { - pfactor += my_factor_plug / 100.0; - } else if (Form == HOP_FORMS_LEAF_WET) { - pfactor += my_factor_wethop / 100.0; // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py - } else if (Form == HOP_FORMS_CRYO) { - pfactor += my_factor_cryohop / 100.0; - } else if (Form == HOP_FORMS_EXTRACT) { - // Nothing for now. - } // Ideas from Zymurgy March-April 2018. These are not exact formulas! - double whirlibus = 0.0; if (Use == HOP_USEAT_AROMA) { if (Whirlpool9) { // Flameout hops are 2 minutes in this range. - whirlibus += (alpha * mass * 20) / liters * (2.0 / 50.0); + whirlibus += (alpha * mass * 20) / Volume * (2.0 / 50.0); } if (Whirlpool7) { // Flameout hops are 4 minutes in this range. - whirlibus += (alpha * mass * 6) / liters * (4.0 / 50.0); + 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) / liters * (Whirlpool9 / 50.0); + 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) / liters * (Whirlpool7 / 50.0); + 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) / liters * (Whirlpool6 / 50.0); + 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 (Method == 0) { // Tinseth - /* http://realbeer.com/hops/research.html */ - AddedAlphaAcids = (alpha * mass * 1000) / liters; - Bigness_factor = 1.65 * pow(0.000125, gravity - 1); - BoilTime_factor = ((1 - exp(-0.04 * time)) / 4.15); - utiisation = Bigness_factor * BoilTime_factor; - ibu = round((utiisation * AddedAlphaAcids * fmoment * pfactor + whirlibus) * 100) / 100; - } - if (Method == 2) { // Daniels - if (Form == 2) // Leaf - boilfactor = -(0.0041 * time * time) + (0.6162 * time) + 1.5779; - else - boilfactor = -(0.0051 * time * time) + (0.7835 * time) + 1.9348; - if (gravity < 1050) - sgfactor = 0; - else - sgfactor = (gravity - 1050) / 200; - ibu = round((fmoment * ((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor))) + whirlibus) * 100) / 100; - } - if (Method == 1) { // Rager - boilfactor = fmoment * 18.11 + 13.86 * tanh((time * 31.32) / 18.27); - if (gravity < 1050) - sgfactor = 0; - else - sgfactor = (gravity - 1050) / 200; - ibu = round(((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor)) + whirlibus) * 100) / 100; + /* + * IBU's from hops during Mash, FWH and boil. + */ + if ((Use == HOP_USEAT_MASH) || (Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { + ibu = boilIBU(Form, SG, Volume, Amount, Fulltime, Alpha, Method); + /* + * Corrections for Mash and FWH + */ + if (Use == HOP_USEAT_MASH) { + ibu *= (1 + my_factor_mashhop / 100.0); + } + if (Use == HOP_USEAT_FWH) { + 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. + } +// ibu *= IBU_reduction(boilPoint()); +// qDebug() << "ibu" << ibu << IBU_reduction(boilPoint()); + +// } else if (Use == HOP_USEAT_AROMA) { + /* + * 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; } - return ibu; + + return round((ibu + whirlibus) * 100.0) / 100.0; }