/****************************************** ** Name : beerlib.c ** Function : tools for beer utilities ** Author : Domenick Venezia **/ /* INCLUDE FILES **************************/ #include /* ENTRY *****************************/ /* Corrects the input specific gravity for the input temperature. */ float corrected_SG( float SG, float temp, char temptype ) { float tempF, correction; if ((temptype == 'C') || (temptype == 'c')) { tempF = C_to_F_degrees( temp ); } else { tempF = temp; } correction = A0 + A1*tempF + A2*tempF*tempF + A3*tempF*tempF*tempF; return( SG + (correction * 0.001) ); } /* ENTRY *****************************/ /* Corrects the input specific gravity at the input temperature ** to the reference temperature of 15 C (59 F). */ float corrected_SG_at_15C( float SG, float temp, char temptype ) { float C, correction; if ((temptype == 'F') || (temptype == 'f')) { C = F_to_C_degrees( temp ); } else { C = temp; } /* ** Use a linear interpolation for 0-3.98 C */ if (C < 3.98) { correction = -0.000032692*C - 0.000740644; } else if (C < 50) { correction = -0.0008031922 - 0.0000473773*C + 0.000007231263*C*C - 0.00000003078278*C*C*C; } else { correction = -0.005431719 + 0.0001963596*C + 0.000002661056*C*C; } return( SG + correction ); } /* ENTRY *****************************/ /* Reference: Manning, M.P., Understanding Specific Gravity and Extract, ** Brewing Techniques, 1,3:30-35 (1993) ** ** Equation is: P = -676.67 + 1286.4SG - 800.47SG**2 + 190.74SG**3 */ float SG_to_Plato( float SG ) { return( -676.67 + 1286.4*SG - 800.47*SG*SG + 190.74*SG*SG*SG ); } /* ENTRY *****************************/ /* Reference: Manning, M.P., Understanding Specific Gravity and Extract, ** Brewing Techniques, 1,3:30-35 (1993) ** ** Equation is: We = P*SG*R/100 ** where: P = degrees Plato @ 60F ** SG = Specific Gravity @ 60F ** R = density of water @ 60F = 8.338lb/USgal = 0.999kg/L */ float lbs_extract_per_gal_SG( float SG_raw, float temp, char temptype ) { float SG, Plato; SG = corrected_SG( SG_raw, temp, temptype ); Plato = SG_to_Plato( SG ); return( SG * Plato * 8.338 * 0.01 ); } /* ENTRY *****************************/ /* Reference: Manning, M.P., Understanding Specific Gravity and Extract, ** Brewing Techniques, 1,3:30-35 (1993) ** ** Equation is: SG2pts = SG1pts * (V1/V2) ** where: SGpts = SG - 1 */ float SG_change_with_vol( float SG0, float vol0, float vol1 ) { return( ((SG0 - 1.0) * (vol0 / vol1)) + 1.0 ); } /* ENTRY *****************************/ /* ** Equation is: V1 = V0 * (SG0pts/SG1pts) ** where: SGpts = SG - 1 */ float vol_change_with_SG( float vol0, float SG0, float SG1 ) { return( ((SG0 - 1.0)/(SG1 - 1.0)) * vol0 ); } /* ENTRY *****************************/ float temp_to_F_degrees( float temp, char temptype ) { if ((temptype == 'C') || (temptype == 'c')) { return( C_to_F_degrees( temp ) ); } else { return( temp ); } } /* ENTRY *****************************/ float temp_to_C_degrees( float temp, char temptype ) { if ((temptype == 'F') || (temptype == 'f')) { return( F_to_C_degrees( temp ) ); } else { return( temp ); } } /* ENTRY *****************************/ float C_to_F_degrees( float temp ) { return( (temp * 1.8) + 32.0 ); } /* ENTRY *****************************/ float F_to_C_degrees( float temp ) { return( (temp - 32.0) / 1.8 ); } /* ENTRY **********************************/ float lbs_to_kgs( float lbs ) { return( lbs * 0.453592 ); } /* ENTRY **********************************/ float kgs_to_lbs( float kgs ) { return( kgs * 2.204623 ); } /* ENTRY **********************************/ float gals_to_liters( float gals ) { return( gals * 3.785412 ); } /* ENTRY **********************************/ float liters_to_gals( float liters ) { return( liters * 0.264272 ); } /* ENTRY **********************************/ float qts_to_liters( float qts ) { return( qts * 0.946353 ); } /* ENTRY **********************************/ float liters_to_qts( float liters ) { return( liters * 1.056688 ); } /* ENTRY **********************************/ /* Calories due to alcohol per 12 ounces of beer */ float alc_calories_per_12_oz( float OSG, float FSG ) { return( (1881.22 * FSG) * ((OSG - FSG)/(1.775 - OSG)) ); } /* ENTRY **********************************/ /* Calories due to extract per 12 ounces of beer */ float ext_calories_per_12_oz( float OSG, float FSG ) { return( 3550.0 * FSG * ((0.1808 * OSG) + (0.8192 * FSG) - 1.0004) ); } /* ENTRY ***********************************/ /* Note temp MUST be fahrenheit */ float CO2_pressure( float vol, float tempF ) { float KP; /* keg pressure */ KP = -16.6999 - (0.0101059 * tempF) + (0.00116512 * tempF * tempF) + (0.173354 * tempF * vol) + (4.24267 * vol) - (0.0684226 * vol * vol); return( KP ); } /* ENTRY ***********************************/ /* Find volume of decoction to raise mash temp from initial temp to target temp */ float decoct( float vt, float tt, float t0, float td ) /* vt, Total Volume */ /* tt, Target temp */ /* t0, Initial temp */ /* td; decoction temp */ { return( (vt * (tt - t0)) / (td - t0) ); } /* ENTRY ***********************************/ /* Find volume addition at given temp to raise mash temp to target. */ float beer_step_add_vol( float vm, float tm, float ta, float tf ) /* vm, Volume mash */ /* tm, Temperature mash */ /* ta, temperature addition */ /* tf; Temperature final */ { float va; /* Volume addition - what we are calculating */ va = (vm * (tm - tf)) / (tf - ta); return( va ); } /* ENTRY *****************************/ /* Returns the Relative density difference between water at the reference ** temp, t0, and the target temp, t1. */ float beer_relative_density_of_water( float t0, float t1 ) { int tlow, thigh; float d0, d1, dlow, dhigh; extern float density_water[BEER_NUM_DENS]; /* ** First find the density of water at the reference temperature by interpolation */ if (t0 == 0.0) { d0 = density_water[0]; } else { tlow = floor( (double) t0 ); thigh = ceil( (double) t0 ); dlow = density_water[tlow]; dhigh = density_water[thigh]; d0 = dlow; d0 += (t0 - tlow) * (dhigh - dlow); } /* ** Next find the density of water at the target temperature by interpolation */ if (t1 == 0.0) { d1 = density_water[0]; } else { tlow = floor( (double) t1 ); thigh = ceil( (double) t1 ); dlow = density_water[tlow]; dhigh = density_water[thigh]; d1 = dlow; d1 += (t1 - tlow) * (dhigh - dlow); } return( d1 / d0 ); } /* ENTRY *****************************/ /* Returns the Relative density difference between water at the reference ** temp, t0, and the target temp, t1. */ float beer_relative_volume_of_water( float t0, float v0, float t1 ) { float rd1; /* relative density of target volume at target temp */ rd1 = beer_relative_density_of_water( t0, t1 ); return( v0 / rd1 ); } /*************************************************************** ** The following are Glenn Tinseth's hopping functions */ /* ENTRY **********************************/ float tinseth_hop_gravity_adjustment( float SGB ) /* SGB Specific Gravity of the boil */ { return( 1.65 * pow( 0.000125, (double) SGB - 1.0 ) ); } /* ENTRY **********************************/ float tinseth_hop_boil_time_factor( float boil_time ) { return( (1.0 - exp( -0.04 * (double)boil_time )) / 4.15 ); } /* ENTRY **********************************/ float tinseth_hop_utilization( float SGB, float boil_time ) { float time_factor, SG_factor; time_factor = tinseth_hop_boil_time_factor( boil_time ); SG_factor = tinseth_hop_gravity_adjustment( SGB ); return( SG_factor * time_factor ); } /* ENTRY **********************************/ float tinseth_hop_ibu_from_weight( float Util, float Acid, float Wt, float Vol, float GA, float WtFactor ) /* Util, Percent Hop utilization as fraction ( Util < 1.0) Acid, Percent Alpha acid content of hops as fraction Wt, Weight of Hops in grams Vol, Volume of batch size in Liters or Gallons GA, Gravity adjustment WtFactor; Weight conversion value for metric or english measures */ { return( (Util*Acid*Wt*WtFactor) / Vol ); } /* ENTRY **********************************/ float tinseth_hop_weight_from_ibu( float Util, float Acid, float IBU, float Vol, float GA, float WtFactor ) /* Util, Percent Hop utilization as fraction ( Util < 1.0) */ /* Acid, Percent Alpha acid content of hops as fraction */ /* IBU, Desired IBU */ /* Vol, Volume of batch size in Liters */ /* GA, Gravity adjustment */ /* WtFactor; Weight conversion value for metric or english measures */ { return( (Vol*IBU) / (Util*Acid*WtFactor) ); } /*************************************************************** ** The following are Jackie Rager's hopping functions */ /* ENTRY **********************************/ float rager_hop_gravity_adjustment( float SGB ) /* SGB; Specific Gravity of the boil */ { if (SGB <= RAGER_HIGH_SPECIFIC_GAVITY) return( 0.0 ); return( (SGB - RAGER_HIGH_SPECIFIC_GAVITY) / (0.2) ); } /* ENTRY **********************************/ float rager_hop_util( int boil_minutes ) { if (boil_minutes == 0) { return( 0.0 ); } else if (boil_minutes <= 5) { return( 0.05 ); } else if (boil_minutes <= 10) { return( 0.06 ); } else if (boil_minutes <= 15) { return( 0.08 ); } else if (boil_minutes <= 20) { return( 0.101 ); } else if (boil_minutes <= 25) { return( 0.121 ); } else if (boil_minutes <= 30) { return( 0.153 ); } else if (boil_minutes <= 35) { return( 0.188 ); } else if (boil_minutes <= 40) { return( 0.228 ); } else if (boil_minutes <= 45) { return( 0.269 ); } else if (boil_minutes <= 50) { return( 0.281 ); } else if (boil_minutes <= 55) { return( 0.291 ); } else { return( 0.30 ); } } /* ENTRY **********************************/ float rager_hop_ibu_from_weight( float Util, float Acid, float Wt, float Vol, float GA, float WtFactor ) /* Util, Percent Hop utilization as fraction ( Util < 1.0) */ /* Acid, Percent Alpha acid content of hops as fraction */ /* Wt, Weight of Hops in grams */ /* Vol, Volume of batch size in Liters or Gallons*/ /* GA, Gravity adjustment */ /* WtFactor; Weight conversion value for metric or english measures */ { return( (Util*Acid*Wt*WtFactor) / (Vol*(1.0 + GA)) ); } /* ENTRY **********************************/ float rager_hop_weight_from_ibu( float Util, float Acid, float IBU, float Vol, float GA, float WtFactor ) /* Util, Percent Hop utilization as fraction ( Util < 1.0) */ /* Acid, Percent Alpha acid content of hops as fraction */ /* IBU, Desired IBU */ /* Vol, Volume of batch size in Liters */ /* GA, Gravity adjustment */ /* WtFactor; Weight conversion value for metric or english measures */ { return( (Vol*IBU*(1.0 + GA)) / (Util*Acid*WtFactor) ); } /* ENTRY **********************************/ /* .39661 + 0.0017091*Po + (1.0788E-5)*Po^2 AJ DeLange */ float ballings_factor( float Plato ) { return( 0.39661 + (0.0017091*Plato) + (1.0788e-5*Plato*Plato) ); } /* ENTRY **********************************/ float alcohol_by_weight_fix( float OSG, float FSG ) { float Alc, OE, AE, RE; OE = SG_to_Plato( OSG ); AE = SG_to_Plato( FSG ); RE = (0.1808 * OE) + (0.8192 * AE); Alc = (OE - RE) / (2.0665 - (0.010665 * OE)); return( Alc ); } /* ENTRY **********************************/ float alcohol_by_weight_AJ( float OSG, float FSG ) { float PO, /* Plato original (corrected) */ PF, /* Plato finished (corrected) */ BF, /* Balling factor */ ABW; PO = SG_to_Plato( OSG ); PF = SG_to_Plato( FSG ); BF = ballings_factor( PO ); ABW = (PO - PF) * BF; return( ABW ); } /* ENTRY **********************************/ /* Alcohol by weight to alcohol by volume */ float abw_to_abv( float abw ) { return( abw/0.793573 ); } /* ENTRY **********************************/ /* ** RI = 1.33302 + 0.001427193(B) + 0.000005791157(B^2) ** ** where: ** ** B = measured refractivity in Brix ** RI = calculated Refractive Index */ float brix_to_ri( float brix ) { double B, ri; B = brix; return( (float) 1.33302 + 0.001427193*B + 0.000005791*B*B ); } /* ENTRY **********************************/ /* ** SG = 1.001843 - 0.002318474(OG) - 0.000007775(OG^2) - 0.000000034(OG^3) + ** 0.00574(AG) + 0.00003344(AG^2) + 0.000000086(AG^3) ** ** where: ** ** SG = estimated specific gravity of the sample ** OG = Original Gravity of the batch (in Brix) ** AG = Apparent Gravity of the sample (in Brix) */ float brix_to_fg( float ob, float ab ) { double OB, AB, SG; OB = (double) ob; AB = (double) ab; SG = 1.001843 - (0.002318474*OB) - (0.000007775*OB*OB) - (0.000000034*OB*OB*OB) + (0.00574*AB) + (0.00003344*AB*AB) + (0.000000086*AB*AB*AB); return( (float) SG ); } /* ENTRY **********************************/ /* Alcohol by Weight from Specific Gravity and Degrees Brix of finished beer */ float brix_to_abw( float sg, float ab ) { double SG, RI; SG = sg; RI = brix_to_ri( ab ); return( (float)(1017.5596 - (277.4*SG) + RI*((937.8135*RI) - 1805.1228))); } /* ENTRY **********************************/ /* Alcohol by Weight from Specific Gravity and Degrees Brix of finished beer */ float brix_sg_to_abw( float ab, float sg ) { double SG, AB, RI; SG = sg; AB = ab; RI = 1.33302 + 0.001427193*AB + 0.000005791*AB*AB; return( (float)(1017.5596 - (277.4*SG) + RI*((937.8135*RI) - 1805.1228))); } /* ENTRY **********************************/ /* Specific Gravity @ 15C from Degrees Brix @ 20C */ float brix_to_sg( float brix ) { double B, SG; B = brix; SG = 1.000898 + 0.003859118*B + 0.00001370735*B*B + 0.00000003742517*B*B*B; return( (float) SG ); }