Added calcMash(). Display total mash time.

Wed, 20 Apr 2022 12:19:36 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 20 Apr 2022 12:19:36 +0200
changeset 148
ba25a566b100
parent 147
af1386a6ece7
child 149
d73719fa2ebb

Added calcMash(). Display total mash time.

src/EditRecipe.cpp file | annotate | diff | comparison | revisions
src/EditRecipe.h file | annotate | diff | comparison | revisions
src/EditRecipeTab2.cpp file | annotate | diff | comparison | revisions
src/EditRecipeTab6.cpp file | annotate | diff | comparison | revisions
ui/EditRecipe.ui file | annotate | diff | comparison | revisions
--- a/src/EditRecipe.cpp	Tue Apr 19 22:27:18 2022 +0200
+++ b/src/EditRecipe.cpp	Wed Apr 20 12:19:36 2022 +0200
@@ -727,6 +727,7 @@
     refreshMiscs();
     refreshYeasts();
     calcYeast();
+    calcMash();
     refreshMashs();
     refreshWaters();
     calcWater();
--- a/src/EditRecipe.h	Tue Apr 19 22:27:18 2022 +0200
+++ b/src/EditRecipe.h	Wed Apr 20 12:19:36 2022 +0200
@@ -234,6 +234,8 @@
     int		miscs_row;
     int		yeasts_row;
     int		mashs_row;
+    double	mashs_kg;		///< Kg fermentables in the mash.
+    int		mashs_time;		///< Total mash time.
     double	preboil_sg;
 };
 
@@ -396,6 +398,9 @@
     double GetBUGU();
     double GetOptSO4Clratio();
     void calcYeast();
+    double infusionVol(double step_infused, double step_mashkg, double infuse_temp, double step_temp, double last_temp);
+    double decoctionVol(double step_volume, double step_temp, double prev_temp);
+    void calcMash();
 };
 
 #endif
--- a/src/EditRecipeTab2.cpp	Tue Apr 19 22:27:18 2022 +0200
+++ b/src/EditRecipeTab2.cpp	Wed Apr 20 12:19:36 2022 +0200
@@ -154,7 +154,6 @@
     double	addedmass = 0;		// Added mass after boil
     double	mvol = 0;		// Mash volume
     double	lintner = 0;		// Total recipe lintner
-    double	mashkg = 0;
     double	sugarsf = 0;		// fermentable sugars mash + boil
     double	sugarsm = 0;		// fermentable sugars in mash
     double	sugardensity = 1.611;	// kg/l in solution
@@ -213,10 +212,12 @@
     	ui->est_abvShow->setValue(0);
     	recipe->est_abv = 0;
     	ui->calEdit->setValue(0);
+	recipe->mashs_kg = 0;
 	return;
     }
     qDebug() << "  adjust to 100" << recipe->fermentables_use100;
 
+    recipe->mashs_kg = 0;
     for (i = 0; i < recipe->fermentables.size(); i++) {
 	if (recipe->fermentables.at(i).f_type == 1 && recipe->fermentables.at(i).f_added < 4)		// Sugars
 	    psugar += recipe->fermentables.at(i).f_percentage;
@@ -230,7 +231,7 @@
 	    }
 	    d = ui->efficiencyEdit->value() / 100 * d;
 	    sugarsm += d;
-	    mashkg += recipe->fermentables.at(i).f_amount;
+	    recipe->mashs_kg += recipe->fermentables.at(i).f_amount;
 	}
 	if (recipe->fermentables.at(i).f_added == 0 || recipe->fermentables.at(i).f_added == 1)		// Mash or boil
 	    sugarsf += d;
@@ -291,11 +292,16 @@
      * We don't have a equipment profile in recipes,
      * so we assume a certain guessed mashtun size.
      */
-    ui->perc_mashShow->setValue(round(mashkg / (ui->boil_sizeEdit->value() / 3) * 100));
+    ui->perc_mashShow->setValue(round(recipe->mashs_kg / (ui->boil_sizeEdit->value() / 3) * 100));
     ui->perc_sugarsShow->setValue(round(psugar));
     ui->perc_caraShow->setValue(round(pcara));
