Added SG/Plato formulas for PHP. Added OG calculation in the recipe print.

Sun, 11 Nov 2018 16:36:38 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 11 Nov 2018 16:36:38 +0100
changeset 85
ca7a37586551
parent 84
3e5e87f1818d
child 86
e977a505ea8c

Added SG/Plato formulas for PHP. Added OG calculation in the recipe print.

www/includes/formulas.php file | annotate | diff | comparison | revisions
www/includes/global.inc.php file | annotate | diff | comparison | revisions
www/js/rec_edit.js file | annotate | diff | comparison | revisions
www/rec_print.php file | annotate | diff | comparison | revisions
--- a/www/includes/formulas.php	Sat Nov 10 20:56:07 2018 +0100
+++ b/www/includes/formulas.php	Sun Nov 11 16:36:38 2018 +0100
@@ -109,4 +109,655 @@
 }
 
 
+
+function sg_to_plato($sg) {
+	if ($sg > 0.5)
+		return 259 - 259 / $sg;
+	return 0;
+}
+
+
+
+function plato_to_sg($plato) {
+	if ($plato < 259)
+		return 259 / (259 - $plato);
+	return 1.000;
+}
+
+
+
+/*
+
+Brouwhulp data.pas
+
+Function THop.FlavourContribution : double; //in % * concentration in g/l
+var bt, vol : double;
+begin
+  bt:= FTime.Value;
+  vol:= 0;
+  if FRecipe <> NIL then vol:= FRecipe.BatchSize.Value;
+  if FUse = huFirstWort then Result:= 0.15 * FAmount.Value * 1000 //assume 15% flavourcontribution for fwh
+  else if bt > 50 then Result:= 0.10 * FAmount.Value * 1000 //assume 10% flavourcontribution as a minimum
+  else
+  begin
+    Result:= 15.25 / (6 * sqrt(2 * PI)) * Exp(-0.5*Power((bt-21)/6, 2))
+             * FAmount.Value * 1000;
+    if result < 0.10 * FAmount.Value * 1000 then
+      Result:= 0.10 * FAmount.Value * 1000 //assume 10% flavourcontribution as a minimum
+  end;
+  if vol > 0 then Result:= Result / vol;
+end;
+
+Function THop.AromaContribution : double; //in % * concentration in g/l
+var bt, vol : double;
+begin
+  bt:= FTime.Value;
+  vol:= 0;
+  if FRecipe <> NIL then vol:= FRecipe.BatchSize.Value;
+  if bt > 20 then Result:= 0
+  else if bt > 7.5 then
+    Result:= 10.03 / (4 * sqrt(2 * PI)) * Exp(-0.5*Power((bt-7.5)/4, 2))
+             * FAmount.Value * 1000
+  else if FUse = huBoil then Result:= FAmount.Value * 1000
+  else if FUse = huAroma then Result:= 1.2 * FAmount.Value * 1000
+  else if FUse = huWhirlpool then Result:= 1.2 * FAmount.Value * 1000
+  else if FUse = huDryHop then Result:= 1.33 * FAmount.Value * 1000;
+    if vol > 0 then Result:= Result / vol;
+end;
+
+
+Procedure TFermentable.SetpHParameters(force : boolean);
+var x, ebc : double;
+begin
+  if Between(FDIpH.Value, -0.01, 0.01) or Between(FAcidTo57.Value, -0.1, 0.1) or force then
+  begin
+    ebc:= SRMtoEBC(FColor.Value);
+    case FGrainType of
+    gtBase, gtKilned:
+    begin
+      FDIpH.Value:= -0.0132 * ebc + 5.7605;
+      x:= 0.4278 * ebc - 1.8106;
+      FAcidTo57.Value:= x;
+    end;
+    gtRoast:
+    begin
+      FDIpH.Value:= 0.00018 * ebc + 4.558;
+      FAcidTo57.Value:= -0.0176 * ebc + 60.04;
+    end;
+    gtCrystal:
+    begin
+      FDIpH.Value:= -0.0019 * ebc + 5.2175;
+      FAcidTo57.Value:= 0.132 * ebc + 14.277;
+    end;
+    gtSour:
+    begin
+      FDIpH.Value:= 3.44;
+      FAcidTo57.Value:= 337;
+    end;
+    gtSpecial: //this could be anything. Assume for now it is a non-acidulated base or kilned malt
+    begin
+      FDIpH.Value:= -0.0132 * ebc + 5.7605;
+      FAcidTo57.Value:= 0.4278 * ebc - 1.8106;
+    end;
+    end;
+  end;
+  //known parameters should be filled in here
+  if FSupplier.Value = 'Weyermann' then
+  begin
+    if FName.Value = 'Vienna mout' then
+    begin
+      FDIpH.Value:= 5.65;
+      FAcidTo57.Value:= 1.6;
+    end;
+    if FName.Value = 'Münchner I' then
+    begin
+      FDIpH.Value:= 5.44;
+      FAcidTo57.Value:= 8.4;
+    end;
+    if FName.Value = 'Münchner II' then
+    begin
+      FDIpH.Value:= 5.54;
+      FAcidTo57.Value:= 5.6;
+    end;
+    if FName.Value = 'Caramunich I' then
+    begin
+      FDIpH.Value:= 5.1;
+      FAcidTo57.Value:= 22.4;
+    end;
+    if FName.Value = 'Caramunich II' then
+    begin
+      FDIpH.Value:= 4.71;
+      FAcidTo57.Value:= 49;
+    end;
+    if FName.Value = 'Caramunich III' then
+    begin
+      FDIpH.Value:= 4.92;
+      FAcidTo57.Value:= 31.2;
+    end;
+    if FName.Value = 'Cara-aroma' then
+    begin
+      FDIpH.Value:= 4.48;
+      FAcidTo57.Value:= 74.4;
+    end;
+    if FName.Value = 'Carafa I' then
+    begin
+      FDIpH.Value:= 4.71;
+      FAcidTo57.Value:= 42;
+    end;
+    if FName.Value = 'Carafa III' then
+    begin
+      FDIpH.Value:= 4.81;
+      FAcidTo57.Value:= 35.4;
+    end;
+    if FName.Value = 'Carafa II' then
+    begin
+      FDIpH.Value:= 4.76;
+      FAcidTo57.Value:= 38.7;
+    end;
+    if FName.Value = 'Carafa Special I' then
+    begin
+      FDIpH.Value:= 4.73;
+      FAcidTo57.Value:= 46.4;
+    end;
+    if FName.Value = 'Carafa Special II' then
+    begin
+      FDIpH.Value:= 4.78;
+      FAcidTo57.Value:= 42.9;
+    end;
+    if FName.Value = 'Carafa Special III' then
+    begin
+      FDIpH.Value:= 4.83;
+      FAcidTo57.Value:= 38.9;
+    end;
+    if IsInString(FName.Value, 'Zuurmout') then
+    begin
+      FDIpH.Value:= 3.44;
+      FAcidTo57.Value:= 358.2;
+    end;
+  end;
+end;
+
+
+function TFermentable.GetExtract: double;
+begin
+  Result := 0;
+  if FRecipe <> nil then
+  begin
+    Result := FAmount.Value * FYield.Value / 100 * (1 - FMoisture.Value / 100);
+    if FAdded = atMash then
+      Result := Result * FRecipe.Efficiency / 100;
+  end;
+end;
+
+
+function TFermentable.GetKolbachIndex: double;
+begin
+  if FProtein.Value > 0 then
+    Result := FDissolvedProtein.Value / FProtein.Value
+  else
+    Result := 0;
+end;
+
+
+Procedure TWater.AddMinerals(Ca, Mg, Na, HCO3, Cl, SO4 : double);
+begin
+  FCalcium.Add(Ca);
+  FMagnesium.Add(Mg);
+  FSodium.Add(Na);
+  FBicarbonate.Add(HCO3);
+  FChloride.Add(Cl);
+  FSulfate.Add(SO4);
+end;
+
+function TWater.GetResidualAlkalinity: double;
+begin
+  //Result in mg/l as CaCO3
+  Result:= FTotalAlkalinity.Value - (FCalcium.Value / 1.4 + FMagnesium.Value / 1.7);
+end;
+
+const
+  Ka1 = 0.0000004445;
+  Ka2 = 0.0000000000468;
+
+Function PartCO3(pH : double) : double;
+var H : double;
+begin
+  H:= Power(10, -pH);
+  Result:= 100 * Ka1 * Ka2 / (H*H + H * Ka1 + Ka1 * Ka2);
+end;
+
+Function PartHCO3(pH : double) : double;
+var H : double;
+begin
+  H:= Power(10, -pH);
+  Result:= 100 * Ka1 * H / (H*H + H * Ka1 + Ka1 * Ka2);
+end;
+
+Function PartH2CO3(pH : double) : double;
+var H : double;
+begin
+  H:= Power(10, -pH);
+  Result:= 100 * H * H / (H*H + H * Ka1 + Ka1 * Ka2);
+end;
+
+Function Charge(pH : double) : double;
+begin
+  Result:= (-2 * PartCO3(pH) - PartHCO3(pH));
+end;
+
+//Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH)
+Function TWater.ZAlkalinity(pHZ : double) : double;  //in mEq/l
+var CT, DeltaCNaught, DeltaCZ, C43, Cw, Cz : double;
+begin
+  C43:= Charge(4.3);
+  Cw:= Charge(FpH.Value);
+  Cz:= Charge(pHz);
+  DeltaCNaught:= -C43+Cw;
+  CT:= GetAlkalinity / 50 / DeltaCNaught;
+  DeltaCZ:= -Cz+Cw;
+  Result:= CT * DeltaCZ;
+end;
+
+//Z Residual alkalinity is the amount of acid (in mEq/l) needed to bring the water in the mash to the target pH (Z pH)
+Function TWater.ZRA(pHZ : double) : double; //in mEq/l
+var Calc, Magn, Z : double;
+begin
+  Calc:= FCalcium.Value / (MMCa / 2);
+  Magn:= FMagnesium.Value / (MMMg / 2);
+  Z:= ZAlkalinity(pHZ);
+  Result:= Z - (Calc / 3.5 + Magn / 7);
+end;
+
+Function TWater.ProtonDeficit(pHZ : double) : double;
+var i : integer;
+    F : TFermentable;
+    x : double;
+begin
+  Result:= ZRA(pHZ) * FAmount.Value;
+  //proton deficit for the added malts
+  for i:= 0 to FRecipe.NumFermentables - 1 do
+  begin
+    F:= FRecipe.Fermentable[i];
+    if (F.AddedType = atMash) and (F.GrainType <> gtNone) then
+    begin
+      x:= F.AcidRequired(pHZ) * F.Amount.Value;
+      Result:= Result + x;
+    end;
+  end;
+end;
+
+Function TWater.MashpH : double;
+var n : integer;
+    pd : double;
+    pH, deltapH, deltapd : double;
+begin
+  Result:= 0;
+  n:= 0;
+  pH:= 5.4;
+  deltapH:= 0.001;
+  deltapd:= 0.1;
+  pd:= ProtonDeficit(pH);
+  while ((pd < -deltapd) or (pd > deltapd)) and (n < 1000) do
+  begin
+    inc(n);
+    if pd < -deltapd then ph:= ph - deltapH
+    else if pd > deltapd then pH:= pH + deltapH;
+    pd:= ProtonDeficit(pH);
+  end;
+  Result:= pH;
+end;
+
+Function TWater.MashpH2(PrDef : double) : double;
+var n : integer;
+    pd : double;
+    pH, deltapH, deltapd : double;
+begin
+  Result:= 0;
+  n:= 0;
+  pH:= 5.4;
+  deltapH:= 0.001;
+  deltapd:= 0.1;
+  pd:= ProtonDeficit(pH);
+  while ((pd < PrDef-deltapd) or (pd > PrDef + deltapd)) and (n < 1000) do
+  begin
+    inc(n);
+    if pd < PrDef-deltapd then ph:= ph - deltapH
+    else if pd > PrDef+deltapd then pH:= pH + deltapH;
+    pd:= ProtonDeficit(pH);
+  end;
+  Result:= pH;
+end;
+
+function TWater.GetAlkalinity: double;
+begin
+  Result := FBicarbonate.Value / 1.22; //mEq/l
+end;
+
+function TWater.GetHardness: double;
+begin
+  Result := 0.14 * FCalcium.Value - 0.23 * FMagnesium.Value;
+end;
+
+function TWater.GetEstPhMash: double;
+{var
+  pHdemi, S: double;}
+begin
+  Result:= MashpH;
+{  Result := 0;
+  if FRecipe <> nil then
+  begin
+    pHDemi := FRecipe.pHdemi;
+    S := 0.013 * FRecipe.MashThickness + 0.013;
+    Result := pHDemi + ResidualAlkalinity / 50 * S;
+  end;}
+end;
+
+
+function TBeerStyle.GetBUGUMin: double;
+var
+  B, G: double;
+begin
+  Result:= 0;
+  if ((FOGMax.Value + FOGMin.Value) > 0) and ((FIBUMax.Value + FIBUMin.Value) > 0) then
+  begin
+    G := (FOGMax.Value - FOGMin.Value) / ((FOGMax.Value + FOGMin.Value) / 2);
+    B := (FIBUMax.Value - FIBUMin.Value) / ((FIBUMax.Value + FIBUMin.Value) / 2);
+    if G > B then
+      Result := ((FIBUMin.Value + FIBUMax.Value) / 2) / (1000 * (FOGMax.Value - 1))
+    else
+      Result := FIBUMin.Value / (1000 * (((FOGMax.Value + FOGMin.Value) / 2) - 1));
+  end;
+end;
+
+function TBeerStyle.GetBUGUMax: double;
+var
+  B, G: double;
+begin
+  Result:= 0;
+  if ((FOGMax.Value + FOGMin.Value) > 0) and ((FIBUMax.Value + FIBUMin.Value) > 0)
+  and (FOGMin.Value > 1) then
+  begin
+    G := (FOGMax.Value - FOGMin.Value) / ((FOGMax.Value + FOGMin.Value) / 2);
+    B := (FIBUMax.Value - FIBUMin.Value) / ((FIBUMax.Value + FIBUMin.Value) / 2);
+    if G > B then
+      Result := ((FIBUMin.Value + FIBUMax.Value) / 2) / (1000 * (FOGMin.Value - 1))
+    else
+      Result := FIBUMax.Value / (1000 * (((FOGMax.Value + FOGMin.Value) / 2) - 1));
+  end;
+end;
+
+
+  CalcOG;
+  CalcBitterness;
+
+        // Get concentration of ions in diluted brewwater (1) and target water (2) in mmol/l
+        Ca1 := W.Calcium.Value / MMCa;
+        Ca2 := W2.Calcium.Value / MMCa;
+        Mg1 := W.Magnesium.Value / MMMg;
+        Mg2 := W2.Magnesium.Value / MMMg;
+        Na1 := W.Sodium.Value / MMNa;
+        Na2 := W2.Sodium.Value / MMNa;
+
+        CO31 := W.Bicarbonate.Value / MMHCO3;
+        CO32 := W2.Bicarbonate.Value / MMHCO3;
+        SO41 := W.Sulfate.Value / MMSO4;
+        SO42 := W2.Sulfate.Value / MMSO4;
+        Cl1 := W.Sulfate.Value / MMSO4;
+        Cl2 := W2.Sulfate.Value / MMSO4;
+
+
+procedure MixWater(W1, W2, Wr: TWater);
+
+  function Mix(V1, V2, C1, C2: double): double;
+  begin
+    if (V1 + V2) > 0 then
+      Result := (V1 * C1 + V2 * C2) / (V1 + V2)
+    else
+      Result := 0;
+  end;
+
+var
+  vol1, vol2: double;
+  phnew: double;
+begin
+  vol1 := W1.Amount.Value;
+  vol2 := W2.Amount.Value;
+  if (vol1 + vol2) > 0 then
+  begin
+    Wr.Amount.Value := vol1 + vol2;
+    Wr.Calcium.Value := Mix(vol1, vol2, W1.Calcium.Value, W2.Calcium.Value);
+    Wr.Magnesium.Value := Mix(vol1, vol2, W1.Magnesium.Value, W2.Magnesium.Value);
+    Wr.Sodium.Value := Mix(vol1, vol2, W1.Sodium.Value, W2.Sodium.Value);
+    Wr.Bicarbonate.Value := Mix(vol1, vol2, W1.Bicarbonate.Value, W2.Bicarbonate.Value);
+    Wr.Sulfate.Value := Mix(vol1, vol2, W1.Sulfate.Value, W2.Sulfate.Value);
+    Wr.Chloride.Value := Mix(vol1, vol2, W1.Chloride.Value, W2.Chloride.Value);
+    pHnew := -log10((power(10, -W1.pHWater.Value) * vol1 +
+      power(10, -W2.pHWater.Value) * vol2) / (vol1 + vol2));
+    Wr.pHwater.Value := pHnew;
+  end;
+end;
+
+
+
+function TRecipe.CalcColorWort : double;
+var
+  i: integer;
+  F: TFermentable;
+  c, v: double;
+begin
+  c := 0;
+  v := FBatchSize.Value;
+  if (v > 0) and (High(FFermentables) >= 0) then
+  begin
+    for i := Low(FFermentables) to High(FFermentables) do
+    begin
+      F := TFermentable(FFermentables[i]);
+      c := c + F.Amount.Value * F.Color.Value / v;
+    end;
+    c := c * 8.34436;
+    case FColorMethod of
+      cmMorey: c := 1.49 * Power(c, 0.69);
+      cmMosher: c := 0.3 * c + 4.7;
+      cmDaniels: c := 0.2 * c + 8.4;
+    end;
+  end;
+  Result:= c;
+end;
+
+
+procedure TRecipe.CalcWaterBalance;
+var
+  i: integer;
+  F: TFermentable;
+begin
+  FAbsorbedByGrain := 0;
+  for i := Low(FFermentables) to High(FFermentables) do
+  begin
+    F := TFermentable(FFermentables[i]);
+    if (F.FermentableType = ftGrain) or (F.FermentableType = ftAdjunct) then
+      FAbsorbedByGrain := FAbsorbedByGrain + F.Amount.Value;
+  end;
+  FAbsorbedByGrain := FAbsorbedByGrain * Settings.GrainAbsorption.Value;
+
+  if FEquipment.CalcBoilVolume.Value then
+    {FBoilSize.Value := FBatchSize.Value / (1 - (FEquipment.EvapRate.Value / 100) *
+      (FBoilTime.Value / 60));}
+    FBoilSize.Value:= FBatchSize.Value + FEquipment.BoilSize.Value
+                              * FEquipment.EvapRate.Value / 100 *
+                              (FBoilTime.Value / 60);
+end;
+
+
+
+procedure TRecipe.CalcOG;
+var
+  i, j, k: integer;
+  v, v2, sg, d, tot, tot2, vol, vol1, vol2, sugF, sug, sug2, p, x: double;
+  mass1, mass2 : double;
+  F: TFermentable;
+begin
+  for j := 1 to 1 do
+  begin
+    sug:= 0;
+    sugf:= 0;
+    sug2:= 0;
+    tot := 0;
+    tot2:= 0;
+    vol:= 0;
+    FEfficiency.Value := GetEfficiency;
+    for i := 0 to NumFermentables - 1 do
+    begin
+      F := TFermentable(Fermentable[i]);
+      if (F.AddedType = atMash) or (F.AddedType = atBoil) then
+      begin
+        d := F.Amount.Value * (F.Yield.Value / 100) * (1 - F.Moisture.Value / 100);
+        if (F.AddedType = atMash) then
+          d := FEfficiency.Value / 100 * d;
+        sugf := sugf + d;
+        tot := tot + F.Amount.Value;
+      end
+      else
+      begin
+        x:= (F.Yield.Value / 100) * (1 - F.Moisture.Value / 100);
+        sug2:= sug2 + F.Amount.Value * x;
+        tot2:= tot2 + F.Amount.Value;
+        tot := tot + F.Amount.Value;
+        vol:= vol + F.Amount.Value / (x * SugarDensity + (1 - x) * 1);
+      end;
+    end;
+    if tot > 0 then
+      for i := 0 to NumFermentables - 1 do
+      begin
+        F := Fermentable[i];
+        F.Percentage.Value := 100 * F.Amount.Value / tot;
+      end;
+
+    if (FEquipment <> NIL) and (FBatchSize.Value > 0) then
+    begin
+      vol1:= FBatchSize.Value - FEquipment.TrubChillerLoss.Value;
+      vol2:= vol1 + FEquipment.TopUpWater.Value + vol;
+      sug:= sugf * vol1 / FBatchSize.Value;       //kg
+      sug:= sug + sug2;                     //kg
+      if vol2 > 0 then
+        sug:= sug / vol2; //kg/l
+      p:= 100 * sug;
+      sg:= PlatoToSG(p);
+      for k:= 1 to 30 do
+      begin
+        if sg > 0 then
+          p := 100 * sug / sg; //deg. Plato
+        sg := PlatoToSG(p);
+      end;
+      FEstOG.Value:= sg;
+    end
+    else if FBatchSize.Value <> 0 then
+    begin
+      p := 100 * sugf / FBatchSize.Value; //deg. Plato
+      sg := PlatoToSG(p);
+      for k:= 1 to 20 do
+      begin
+        if sg > 0 then
+          p := 100 * sugf / (FBatchSize.Value * sg); //deg. Plato
+        sg := PlatoToSG(p);
+      end;
+      FEstOG.Value := sg;
+    end
+    else
+      FEstOG.Value := 1.0;
+  end;
+
+  CalcWaterBalance;
+end;
+
+
+
+procedure TRecipe.EstimateFG;
+var
+  i: integer;
+  percS, percCara, BD, Att, AttBeer, sg: double;
+  Temp, TotTme: double;
+  Y: TYeast;
+//  Eq: TEquipment;
+begin
+  percS := GetPercSugar;
+  //if PercS > 40 then PercS:= 0;
+  percCara := GetPercCrystalMalt;
+  if percCara > 50 then PercCara:= 0;
+  if (Mash <> nil) and (Mash.MashStep[0] <> nil) then
+  begin
+    BD := Mash.MashStep[0].WaterToGrainRatio;
+    BD:= Max(2, Min(5.5, BD));
+    Temp := Mash.AverageTemperature;
+    Temp:= Max(60, Min(72, Temp));
+    TotTme := Mash.TotalMashTime;
+    TotTme:= Max(20, Min(90, TotTme));
+  end
+  else
+  begin
+    BD := 3.5;
+    Temp := 67;
+    TotTme := 75;
+  end;
+  Y := Yeast[0];
+  if Y <> nil then
+  begin
+    Att := Y.Attenuation.Value;
+    if Att < 30 then Att:= 77;
+  end
+  else
+    Att := 77;
+  AttBeer := 0.00825 * Att + 0.00817 * BD - 0.00684 * Temp + 0.00026 *
+             TotTme - 0.00356 * PercCara + 0.00553 * PercS + 0.547;
+
+{  Eq := nil;
+  if FEquipment <> nil then
+    Eq := TEquipment(Equipments.FindByName(FEquipment.Name.Value));
+  if Eq <> nil then
+    AttBeer2 := Eq.EstimateFG(Att, BD, Temp, TotTme, PercCara, PercS);}
+
+  FEstFG.Value := 1 + (1 - AttBeer) * (FEstOG.Value - 1);
+  CalcOGFermenter;
+  if FOGFermenter.Value > 1.001 then
+  begin
+    sg:= FOGFermenter.Value;
+    FEstFG2.Value := 1 + (1 - AttBeer) * (sg - 1);
+    FEstABV.Value := ABVol(FEstOG.Value, FEstFG.Value);
+  end
+  else if FOG.Value > 1.001 then
+  begin
+    sg:= FOG.Value;
+    FEstFG2.Value := 1 + (1 - AttBeer) * (sg - 1);
+    FEstABV.Value := ABVol(FEstOG.Value, FEstFG.Value);
+  end
+  else
+  begin
+    FEstFG2.Value := 1 + (1 - AttBeer) * (FEstOG.Value - 1);
+    FEstABV.Value := ABVol(FEstOG.Value, FEstFG.Value);
+  end;
+end;
+
+Procedure TRecipe.CalcCalories;
+var sug, alc, org, fig : double;
+begin
+  if FOGFermenter.Value > 1.001 then org:= FOGFermenter.Value
+  else if FOG.Value > 1.001 then org:= FOG.Value
+  else org:= 0;
+  if FFG.Value > 0.999 then fig:= FFG.Value
+  else if FEstFG.Value > 1.000 then fig:= FEstFG.Value
+  else if FEstFG2.Value > 1.000 then fig:= FEstFG2.Value
+  else fig:= 0;
+  if (org > 0) and (fig > 0) then
+  begin
+    alc:= 1881.22 * fig * (org - fig) / (1.775 - org);
+    sug:= 3550 * fig * (0.1808 * org + 0.8192 * fig - 1.0004);
+    FCalories.Value:= (alc + sug) / (12 * 0.0295735296);
+  end
+  else FCalories.Value:= 0;
+end;
+
+
+
+
+*/
+
 ?>
