Implemented yeast viability calculation from the yeast production date. Changed the pitchrate logic and added Ale above 1.076 rate of 1.25.

Mon, 02 Mar 2020 17:15:38 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 02 Mar 2020 17:15:38 +0100
changeset 617
f962f1e5936e
parent 616
2cbf21bb9bdc
child 618
2712082437b0

Implemented yeast viability calculation from the yeast production date. Changed the pitchrate logic and added Ale above 1.076 rate of 1.25.

README.design file | annotate | diff | comparison | revisions
www/includes/db_product.php file | annotate | diff | comparison | revisions
www/js/prod_edit.js file | annotate | diff | comparison | revisions
www/prod_edit.php file | annotate | diff | comparison | revisions
--- a/README.design	Sun Mar 01 12:48:09 2020 +0100
+++ b/README.design	Mon Mar 02 17:15:38 2020 +0100
@@ -13,7 +13,6 @@
   NOOT: experimentele wijziging is toegevoegd op 5-dec-2019. New Cubic van seanterrill.
 
 Wish:
-  Giststarter bereken gist viability. Viability zelf wordt al gebruikt.
   Giststarter automatisch aantal regels aanpassen bij wijzigen van de hoeveelheden.
 
 Extra:
--- a/www/includes/db_product.php	Sun Mar 01 12:48:09 2020 +0100
+++ b/www/includes/db_product.php	Mon Mar 02 17:15:38 2020 +0100
@@ -265,7 +265,11 @@
 		$sql .= "', prop4_type='" . $_POST['prop4_type'];
 		$sql .= "', prop4_volume='" . $_POST['prop4_volume'];
 	}
-	$sql .= "', divide_type='" . $_POST['divide_type'];
+	if ($_POST['yeast_prod_date'] == '')
+                $sql .= "', yeast_prod_date=NULL";
+        else
+                $sql .= "', yeast_prod_date='" . $_POST['yeast_prod_date'] . "'";
+	$sql .=  ", divide_type='" . $_POST['divide_type'];
 	$sql .= "', divide_size='" . floatval($_POST['divide_size']);
 	$sql .= "', divide_factor='" . floatval($_POST['divide_factor']);
 	$sql .= "', divide_parts='" . $_POST['divide_parts'];
@@ -869,7 +873,8 @@
 		$brew .= ',"starter_type":' . $row['starter_type'];
 		$brew .= ',"starter_sg":' . $row['starter_sg'];
 		$brew .= ',"starter_viability":' . $row['starter_viability'];
-		$brew .= ',"prop1_type":' . $row['prop1_type'];
+		$brew .= ',"yeast_prod_date":"' . $row['yeast_prod_date'];
+		$brew .= '","prop1_type":' . $row['prop1_type'];
 		$brew .= ',"prop1_volume":' . $row['prop1_volume'];
 		$brew .= ',"prop2_type":' . $row['prop2_type'];
 		$brew .= ',"prop2_volume":' . $row['prop2_volume'];
--- a/www/js/prod_edit.js	Sun Mar 01 12:48:09 2020 +0100
+++ b/www/js/prod_edit.js	Mon Mar 02 17:15:38 2020 +0100
@@ -300,6 +300,7 @@
    { name: 'starter_type', type: 'int' },
    { name: 'starter_sg', type: 'float' },
    { name: 'starter_viability', type: 'int' },
+   { name: 'yeast_prod_date', type: 'string' },
    { name: 'prop1_type', type: 'int' },
    { name: 'prop1_volume', type: 'float' },
    { name: 'prop2_type', type: 'int' },
@@ -530,6 +531,7 @@
    $('#starter_type').val(dataRecord.starter_type);
    $('#starter_sg').val(dataRecord.starter_sg);
    $('#starter_viability').val(dataRecord.starter_viability);
+   $('#yeast_prod_date').val(dataRecord.yeast_prod_date);
    $('#prop1_type').val(dataRecord.prop1_type);
    $('#prop1_volume').val(dataRecord.prop1_volume);
    $('#prop2_type').val(dataRecord.prop2_type);
@@ -1310,7 +1312,7 @@
 
   $('#yeastGrid').jqxGrid({
    width: 1240,
-   height: 350,
+   height: 325,
    source: yeastAdapter,
    theme: theme,
    selectionmode: 'singlerow',
@@ -1367,6 +1369,7 @@
       row['y_zymocide'] = datarecord.zymocide;
       $('#yeastGrid').jqxGrid('addrow', null, row);
      }
+     calcViability();
      calcYeast();
      $('#yaddrowbutton').jqxDropDownList('clearSelection');
     });
@@ -1383,6 +1386,7 @@
      if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
       id = $('#yeastGrid').jqxGrid('getrowid', selectedrowindex);
       $('#yeastGrid').jqxGrid('deleterow', id);
+      calcViability();
       calcYeast();
      }
     });
@@ -1627,6 +1631,63 @@
  /*
   * Generic functions
   */
