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.

Fri, 08 Jul 2022 13:24:45 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 08 Jul 2022 13:24:45 +0200
changeset 343
67e645b9d23f
parent 342
0d14f1f565c4
child 344
9ffac162000c

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.

src/Utils.cpp file | annotate | diff | comparison | revisions
src/Utils.h file | annotate | diff | comparison | revisions
--- 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;
 }
 
 
--- 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.

mercurial