--- a/www/includes/global.inc.php	Sat Nov 10 20:56:07 2018 +0100
+++ b/www/includes/global.inc.php	Sun Nov 11 16:36:38 2018 +0100
@@ -212,7 +212,7 @@
 	if ($added == "Fermentation")
 		return 'Vergisten';
 	if ($added == "Lagering")
-		return 'Nagisten/lageren';
+		return 'Nagisten';
 	if ($added == "Bottle")
 		return 'Bottelen';
 	return $added;
--- a/www/js/rec_edit.js	Sat Nov 10 20:56:07 2018 +0100
+++ b/www/js/rec_edit.js	Sun Nov 11 16:36:38 2018 +0100
@@ -73,7 +73,7 @@
 
 	function calcInit () {
 		console.log("calc.init()");
-		$('#est_og').on('change', function (event) { calcFermentables(true); });
+//		$('#est_og').on('change', function (event) { calcFermentables(true); });
 		$('#efficiency').on('change', function (event) { calcFermentables(true); });
 		$('#batch_size').on('change', function (event) { calcFermentables(true); });
 		$('#boil_time').on('change', function (event) { calcFermentables(true); });
--- a/www/rec_print.php	Sat Nov 10 20:56:07 2018 +0100
+++ b/www/rec_print.php	Sun Nov 11 16:36:38 2018 +0100
@@ -141,9 +141,10 @@
 		$this->TableHeader();
 		$this->ProcessingTable=true;
 
+		$sugf = 0;
+		$tot = 0;
 		$this->SetFont('Helvetica','',9);
 		$this->SetFillColor(250, 195, 65);
-
 		$arr = json_decode($row['json_fermentables'], true);
 		foreach($arr as $item) { //foreach element in $arr
 			$name     = iconv('UTF-8','windows-1252',$item['f_name']);
@@ -152,11 +153,22 @@
 			$amount   = floatval($item['f_amount']);
 			$costkg   = floatval($item['f_cost']);
 			$yield    = floatval($item['f_yield']);
+			$moisture = floatval($item['f_moisture']);
 			$color    = floatval($item['f_color']);
 			$percent  = floatval($item['f_percentage']);
 			$cost = $amount * $costkg;
 			$cost_fermentables += $cost;
 			$total_fermentables += $amount;
+			/* Calculate the amount of sugars */
+			$d = $amount * ($yield / 100) * (1 - $moisture / 100);
+			if ($added == "Mash")
+				$d = floatval($row['efficiency']) / 100 * $d;
+			$sugf += $d;
+			$tot += $amount;
+			//$plato = 100 * $d / $amount;
+			//$this->Cell(0,5,$tot.' sugf: '.$sugf.' d: '.$d.' pt: '.$plato.' moisture: '.$moisture,0,0,'L',false);
+			//$this->Ln();
+
 			$this->Cell($vul,5,$name,0,0,'L',true);
 			$this->Cell(30,5,$supplier,0,0,'L',true);
 			$this->Cell(15,5,sprintf("%.0f",$color),0,0,'R',true);
@@ -168,8 +180,17 @@
 			$this->Ln();
 		}
 
+		$plato = 100 * $sugf / floatval($row['batch_size']);
+		$sg = plato_to_sg($plato);
+		/* Average loops, HansH 5x. Brouwhulp 20x, about 10x is enough so keep 20. */ 
+		for ($i = 0; $i < 20; $i++) {
+			if ($sg > 0)
+				$plato = 100 * $sugf / (floatval($row['batch_size']) * $sg);
+			$sg = plato_to_sg($plato);
+		}
 		$this->SetFillColor(210,245,255);
-		$this->Cell($vul+62,5,'',0,0,'L',false);
+		$this->Cell($vul,5,sprintf("%.1f",$plato).' Plato, OG: '.sprintf("%.3f",$sg),0,0,'L',true);
+		$this->Cell(62,5,'',0,0,'L',false);
 		$this->Cell(20,5,sprintf("%8.3f",$total_fermentables),0,0,'R',true);
 		$this->Cell(30,5,'',0,0,'L',false);
 		$this->Cell(20,5,sprintf("%8.3f",$cost_fermentables).EURO,0,0,'R',true);

mercurial