+ function calcViability() {
+  var vpm = 1.00;
+  var max = 100;
+  var rowscount = $('#yeastGrid').jqxGrid('getdatainformation').rowscount;
+  if (rowscount) {
+   for (i = 0; i < rowscount; i++) {
+    row = $('#yeastGrid').jqxGrid('getrowdata', i);
+    if (row.y_use == 0) {
+     if (row.y_form == 0) { // Liquid
+      vpm = 0.80;
+      max = 97;
+      if (row.y_laboratory == 'White Labs')
+       vpm = 0.95;
+     } else if (row.y_form == 1) { // dry yeast
+      vpm = 0.98;
+      max = 100;
+     } else if (row.y_form == 6) { // Dried kveik
+      vpm = 1.00;
+      max = 100;
+     } else { // Slant, Culture, Frozen, Bottle
+      vpm = 0.99;
+      max = 97;
+     }
+    }
+   }
+  }
+  var base = max;
+  var days = 0;
+
+  if (parseFloat($('#yeast_prod_date').val()) > 2000) {
+   console.log('calculate viability');
+   var d = new Date();
+   var date2 = $('#yeast_prod_date').val();
+   date2 = date2.split('-');
+   // Now we convert the array to a Date object
+   var date1 = new Date(d.getFullYear(), d.getMonth(), d.getDate());
+   date2 = new Date(date2[0], date2[1] - 1, date2[2]);
+   var diff = parseInt(date1.getTime()) - parseInt(date2.getTime());
+   days = Math.floor(diff/1000/60/60/24);
+
+   var degrade = 1 - ((1 - vpm) / 30.41);  // viability degradation per day.
+   for (i = 0; i < days; i++) {
+    base = base * degrade;
+   }
+   if (base > max) {
+    base = max;
+   }
+   base = Math.round(base);
+  }
+  console.log('age:' + days + ' max:' + max + ' vpm:' + vpm + ' base:' + base);
+
+  if (dataRecord.starter_viability != base) {
+   dataRecord.starter_viability = base;
+   $('#starter_viability').val(dataRecord.starter_viability);
+  }
+ }
+
  function calcSupplies() {
   if (dataRecord.inventory_reduced > 6) {
    $('#ok_pmpt').hide();
@@ -1884,7 +1945,7 @@
     if (row.y_form == 0)
      initcells += (parseFloat(row.y_cells) / 1000000000) * parseFloat(row.y_amount) * (dataRecord.starter_viability / 100);
     else
-     initcells += (parseFloat(row.y_cells) / 1000000) * parseFloat(row.y_amount);
+     initcells += (parseFloat(row.y_cells) / 1000000) * parseFloat(row.y_amount) * (dataRecord.starter_viability / 100);
    }
    // TODO: brett in secondary ??
    if ((((dataRecord.inventory_reduced <= 3) && (row.y_use == 0)) ||  // Primary
@@ -2368,17 +2429,19 @@
    if (row.y_use == 0) { // primary
     // pitchrate see https://www.brewersfriend.com/yeast-pitch-rate-and-starter-calculator/
     // and http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/
-    pitchrate = 0.75;
-    if (dataRecord.est_og > 1.060)
-     pitchrate = 1.0;
-//  if (dataRecord.est_og > 1.076)
-//   pitchrate = 1.25;       // Wyeast labs. http://www.wyeastlab.com/hb_pitchrates.cfm
-    if (row.y_type == 0) // lager yeast
-     pitchrate *= 2;
-    if (row.y_type == 6) // Kveik
-     pitchrate /= 10; // http://suigenerisbrewing.com/index.php/2019/08/28/drying-kveik-the-grand-finale/
-//   pitchrate = 0.25; // Who knows.
-
+    if (row.y_type == 0) { // lager yeast
+     pitchrate = 1.5;
+     if (dataRecord.est_og > 1.060)
+      pitchrate = 2.0;
+    } else if (row.y_type == 6) { // Kveik
+     pitchrate = 0.075;
+    } else {
+     pitchrate = 0.75;
+     if (dataRecord.est_og > 1.060)
+      pitchrate = 1.0;
+     if (dataRecord.est_og > 1.076)
+      pitchrate = 1.25; // Wyeast labs. http://www.wyeastlab.com/hb_pitchrates.cfm
+    }
 //    if (row.y_form == 1) { // dry yeast
 //    } else { // possible starter needed
 //    }
@@ -3558,7 +3621,7 @@
    $('#starter_type').jqxDropDownList({ disabled: true });
    $('#starter_try').jqxButton({ disabled: true });
    $('#starter_sg').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
-   $('#starter_viability').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
+   $('#yeast_prod_date').jqxDateTimeInput({ disabled: true });
   }
   if (dataRecord.stage > 3) { // Primary fermentation done
    $('#brew_date_start').jqxDateTimeInput({ disabled: true });
@@ -3652,13 +3715,11 @@
    $('#starter_type').jqxDropDownList({ disabled: false });
    $('#starter_try').jqxButton({ disabled: false });
    $('#starter_sg').jqxNumberInput({ spinButtons: true, readOnly: false, width: 110 });
-   $('#starter_viability').jqxNumberInput({ spinButtons: true, readOnly: false, width: 110 });
   } else {
    $('#propagator').hide();
    $('#starter_type').jqxDropDownList({ disabled: true });
    $('#starter_try').jqxButton({ disabled: true });
    $('#starter_sg').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
-   $('#starter_viability').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
   }
  }
 
@@ -3698,13 +3759,6 @@
     calcYeast();
    }
   });