-    qDebug() << "  lintner" << lintner << "  mashkg" << mashkg << "final" << round(lintner / mashkg);
-    ui->lintnerShow->setValue(round(lintner / mashkg));
+    if (recipe->mashs_kg > 0) {
+	qDebug() << "  lintner" << lintner << "  mashkg" << recipe->mashs_kg << "final" << round(lintner / recipe->mashs_kg);
+	ui->lintnerShow->setValue(round(lintner / recipe->mashs_kg));
+    } else {
+	qDebug() << "  lintner N/A";
+	ui->lintnerShow->setValue(0);
+    }
 
     /*
      * Calculate the apparant attenuation.
@@ -316,8 +322,8 @@
     ui->est_svgEdit->setValue(svg);
 
     double fg;
-    if (mashkg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
-	fg = Utils::estimate_fg(psugar, pcara, mashinfuse / mashkg, mashtime, mashtemp, svg, og);
+    if (recipe->mashs_kg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
+	fg = Utils::estimate_fg(psugar, pcara, mashinfuse / recipe->mashs_kg, mashtime, mashtemp, svg, og);
     else
 	fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og);
     qDebug() << "  FG" << ui->est_fgEdit->value() << fg;
--- a/src/EditRecipeTab6.cpp	Tue Apr 19 22:27:18 2022 +0200
+++ b/src/EditRecipeTab6.cpp	Wed Apr 20 12:19:36 2022 +0200
@@ -26,12 +26,11 @@
     QTableWidgetItem *item;
     QIcon down_icon, up_icon;
 
+    qDebug() << "refreshMashs" << recipe->mashs.size();
+
     down_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_down.png"), QSize(), QIcon::Normal, QIcon::Off);
     up_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_up.png"), QSize(), QIcon::Normal, QIcon::Off);
 
-    qDebug() << "refreshYeasts" << recipe->yeasts.size();
-    std::sort(recipe->yeasts.begin(), recipe->yeasts.end(), yeast_sort_test);
-
     /*
      * During filling the table turn off the cellChanged signal because every cell that is filled
      * triggers the cellChanged signal. The QTableWidget has no better signal to use.
@@ -159,6 +158,97 @@
 }
 
 
+double EditRecipe::infusionVol(double step_infused, double step_mashkg, double infuse_temp, double step_temp, double last_temp)
+{
+    double a = last_temp * (eq_tun_weight * eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
+    double b = step_temp * (eq_tun_weight * eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
+    double vol = round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)) * 100.0) / 100.0;
+
+    if (vol < 0)
+	vol = 0;
+    qDebug() << "  infusionVol(" << step_infused << "," << step_mashkg << "," << infuse_temp <<"," << step_temp << "," << last_temp << "):" << vol;
+    return vol;
+}
+
+
+double EditRecipe::decoctionVol(double step_volume, double step_temp, double prev_temp)
+{
+    double a = (eq_tun_weight * eq_tun_specific_heat + step_volume * SpecificHeatWater) * (step_temp - prev_temp);
+    double b = SpecificHeatWater * (99 - step_temp);
+    double vol = 0;
+
+    if (b > 0)
+	vol = round((a / b) * 1000000.0) / 1000000.0;
+    qDebug() << "  decoctionVol(" << step_volume << "," << step_temp << "," << prev_temp << "):" << vol;
+    return vol;
+}
+
+
+void EditRecipe::calcMash()
+{
+    double infused = 0, vol, a, b, temp;
+    int    i, j, n;
+    double lasttemp = 18.0;
+    double graintemp = 18.0;
+    double tuntemp = 18.0;
+
+    recipe->mashs_time = 0;
+
+    if (recipe->mashs.size() && recipe->mashs_kg > 0) {
+	qDebug() << "calcMash()";
+
+	for (i = 0; i < recipe->mashs.size(); i++) {
+	    if (recipe->mashs.at(i).step_type == 0) { // Infusion
+		if (i == 0) {
+		    // First mash step, temperature from the mashtun and malt.
+		    n = 20; // tun is preheated.
+		    tuntemp = recipe->mashs.at(i).step_temp;
+		    for (j = 0; j < n; j++) {
+			a = recipe->mashs_kg * graintemp * SpecificHeatMalt + eq_tun_weight * tuntemp * eq_tun_specific_heat;
+			b = recipe->mashs[i].step_temp *
+			      (eq_tun_weight * eq_tun_specific_heat + 
+			       recipe->mashs.at(i).step_infuse_amount * SpecificHeatWater + 
+			       recipe->mashs_kg * SpecificHeatMalt) -
+			      SlakingHeat * recipe->mashs_kg;
+			if (recipe->mashs.at(i).step_infuse_amount > 0) {
+			    temp = (b - a) / (recipe->mashs.at(i).step_infuse_amount * SpecificHeatWater);
+			} else {
+			    temp = 99;
+			}
+			tuntemp += (temp - tuntemp) / 2;
+			recipe->mashs[i].step_infuse_temp = round(temp * 1000000.0) / 1000000.0;
+		    }
+		    qDebug() << "  init infuse temp:" << recipe->mashs.at(i).step_infuse_temp;
+		} else {
+		    // Calculate amount of infusion water.
+		    recipe->mashs[i].step_infuse_amount =
+			    infusionVol(infused, recipe->mashs_kg, recipe->mashs.at(i).step_infuse_temp, recipe->mashs.at(i).step_temp, lasttemp);
+		    qDebug() << i << "  vol:" << recipe->mashs.at(i).step_infuse_amount << "temp:" << recipe->mashs.at(i).step_infuse_temp;
+		}
+		infused += recipe->mashs.at(i).step_infuse_amount;
+	    } else if (recipe->mashs.at(i).step_type == 1) { // Temperature
+		if (i > 0)
+		    recipe->mashs[i].step_infuse_amount = 0;
+		recipe->mashs[i].step_infuse_temp = 0;
+	    } else if (recipe->mashs.at(i).step_type == 2) { // Decoction
+		recipe->mashs[i].step_infuse_amount = decoctionVol(infused, recipe->mashs.at(i).step_temp, lasttemp);
+		recipe->mashs[i].step_infuse_temp = 99;
+	    }
+	    recipe->mashs[i].step_volume = infused;
+	    qDebug() << i << "  type:" << recipe->mashs.at(i).step_type << "volume:" << recipe->mashs.at(i).step_infuse_amount << "temp:" << recipe->mashs.at(i).step_infuse_temp;
+	    lasttemp = recipe->mashs.at(i).step_temp;
+	    recipe->mashs_time += recipe->mashs.at(i).step_time;
+	    if (i > 0)
+	    	recipe->mashs_time += recipe->mashs.at(i).ramp_time;
+	    recipe->mashs[i].step_wg_ratio = round((infused / recipe->mashs_kg) * 1000000.0) / 1000000.0;
+	}
+    }
+
+    /* Show the calculated total mash time. */
+    ui->mash_timeEdit->setText(QString("%1:%2").arg(recipe->mashs_time / 60).arg(recipe->mashs_time % 60, 2, 'f', 0, '0'));
+}
+
+
 void EditRecipe::addMashRow_clicked()
 {
     Mashs newm;
@@ -287,6 +377,7 @@
     ui->mashsTable->setItem(recipe->mashs_row, 2, item);
     this->ignoreChanges = false;
     is_changed();
+    emit refreshAll();
 }
 
 
