# HG changeset patch # User Michiel Broek # Date 1541950598 -3600 # Node ID ca7a375865514c31aa0725774d47afa1c11cc957 # Parent 3e5e87f1818dde9310a7b9075364729fc53041d2 Added SG/Plato formulas for PHP. Added OG calculation in the recipe print. diff -r 3e5e87f1818d -r ca7a37586551 www/includes/formulas.php --- 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; + + + + +*/ + ?> diff -r 3e5e87f1818d -r ca7a37586551 www/includes/global.inc.php --- 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; diff -r 3e5e87f1818d -r ca7a37586551 www/js/rec_edit.js --- 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); }); diff -r 3e5e87f1818d -r ca7a37586551 www/rec_print.php --- 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);