-  $('#starter_viability').on('change', function(event) {
-   if (event.args) {
-    dataRecord.starter_viability = event.args.value;
-    calcFermentables();
-    calcYeast();
-   }
-  });
   $('#prop1_type').on('change', function(event) {
    if (event.args) {
     dataRecord.prop1_type = event.args.index;
@@ -4436,6 +4490,7 @@
    starter_type: $('#starter_type').val(),
    starter_sg: parseFloat($('#starter_sg').jqxNumberInput('decimal')),
    starter_viability: parseFloat($('#starter_viability').jqxNumberInput('decimal')),
+   yeast_prod_date: $('#yeast_prod_date').val(),
    prop1_type: $('#prop1_type').val(),
    prop1_volume: parseFloat($('#prop1_volume').jqxNumberInput('decimal')),
    prop2_type: $('#prop2_type').val(),
@@ -5224,6 +5279,13 @@
  $('#need_cells').jqxNumberInput(Show1dec);
  $('#plato_cells').jqxTooltip({ content: 'De berekende hoeveelheid gistcellen in miljard per ml per graad Plato. Dit is de zogenaamde pitchrate.' });
  $('#plato_cells').jqxNumberInput(Show2dec);
+ $('#yeast_prod_date').jqxTooltip({ content: 'Bij korrelgisten is meestal "best voor" datum op het zakje gedrukt.<br>Gebruik die datum maar dan twee jaar eerder als productie datum.<br>Bij White Labs is de productie datum vier maanden voor de "Best by" datum die geprint op het buisje.<br>Bij Wyeast is dit de "manufacture date" die op het pak geprint is.<br>Voor schuine buis, slurry, opkweek en gedroogd is dit de datum dat je de gist geoogst hebt.' });
+ $('#yeast_prod_date').jqxDateTimeInput(Dateopts);
+ $('#yeast_prod_date').on('close', function(event) {
+  calcViability();
+  calcFermentables();
+  calcYeast();
+ });
  $('#popupYeast').jqxWindow({
   width: 800,
   height: 300,
@@ -5265,6 +5327,7 @@
    y_avail: yeastData.y_avail
   };
   $('#yeastGrid').jqxGrid('updaterow', rowID, row);
+  calcViability();
   calcFermentables();
   calcYeast();
  });
@@ -5321,6 +5384,7 @@
    } else {
     $('#wy_pmpt_amount').html('Volume ml:');
    }
+   calcViability();
    calcFermentables();
    calcYeast();
   }
@@ -5348,6 +5412,7 @@
   if (event.args) {
    var index = event.args.index;
    yeastData.y_use = index;
+   calcViability();
    calcFermentables();
    calcYeast();
   }
@@ -5389,12 +5454,11 @@
  });
  $('#starter_sg').jqxTooltip({ content: 'Het ideale starter SG moet tussen de 1.030 en 1.040 zijn. Optimaal is 1.037.' });
  $('#starter_sg').jqxNumberInput(SGopts);
- $('#starter_viability').jqxTooltip({
-  content: 'De gist conditie te berekenen vanaf de productie datum. Vloeibare gist verlist iedere maand ongeveer 20% active cellen.'
- });
- $('#starter_viability').jqxNumberInput(Perc0);
+ $('#starter_viability').jqxTooltip({ content: 'De gist conditie.' });
+ $('#starter_viability').jqxNumberInput(Smal0dec);
  $('#starter_try').jqxButton({ template: 'primary', width: '100px', height: 23, theme: theme });
 
+
  // Tab 7, Mashing
  $('#mash_name').jqxTooltip({ content: 'De omschrijving van dit maisch profiel.' });
  $('#mash_name').jqxInput({ theme: theme, width: 320, height: 23 });
--- a/www/prod_edit.php	Sun Mar 01 12:48:09 2020 +0100
+++ b/www/prod_edit.php	Mon Mar 02 17:15:38 2020 +0100
@@ -325,6 +325,10 @@
         <td style="padding: 3px;"><div id="yeast_cells"></div></td>
        </tr>
        <tr>
+        <td align="right" style="vertical-align: top;">Gist productie datum:</td>
+        <td style="padding: 3px;"><input readonly="1" id="yeast_prod_date" /></div></td>
+       </tr>
+       <tr>
         <td style="vertical-align: top; float: right; padding: 3px;">Cellen nodig miljard:</td>
         <td style="padding: 3px;"><div id="need_cells"></div></td>
        </tr>

mercurial