@@ -300,6 +391,7 @@
     ui->mashsTable->setItem(recipe->mashs_row, 3, item);
     this->ignoreChanges = false;
     is_changed();
+    emit refreshAll();
 }
 
 
@@ -313,6 +405,7 @@
     ui->mashsTable->setItem(recipe->mashs_row, 4, item);
     this->ignoreChanges = false;
     is_changed();
+    emit refreshAll();
 }
 
 
@@ -326,6 +419,7 @@
     ui->mashsTable->setItem(recipe->mashs_row, 5, item);
     this->ignoreChanges = false;
     is_changed();
+    emit refreshAll();
 }
 
 
@@ -548,6 +642,7 @@
 	    /*
 	     * Got the json data in the steps array, replace the recipe steps.
 	     */
+	    double infuse = recipe->mashs.at(0).step_infuse_amount;
 	    recipe->mashs.clear();
 	    ui->mashsTable->clear();
 	    if (newsteps.isArray()) {
@@ -582,6 +677,8 @@
 		    recipe->mashs.append(m);
 		}
 	    }
+	    if (recipe->mashs.at(0).step_type == 0)
+		recipe->mashs[0].step_infuse_amount = infuse;	// Restore save initial infusion
 	}
     }
     is_changed();
--- a/ui/EditRecipe.ui	Tue Apr 19 22:27:18 2022 +0200
+++ b/ui/EditRecipe.ui	Wed Apr 20 12:19:36 2022 +0200
@@ -95,7 +95,7 @@
        <enum>QTabWidget::Rounded</enum>
       </property>
       <property name="currentIndex">
-       <number>5</number>
+       <number>0</number>
       </property>
       <property name="elideMode">
        <enum>Qt::ElideNone</enum>
@@ -2384,6 +2384,35 @@
          </rect>
         </property>
        </widget>
+       <widget class="QLabel" name="mash_timeLabel">
+        <property name="geometry">
+         <rect>
+          <x>570</x>
+          <y>40</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Mash time:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="mash_timeEdit">
+        <property name="geometry">
+         <rect>
+          <x>710</x>
+          <y>40</y>
+          <width>71</width>
+          <height>23</height>
+         </rect>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+       </widget>
       </widget>
       <widget class="QWidget" name="water">
        <attribute name="icon">

mercurial