BrewDay - Tools for Homebrewers

BrewDay is a set of tools for homebrewers written in python.

Tutorial

Features

  • Build a Recipe to know what to expect from your brew
  • Develop Recipes to help you pick ingredients to match a style
  • Explore concepts like Bitterness, Color, and Alcohol Content

Back to Index

Building a Recipe

This tutorial is going to show you how to build a recipe using the objects provided by the BrewDay library. This will walk you through putting together each object, describe them and some methods, and then finally build a complete recipe.

The Grain Bill

To build a beer recipe you will want to first describe the grains to be used. This is done by describing each grain with a brew.grains.Grain object.

from brew.grains import Grain
pale = Grain('Pale Malt (2 Row) US',
             color=1.8,
             ppg=37)

This object is really only descriptive. It tells you the characteristics of the Grain that you will later add to your beer. What you want to construct is a brew.grains.GrainAddition object, which describes how much of the grain is added to the recipe:

from brew.grains import GrainAddition
pale_add = GrainAddition(pale, weight=13.96)

This object now decribes what will be added to the beer. You can further customize the grain by describing the type of grain being added:

from brew.constants import GRAIN_TYPE_LME
from brew.grains import GrainAddition
pale_add = GrainAddition(pale, weight=13.96, grain_type=GRAIN_TYPE_LME)

By describing the type of grain you change how it is utilized in the recipe equations.

The Hops Bill

The next piece to describe is the brew.hops.Hop object.

from brew.hops import Hop
centennial = Hop('Centennial',
                 percent_alpha_acids=0.14)

Again, this object is really only descriptive. It tells you the characteristics of the Hop that you will later add to your beer. What you want to construct is a brew.hops.HopAddition object, which describes how many hops are added to the recipe and how long they are boiled for.

from brew.hops import HopAddition
centennial_add = HopAddition(centennial,
                             weight=0.57,
                             boil_time=60.0)

This object now decribes what will be added to the beer. You can further customize the hop by describing the type of hop being added:

from brew.constants import HOP_TYPE_WHOLE
from brew.hops import HopAddition
centennial_add = HopAddition(centennial,
                             weight=0.57,
                             boil_time=60.0,
                             hop_type=HOP_TYPE_WHOLE)

By describing the type of hop you change how it is utilized in the recipe equations.

Yeast

Yeast describes how much alcohol is expected to be produced by the recipe. This is done with a brew.yeasts.Yeast object.

from brew.yeasts import Yeast
yeast = Yeast('Wyeast 1056',
              percent_attenuation=0.70)

By changing the percentage of attenuation you can estimate different final ABV amounts for the recipe. By default the yeast expects 75% attenuation.

Building the Recipe

Let’s take what we’ve learned so far and prepare a Pale Ale recipe:

from brew.grains import Grain
from brew.grains import GrainAddition
from brew.hops import Hop
from brew.hops import HopAddition
from brew.yeasts import Yeast

# Define Grains
pale = Grain('Pale Malt (2 Row) US',
             color=1.8,
             ppg=37)
pale_add = GrainAddition(pale,
                         weight=13.96)

crystal = Grain('Caramel/Crystal Malt - 20L',
                color=20.0,
                ppg=35)
crystal_add = GrainAddition(crystal,
                            weight=0.78)
grain_additions = [pale_add, crystal_add]

# Define Hops
centennial = Hop('Centennial',
                 percent_alpha_acids=0.14)
centennial_add = HopAddition(centennial,
                             weight=0.57,
                             boil_time=60.0)

cascade = Hop('Cascade (US)',
              percent_alpha_acids=0.07)
cascade_add = HopAddition(cascade,
                          weight=0.76,
                          boil_time=5.0)
hop_additions = [centennial_add, cascade_add]

# Define Yeast
yeast = Yeast('Wyeast 1056')

Now we want to put them together into a brew.recipes.Recipe.

from brew.recipes import Recipe
beer = Recipe('Pale Ale',
              grain_additions=grain_additions,
              hop_additions=hop_additions,
              yeast=yeast,
              percent_brew_house_yield=0.70,
              start_volume=7.0,
              final_volume=5.0)

In any recipe you will want to define a few more pieces about the brew that will be done. The first is the Brew House Yield, or how efficient your system is. Typically this is set at 70% efficiency but can be anywhere from 60%-80% for a typical homebrewer.

You also need to describe the start and ending volume of your system. Here the recipe expects to start at 7 Gallons and end at 5 Gallons. The units are expected to be in Imperial Units unless otherwise specified.

Now you’ll want to see what this recipe produces. Just format the recipe to see what you’ve constructed!

print(beer.format())

Produces the output:

Pale Ale
===================================

Brew House Yield:   70.0%
Start Volume:       7.0
Final Volume:       5.0

Boil Gravity:       1.054
Original Gravity:   1.076
Final Gravity:      1.019

ABV / ABW Standard: 7.49% / 5.95%
ABV / ABW Alt:      7.98% / 6.33%

IBU:                33.0 ibu
BU/GU:              0.6

Morey   (SRM/EBC):  6.3 degL / 12.4
Daniels (SRM/EBC):  N/A degL / N/A
Mosher  (SRM/EBC):  7.1 degL / 14.1

Grains
===================================

Pale Malt (2 Row) US Addition
-----------------------------------
Grain Type:        cereal
Weight:            13.96 lbs
Percent Malt Bill: 95.0%
Working Yield:     56.0%
SRM/EBC:           4.5 degL / 8.9

Caramel/Crystal Malt - 20L Addition
-----------------------------------
Grain Type:        cereal
Weight:            0.78 lbs
Percent Malt Bill: 5.0%
Working Yield:     53.0%
SRM/EBC:           3.3 degL / 6.4

Hops
===================================

Centennial Addition
-----------------------------------
Hop Type:     pellet
AA %:         14.0%
Weight:       0.57 oz
Boil Time:    60.0 min
IBUs:         29.2
Utilization:  24.0%

Cascade (US) Addition
-----------------------------------
Hop Type:     pellet
AA %:         7.0%
Weight:       0.76 oz
Boil Time:    5.0 min
IBUs:         3.9
Utilization:  5.0%

Yeast
===================================

Wyeast 1056 Yeast
-----------------------------------
Attenuation:  75.0%

Congratulations, you’ve now constructed your first recipe.


Back to Index

Designing a Recipe

This tutorial is going to show you how to design a beer recipe from raw ingredients. This will walk you through building the objects provided by the BrewDay library, describe some methods, and end by building a complete recipe.

The Style

The first thing you’ll want to do is determine the style of the beer. In this example we’re going to design a Pale Ale. The pale ale uses two types of grain and two types of hops. Let’s build those first:

from brew.grains import Grain
from brew.hops import Hop
pale = Grain('pale 2-row',
             color=2.0,
             ppg=37.0)
crystal = Grain('crystal C20',
                color=20.0,
                ppg=35.0)
grain_list = [pale, crystal]
centennial = Hop(name='centennial',
                 percent_alpha_acids=0.14)
cascade = Hop(name='cascade',
              percent_alpha_acids=0.07)
hop_list = [centennial, cascade]

The style dictates the ingredients, the expected original gravity and the target IBU. Here is a list of what we expect for the latter two:

  • Original Gravity: 1.076
  • Target IBU: 33.0

We must also describe the system we intend to brew on:

  • Percent brew house yield: 70%
  • Start volume: 7.0
  • Final volume: 5.0

This helps us construct the brew.recipes.RecipeBuilder for building our recipe:

from brew.recipes import RecipeBuilder
builder = RecipeBuilder(name='Pale Ale',
                        grain_list=grain_list,
                        hop_list=hop_list,
                        target_ibu=33.0,
                        target_og=1.0761348,
                        percent_brew_house_yield=0.70,
                        start_volume=7.0,
                        final_volume=5.0,
                        )

With the builder class we can now determine the amount of grains and hops that we will use in our recipe.

The Grain Bill

Now that we have a brew.recipes.RecipeBuilder to help us build a recipe we want to determine the grain additions that we’ll be using. This is done by providing an estimate of the percentages each grain will contribute to the final beer. In this case the pale 2-row will contribute 95% and the crystal 20L will contribute 5%.

percent_list = [0.95, 0.05]
grain_additions = builder.get_grain_additions(percent_list)
for grain_add in grain_additions:
    print(grain_add.format())
    print('')

Produces the output:

Pale Malt (2 Row) US Addition
-----------------------------------
Grain Type:        cereal
Weight:            13.96 lbs

Caramel/Crystal Malt - 20L Addition
-----------------------------------
Grain Type:        cereal
Weight:            0.78 lbs

Now you have designed the grain bill for your recipe.

The Hop Bill

Next we will use the brew.recipes.RecipeBuilder to determine the hop additions that we’ll be using. This is done by providing an estimate of the percentages each grain will contribute to the final beer. In this case the centennial will contribute 88.27% and the cascade will contribute 11.73%.

Additionally we need to know how long each hop will be in the boil. For the centennial we will boil 60 minutes and for the cascade we will boil 5 minutes. Time is measured from the end of the boil.

percent_list = [0.8827, 0.1173]
boil_time_list = [60.0, 5.0]
hop_additions = builder.get_hop_additions(percent_list, boil_time_list)
for hop_add in hop_additions:
    print(hop_add.format())
    print('')

Produces the output:

Centennial Addition
-----------------------------------
Hop Type:     pellet
AA %:         14.0%
Weight:       0.57 oz
Boil Time:    60.0 min

Cascade (US) Addition
-----------------------------------
Hop Type:     pellet
AA %:         7.0%
Weight:       0.76 oz
Boil Time:    5.0 min

Now you have designed the hop bill for your recipe.

The Yeast

There is very little control over the yeast that you’ll use. The style typically dictates two or three choices of yeast to get the correct flavor. The remaining question is how much the yeast will attenuate the wort to create alcohol. Since attenuation is a property of the yeast the best you can do is set a target ABV and use that to determine what range of attenuation you will need from your yeast.

Let’s assume that your target ABV is 7.49%. Then you can find the needed attenuation with the following code:

abv = 0.0749
attenuation = builder.get_yeast_attenuation(abv)
print("Attenuation = {:0.1%}".format(attenuation))

Yields:

Attenuation = 75.0%

Using this number you can pick a yeast in your style that has the 75% expected attenuation number. Since there are so many factors surrounding yeast this should only be used as an estimate.


Back to Index

Matching Beer Styles

This tutorial is going to show you how to match your beer against a beer style.

The Style

To begin we’ll want to create an object that represents the style of the beer we wish to brew. To do this we’ll need to make a brew.recipes.Style object.

from brew.styles import Style
style = Style('American Pale Ale',
              category='18',
              subcategory='B',
              og=[1.045, 1.060],
              fg=[1.010, 1.015],
              abv=[0.045, 0.062],
              ibu=[30, 50],
              color=[5, 10])

This represents an American Pale Ale from the BJCP 2015 Style Guidelines. The beer recipe will match the style if it falls within the range of values given here. For example, the original gravity must fall between 1.045 and 1.060 to be considered “in the style” of an American Pale Ale. Similarly the final gravity, alcohol by volume, IBUs, and color must all fall within the range.

Matching a Recipe

In previous tutorials we have created an American Pale Ale recipe. It looked something like this:

from brew.recipes import Recipe
beer = Recipe('Pale Ale',
              grain_additions=grain_additions,
              hop_additions=hop_additions,
              yeast=yeast,
              percent_brew_house_yield=0.70,
              start_volume=7.0,
              final_volume=5.0)

In order to match the recipe we use a method on the class:

>>> style.recipe_matches(recipe)
False
>>> style.og_matches(recipe.og)
False
>>> style.fg_matches(recipe.fg)
False
>>> style.abv_matches(recipe.abv)
False
>>> style.ibu_matches(recipe.ibu)
True
>>> style.color_matches(recipe.color)
True

Interestingly the recipe used in the examples does not match the BJCP style! The only feature that matches the style is the IBUs, but the remaining values for og, fg, abv, and color are all too high. That means its time to correct our recipe.

As a short hand you can also get this information in a more friendly way:

>>> style.recipe_matches(recipe)
['OG is above style', 'FG is above style', 'ABV is above style']

This will help you quickly discover the problems with your recipe.

Correcting a Recipe

The recipe we started with has the right ingredients but it appears the grain bill may contain too much grain. Let’s repeat the builder example but this time change the original gravity to 1.050 and keep everything else the same.

# Define Builder
builder = RecipeBuilder(name='Pale Ale',
                        grain_list=grain_list,
                        hop_list=hop_list,
                        target_ibu=33.0,
                        target_og=1.050,
                        percent_brew_house_yield=0.70,
                        start_volume=7.0,
                        final_volume=5.0,
                        )

# Get Grain Bill
percent_list = [0.95, 0.05]
grain_additions = builder.get_grain_additions(percent_list)
for grain_add in grain_additions:
    print(grain_add.format())
    print('')

When we print out the grain bill with the new parameters we get:

pale 2-row Addition
-----------------------------------
Grain Type:        cereal
Weight:            9.17 lbs

crystal C20 Addition
-----------------------------------
Grain Type:        cereal
Weight:            0.51 lbs

Notice that the pale 2-row addition came down from 13.86 lbs to 9.17 lbs. The crystal 20L has come down from 0.78 lbs to 0.51 lbs. Let’s try this again.

>>> style.recipe_matches(recipe)
False
>>> style.og_matches(recipe.og)
True
>>> style.fg_matches(recipe.fg)
True
>>> style.abv_matches(recipe.abv)
True
>>> style.ibu_matches(recipe.ibu)
True
>>> style.color_matches(recipe.color)
False

It turns out the recipe still doesn’t match. Why? It appears that our color is now off after our adjustments.

Correcting for Color

Correcting color is difficult because it requires an understanding of the grains being used. In this case the pale ale should remain primarily pale 2-row grains. However, we can reduce the pale 2-row and increase the crystal 20L and get a different color.

# Get Grain Bill
percent_list = [0.90, 0.10]
grain_additions = builder.get_grain_additions(percent_list)
for grain_add in grain_additions:
    print(grain_add.format())
    print('')

Gives us:

pale 2-row Addition
-----------------------------------
Grain Type:        cereal
Weight:            8.69 lbs

crystal C20 Addition
-----------------------------------
Grain Type:        cereal
Weight:            1.02 lbs

Notice that the weight of the pale 2-row went down from 9.17 lbs to 8.69 lbs and the crystal 20L went up from 0.51 lbs to 1.02 lbs. Now we can recreate the recipe and check the style:

>>> style.recipe_matches(recipe)
True
>>> style.og_matches(recipe.og)
True
>>> style.fg_matches(recipe.fg)
True
>>> style.abv_matches(recipe.abv)
True
>>> style.ibu_matches(recipe.ibu)
True
>>> style.color_matches(recipe.color)
True

Nice job, now your have a beer recipe that matches the style of an American Pale Ale.


Back to Index

Appendix

API Docs

Model API

brew.constants

brew.grains

class brew.grains.Grain(name, color=None, ppg=None, hwe=None)

A representation of a type of grain.

Parameters:
  • name (str) – The name of the grain
  • color (float) – The color of the grain in SRM
  • ppg (float) – The potential points per gallon
  • hwe (float) – The hot water extract value
Raises:
  • Exception – If color is not provided
  • Exception – If ppg or hwe is not provided
  • Exception – If both ppg and hwe are provided
format()
get_working_yield(percent_brew_house_yield)

Get Working Yield

Parameters:percent_brew_house_yield (float) – The Percent Brew House Yield
Returns:The working yield
Return type:float
to_dict()
to_json()
class brew.grains.GrainAddition(grain, weight=None, grain_type=u'cereal', units=u'imperial')

A representation of the grain as added to a Recipe.

Parameters:
  • grain (Grain) – The Grain object
  • weight (float) – The weight of the grain addition
  • grain_type (str) – The type of the grain being used
  • units (str) – The units
change_units()

Change units of the class from one type to the other

Returns:Grain Addition in new unit type
Return type:GrainAddition
format()
get_cereal_weight()

Get the weight of the addition in cereal weight

Returns:Cereal weight
Return type:float
get_dry_weight()

Get the weight of the addition in Dry Malt Extract weight

Returns:Dry weight
Return type:float
get_lme_weight()

Get the weight of the addition in Liquid Malt Extract weight

Returns:LME weight
Return type:float
get_weight_map()

Get map of grain weights by type

Returns:Grain weights
Return type:dict
set_units(units)

Set the units and unit types

Parameters:units (str) – The units
to_dict()
to_json()
classmethod validate(grain_data)

brew.hops

class brew.hops.Hop(name, percent_alpha_acids=None)

A representation of a type of Hop.

Parameters:
  • name (str) – The name of the hop
  • percent_alpha_acids (float) – The percent alpha acids in the hop
Raises:

Exception – If percent_alpha_acids is not provided

format()
to_dict()
to_json()
class brew.hops.HopAddition(hop, weight=None, boil_time=None, hop_type=u'pellet', utilization_cls=<class 'brew.utilities.hops.HopsUtilizationGlennTinseth'>, utilization_cls_kwargs=None, units=u'imperial')

A representation of the Hop as added to a Recipe.

Parameters:
  • hop (Hop) – The Hop object
  • weight (float) – The weight of the hop addition
  • boil_time (float) – The amount of time the hop is boiled
  • hop_type (float) – The type of the hop being used
  • utilization_cls (HopsUtilization) – The utilization class used for calculation
  • utilization_cls_kwargs (dict) – The kwargs to initialize the utilization_cls object
  • units (str) – The units
change_units()

Change units of the class from one type to the other

Returns:Hop Addition in new unit type
Return type:HopAddition
format()
get_alpha_acid_units()

Get Alpha Acid Units

Returns:alpha acid units
Return type:float
get_ibus(sg, final_volume)

Get the IBUs

Parameters:
  • sg (float) – The specific gravity of the wort
  • final_volume (float) – The final volume of the wort
Returns:

The IBUs of the wort

Return type:

float

set_units(units)

Set the units and unit types

Parameters:units (str) – The units
to_dict()
to_json()
classmethod validate(hop_data)

brew.parsers

class brew.parsers.DataLoader(data_dir)

Base class for loading data from data files inside the data_dir.

Parameters:data_dir (str) – The directory where the data resides
DATA = {}
EXT = ''
classmethod format_name(name)

Reformat a given name to match the filename of a data file.

get_item(dir_suffix, item_name)
Parameters:
  • dir_suffix (str) – The directory name suffix
  • item_name (str) – The name of the item to load
Returns:

The item as a python dict

Raises:
  • Exception – If item directory does not exist
  • Warning – If item not found in the directory
classmethod read_data(filename)
Parameters:filename (str) – The filename of the file to read
Raises:NotImplementedError – Must be supplied in inherited class
class brew.parsers.JSONDataLoader(data_dir)

Load data from JSON files inside the data_dir.

Parameters:data_dir (str) – The directory where the data resides
DATA = {}
EXT = 'json'
format_name(name)

Reformat a given name to match the filename of a data file.

get_item(dir_suffix, item_name)
Parameters:
  • dir_suffix (str) – The directory name suffix
  • item_name (str) – The name of the item to load
Returns:

The item as a python dict

Raises:
  • Exception – If item directory does not exist
  • Warning – If item not found in the directory
classmethod read_data(filename)
Parameters:filename (str) – The filename of the file to read
Returns:The data loaded from a JSON file
parsers.parse_cereals(cereal, loader, dir_suffix='cereals/')

Parse grains data from a recipe

Parameters:
  • cereal (dict) – A representation of a cereal
  • loader (DataLoader) – A class to load additional information

Grain must have the following top level attributes:

  • name (str)
  • weight (float)
  • data (dict) (optional)

Additionally grains may contain override data in the ‘data’ attribute with the following keys:

  • color (float)
  • ppg (int)
parsers.parse_hops(hop, loader, dir_suffix='hops/')

Parse hops data from a recipe

Parameters:
  • hops (dict) – A representation of a hop
  • loader (DataLoader) – A class to load additional information

Hops must have the following top level attributes:

  • name (str)
  • weight (float)
  • boil_time (float)
  • data (dict) (optional)

Additionally hops may contain override data in the ‘data’ attribute with the following keys:

  • percent_alpha_acids (float)
parsers.parse_yeast(yeast, loader, dir_suffix='yeast/')

Parse yeast data from a recipe

Parameters:
  • hops (dict) – A representation of a yeast
  • loader (DataLoader) – A class to load additional information

Yeast must have the following top level attributes:

  • name (str)
  • data (dict) (optional)

Additionally yeast may contain override data in the ‘data’ attribute with the following keys:

  • percent_attenuation (float)
parsers.parse_recipe(recipe, loader, cereals_loader=None, hops_loader=None, yeast_loader=None, cereals_dir_suffix='cereals/', hops_dir_suffix='hops/', yeast_dir_suffix='yeast/')

Parse a recipe from a python Dict

Parameters:
  • recipe (dict) – A representation of a recipe
  • loader (DataLoader) – A class to load additional information
  • cereal_loader (DataLoader) – A class to load additional information specific to cereals
  • hops_loader (DataLoader) – A class to load additional information specific to hops
  • yeast_loader (DataLoader) – A class to load additional information specific to yeast

A recipe must have the following top level attributes:

  • name (str)
  • start_volume (float)
  • final_volume (float)
  • grains (list(dict))
  • hops (list(dict))
  • yeast (dict)

Additionally the recipe may contain override data in the ‘data’ attribute with the following keys:

  • percent_brew_house_yield (float)
  • units (str)

All other fields will be ignored and may be used for other metadata.

The dict objects in the grains, hops, and yeast values are required to have the key ‘name’ and the remaining attributes will be looked up in the data directory if they are not provided.

brew.recipes

class brew.recipes.Recipe(name, grain_additions=None, hop_additions=None, yeast=None, percent_brew_house_yield=0.7, start_volume=7.0, final_volume=5.0, units=u'imperial')

A representation of a Recipe that can be brewed to make beer.

Parameters:
  • name (str) – The name of the recipe
  • grain_additions (list of GrainAddition objects) – A list of Grain Additions
  • hop_additions (list of HopAddition objects) – A list of Hop Additions
  • percent_brew_house_yield (float) – The brew house yield
  • start_volume (float) – The starting volume of the wort
  • final_volume (float) – The final volume of the wort
  • units (str) – The units
Raises:
  • Exception – If the units of any GrainAddition is not the same as the units of the Recipe
  • Exception – If the units of any HopAddition is not the same as the units of the Recipe
abv
bg
change_units()

Change units of the class from one type to the other

Returns:Recipe in new unit type
Return type:Recipe
color
fg
format()
get_boil_gravity(evaporation=0.0)

Get the boil specific gravity

Parameters:evaporation (float) – Percent water evaporation during boil
Returns:The boil specific gravity
Return type:float
get_boil_gravity_units(evaporation=0.0)

Get the boil gravity units

Parameters:evaporation (float) – Percent water evaporation during boil
Returns:The boil gravity units
Return type:float
get_brew_house_yield(plato_actual, vol_actual)

Get the Brew House Yield

Parameters:
  • plato_actual (float) – The actual degrees Plato
  • vol_actual (float) – The actual volume collected from the kettle
Returns:

Brew House Yield

Rtyle:

float

get_bu_to_gu()

Get BU to GU Ratio

Returns:Ratio of Bitterness Units to Original Gravity Units
Return type:float
get_degrees_plato()

Get the degrees plato

Returns:The degrees plato of the wort
Return type:float
get_extract_weight()

Get the weight of the extract

Returns:The weight of extract
Return type:float
get_final_gravity()

Get the final specific gravity

Returns:The final specific gravity
Return type:float
get_final_gravity_units()

Get the final gravity units

Returns:The final gravity units
Return type:float
get_grain_add_cereal_weight(grain_add)

Get Grain Addition as Cereal

Parameters:grain_add (GrainAddition) – The Grain Addition
Returns:The weight of the grain as Cereal
Return type:float

When converting DME or LME to grain its important to remember that you can’t get 100% efficiency from grains. Dividing by the brew house yield will increase the size of the grain accordingly.

get_grain_add_dry_weight(grain_add)

Get Grain Addition as DME

Parameters:grain_add (GrainAddition) – The Grain Addition
Returns:The weight of the grain as DME
Return type:float

When converting Grain to DME its important to remember that you can’t get 100% efficiency from grains. Multiplying by the brew house yield will decrease the size of the DME accordingly.

get_mash_water_volume(liquor_to_grist_ratio)

Get the Mash Water Volume

Parameters:liquor_to_grist_ratio (float) – The Liquor to Grist Ratio
Returns:The mash water volume
Return type:float
get_original_gravity()

Get the original specific gravity

Returns:The original specific gravity
Return type:float
get_original_gravity_units()

Get the original gravity units

Returns:The original gravity units
Return type:float
get_percent_ibus(hop_add)

Get the percentage the hops contributes to total ibus

Parameters:hop_add (HopAddition) – The Hop Addition
Returns:The percent the hops contributes to total ibus
Return type:float
get_percent_malt_bill(grain_add)

Get Percent Malt Bill

Parameters:grain_add (GrainAddition) – The Grain Addition
Returns:The percent extract the addition adds to the bill
Return type:float

To ensure different additions are measured equally each is converted to dry weight.

classmethod get_strike_temp(mash_temp, malt_temp, liquor_to_grist_ratio)

Get Strike Water Temperature

Parameters:
  • mash_temp (float) – Mash Temperature
  • malt_temp (float) – Malt Temperature
  • liquor_to_grist_ratio (float) – The Liquor to Grist Ratio
Returns:

The strike water temperature

Return type:

float

get_total_dry_weight()

Get total DME weight

Returns:The total weight of the DME
Return type:float
get_total_grain_weight()

Get total Cereal weight

Returns:The total weight of the Cereal
Return type:float
get_total_ibu()

Convenience method to get total IBU for the recipe

Returns:The total IBU for the Recipe
Return type:float
get_total_points()

Get the total points of the recipe

Returns:PPG or HWE depending on the units of the Recipe
Return type:float
get_total_wort_color()

Get the Total Color of the Wort in SRM using Morey Power Equation

Returns:The total color of the wort in SRM
Return type:float
get_total_wort_color_map()

Get a map of wort color by method

Returns:A map of wort color in SRM and EBC by method (Morey, Daniels, and Mosher)
Return type:dict
get_wort_color(grain_add)

Get the Wort Color in SRM

Parameters:grain_add (GrainAddition) – The Grain Addition to calculate
Returns:The SRM of the Grain Addition
Return type:float
get_wort_color_mcu(grain_add)

Get the Wort Color in Malt Color Units

Parameters:grain_add (GrainAddition) – The Grain Addition to calculate
Returns:The MCU of the Grain Addition
Return type:float
grain_lookup = {}
hop_lookup = {}
ibu
og
plato
set_units(units)

Set the units and unit types

Parameters:units (str) – The units
to_dict()
to_json()
classmethod validate(recipe)
class brew.recipes.RecipeBuilder(name, grain_list=None, hop_list=None, target_ibu=33.0, target_og=1.05, percent_brew_house_yield=0.7, start_volume=7.0, final_volume=5.0, units=u'imperial')

A class for building recipes

Parameters:
  • name (str) – The name of the recipe
  • grain_list (list of Grain objects) – A list of Grains
  • hop_list (list of Hop objects) – A list of Hops
  • target_ibu (float) – The IBU Target
  • target_og (float) – The Original Gravity Target
  • percent_brew_house_yield (float) – The brew house yield
  • start_volume (float) – The starting volume of the wort
  • final_volume (float) – The final volume of the wort
  • units (str) – The units
change_units()

Change units of the class from one type to the other

Returns:RecipeBuilder in new unit type
Return type:RecipeBuilder
get_grain_additions(percent_list)

Calculate GrainAdditions from list of percentages

Parameters:

percent_list (list) – A list of percentages mapped to each Grain

Returns:

A list of Grain Additions

Return type:

list(GrainAddition)

Raises:
  • Exception – If sum of percentages does not equal 1.0
  • Exception – If length of percent_list does not match length of self.grain_list
get_hop_additions(percent_list, boil_time_list, hop_type=u'pellet', utilization_cls=<class 'brew.utilities.hops.HopsUtilizationGlennTinseth'>)

Calculate HopAdditions from list of boil times

Parameters:
  • boil_time_list (list) – A list of boil times mapped to each Hop
  • utilization_cls (HopsUtilization) – The utilization class used for calculation
Returns:

A list of Hop Additions

Return type:

list(HopAddition)

Raises:
  • Exception – If sum of percentages does not equal 1.0
  • Exception – If length of percent_list does not match length of self.grain_list
  • Exception – If length of boil_time_list does not match length of self.hop_list
get_yeast_attenuation(abv)

Estimate yeast attenuation given a target abv

Parameters:abv (float) – Alcohol by Volume
Returns:Yeast Attenuation Percentage
Return type:float

This uses the ABV Standard Equation

grain_lookup = {}
hop_lookup = {}
set_units(units)

Set the units and unit types

Parameters:units (str) – The units

brew.styles

class brew.styles.Style(style, category=u'', subcategory=u'', og=None, fg=None, abv=None, ibu=None, color=None)

A beer style

Parameters:
  • category (str) – The style category
  • subcategory (str) – The style subcategory
  • style (str) – The style name
  • og (list(float)) – The lower and upper original gravity
  • fg (list(float)) – The lower and upper final gravity
  • abv (list(float)) – The lower and upper alcohol by volume
  • ibu (list(float)) – The lower and upper IBU
  • color (list(float)) – The lower and upper color (in SRM)
abv_errors(abv)

Return list of errors if abv doesn’t match the style

Parameters:abv (float) – Alcohol by Volume
Returns:List
Rtyle:list
abv_matches(abv)

Determine if abv matches the style

Parameters:abv (float) – Alcohol by Volume
Returns:True if matches style, otherwise False
Rtyle:bool
color_errors(color)

Return list of errors if color doesn’t match the style

Parameters:color (float) – Color in SRM
Returns:List
Rtyle:list
color_matches(color)

Determine if color matches the style

Parameters:color (float) – Color in SRM
Returns:True if matches style, otherwise False
Rtyle:bool
fg_errors(fg)

Return list of errors if fg doesn’t match the style

Parameters:fg (float) – Final Gravity
Returns:List
Rtyle:list
fg_matches(fg)

Determine if fg matches the style

Parameters:fg (float) – Final Gravity
Returns:True if matches style, otherwise False
Rtyle:bool
format()
ibu_errors(ibu)

Return list of errors if ibu doesn’t match the style

Parameters:ibu (float) – IBU
Returns:List
Rtyle:list
ibu_matches(ibu)

Determine if ibu matches the style

Parameters:ibu (float) – IBU
Returns:True if matches style, otherwise False
Rtyle:bool
og_errors(og)

Return list of errors if og doesn’t match the style

Parameters:og (float) – Original Gravity
Returns:List
Rtyle:list
og_matches(og)

Determine if og matches the style

Parameters:og (float) – Original Gravity
Returns:True if matches style, otherwise False
Rtyle:bool
recipe_errors(recipe)

Return list errors if the recipe doesn’t match the style

Parameters:recipe (Recipe) – A Recipe object
Returns:Errors
Return type:list
recipe_matches(recipe)

Determine if a recipe matches the style

Parameters:recipe (Recipe) – A Recipe object
Returns:True if recipe matches style, otherwise False
Return type:bool
to_dict()
to_json()
classmethod validate(recipe)

brew.validators

validators.validate_grain_type(grain_type)

Validate a grain type

Parameters:grain_type (str) – Type of Grain
Returns:grain type
Return type:str
Raises:Exception – If grain type is unknown
validators.validate_hop_type(hop_type)

Validate a hop type

Parameters:hop_type (str) – Type of Grain
Returns:hop type
Return type:str
Raises:Exception – If hop type is unknown
validators.validate_percentage(percent)

Validate decimal percentage

Parameters:percent (float) – Percentage between 0.0 and 1.0
Returns:percentage
Return type:float
Raises:Exception – If decimal percentage not between 0.0 and 1.0
validators.validate_units(units)

Validate units

Parameters:units (str) – Unit type
Returns:units
Return type:str
Raises:Exception – If units is unknown
validators.validate_required_fields(data, required_fields)

Validate fields which are required as part of the data.

Parameters:
  • data (dict) – A python dictionary to check for required fields
  • required_fields (list(tuple)) – Values and types to check for in data
Raises:
  • Exception – Required field is missing from data
  • Exception – Required field is of the wrong type

The format is a list of tuples where the first element is a string with a value that should be a key found in the data dict and where the second element is a python type or list/tuple of python types to check the field against.

validators.validate_optional_fields(data, optional_fields, data_field=u'data')

Validate fields which are optional as part of the data.

Parameters:
  • data (dict) – A python dictionary to check for required fields
  • optional_fields (list(tuple)) – Values and types to check for in data
  • data_field (str) – The key in the data dictionary containing the optional fields
Raises:

Exception – Optional field is of the wrong type

The format is a list of tuples where the first element is a string with a value that should be a key found in the data dict and where the second element is a python type or list/tuple of python types to check the field against.

brew.yeasts

class brew.yeasts.Yeast(name, percent_attenuation=0.75)

A representation of a type of Yeast as added to a Recipe.

Parameters:percent_attenuation (float) – The percentage the yeast is expected to attenuate the sugar in the yeast to create alcohol
Raises:Exception – If percent_attenuation is not provided
format()
to_dict()
to_json()
classmethod validate(yeast_data)

Utilities API Docs

brew.utilities.abv

abv.apparent_attenuation(original_extract, apparent_extract)

Apparent Attenuation

Parameters:
  • original_extract (float) – Original degrees Plato
  • apparent_extract (float) – Apparent degrees Plato of finished beer
Returns:

The percent of apparent attenuation

Return type:

float

Source:

abv.real_attenuation(original_extract, real_extract)

Real Attenuation

Parameters:
  • original_extract (float) – Original degrees Plato
  • real_extract (float) – Real degrees Plato of finished beer
Returns:

The percent of real attenuation

Return type:

float

abv.real_attenuation_from_apparent_extract(original_extract, apparent_extract)

Real Attenuation from Apparent Extract

Parameters:
  • original_extract (float) – Original degrees Plato
  • apparent_extract (float) – Apparent degrees Plato of finished beer
Returns:

The percent of real attenuation

Return type:

float

abv.alcohol_by_volume_standard(og, fg)

Alcohol by Volume Standard Calculation

Parameters:
  • og (float) – Original Gravity
  • fg (float) – Final Gravity
Returns:

Alcohol by Volume decimal percentage

Return type:

float

Most brewing sites use this basic formula:

\(\text{ABV} = \big(\text{og} - \text{fg}\big) \times 131.25\)

This equation was created before the computer age. It is easy to do by hand, and over time became the accepted formula for home brewers!

Variations on this equation which report within tenths of each other come from The Joy of Homebrewing Method by Charlie Papazian, Bee Lee’s Method, Beer Advocate Method. Some variations use 131 instead of 131.25. The resulting difference is pretty minor.

Source:

\(\text{ABV} = \frac{46.07 \text{g/mol C2H6O}}{44.0095 \text{g/mol CO2}} \times \frac{1.0}{0.7936} \times 100 \times (og - fg)\)

abv.alcohol_by_volume_alternative(og, fg)

Alcohol by Volume Alternative Calculation

Parameters:
  • og (float) – Original Gravity
  • fg (float) – Final Gravity
Returns:

Alcohol by Volume decimal percentage

Return type:

float

Alternate Formula:

A more complex equation which attempts to provide greater accuracy at higher gravities is:

\(\text{ABV} = \frac{76.08 \times \big( \text{og} - \text{fg} \big)}{1.775 - \text{og}} \times \frac{\text{fg}}{0.794}\)

The alternate equation reports a higher ABV for higher gravity beers. This equation is just a different take on it. Scientists rarely agree when it comes to equations. There will probably be another equation for ABV down the road.

The complex formula, and variations on it come from:

  • Ritchie Products Ltd, (Zymurgy, Summer 1995, vol. 18, no. 2)
  • Michael L. Hall’s article Brew by the Numbers: Add Up What’s in Your Beer, and Designing Great Beers by Daniels.

Source:

abv.alcohol_by_weight(abv)

Alcohol by Weight from ABV

Parameters:abv (float) – Alcohol by Volume
Returns:Alcohol by Weight
Return type:float

brew.utilities.color

color.srm_to_ebc(srm)

Convert SRM to EBC Color

Parameters:srm (float) – SRM Color
Returns:EBC Color
Return type:float
color.ebc_to_srm(ebc)

Convert EBC to SRM Color

Parameters:ebc (float) – EBC Color
Returns:SRM Color
Return type:float
color.calculate_mcu(grain_weight, beer_color, final_volume, units=u'imperial')

Calculate MCU from Grain

Parameters:
  • grain_weight (float) – Grain weight in lbs or kg
  • beer_color (float) – Beer color in deg Lovibond
  • final_volume (float) – Final Volume in gal or liters
  • units (str) – The units

Source:

color.calculate_srm_mosher(mcu)

Mosher Equation for SRM

Parameters:mcu (float) – The Malt Color Units
Returns:SRM Color
Return type:float
Raises:Exception – If the MCU is < 7.0
color.calculate_srm_daniels(mcu)

Daniels Equation for SRM

Parameters:mcu (float) – The Malt Color Units
Returns:SRM Color
Return type:float
Raises:Exception – If the MCU is < 11.0
color.calculate_srm_daniels_power(mcu)

Daniels Power Equation for SRM based on work by Druey

Parameters:mcu (float) – The Malt Color Units
Returns:SRM Color
Return type:float
Raises:Exception – If the SRM is > 50.0
color.calculate_srm_noonan_power(mcu)

Noonan Power Equation for SRM based on work by Druey

Parameters:mcu (float) – The Malt Color Units
Returns:SRM Color
Return type:float
Raises:Exception – If the SRM is > 50.0
color.calculate_srm_morey_hybrid(mcu)

A hybrid approach used by Morey for SRM.

Parameters:mcu (float) – The Malt Color Units
Returns:SRM Color
Return type:float
Raises:Exception – If the MCU is > 50.0

Assumptions:

  1. SRM is approximately equal to MCU for values from 0 to 10.
  2. Homebrew is generally darker than commercial beer.
  3. Base on the previous qualitative postulate, I assumed that Ray Daniels’ predicted relationship exists for beers with color greater than 10.
  4. Since Mosher’s equation predicts darker color than Daniels’ model for values of MCU greater than 37, I assumed that Mosher’s approximation governed beer color for all values more than 37 MCUs.
  5. Difference in color for beers greater than 40 SRM are essentially impossible to detect visually; therefore, I limited the analysis to SRM of 50 and less.

Source:

color.calculate_srm_morey(mcu)

Morey Equation for SRM

Parameters:mcu (float) – The Malt Color Units
Returns:SRM Color
Return type:float
Raises:Exception – If the SRM is > 50.0

Source:

color.calculate_srm(mcu)

General SRM calculation uses the Morey Power Equation

Parameters:mcu (float) – The Malt Color Units
Returns:SRM Color
Return type:float
Raises:Exception – If the SRM is > 50.0
color.lovibond_to_srm(lovibond)

Convert deg Lovibond to SRM

Parameters:lovibond (float) – The degrees Lovibond
Returns:SRM Color
Return type:float

Source:

color.srm_to_lovibond(srm)

Convert SRM to deg Lovibond

Parameters:srm (float) – SRM Color
Returns:The degrees Lovibond
Return type:float

Source:

color.srm_to_a430(srm, dilution=1.0)

Get attenuation at A430 from SRM and dilution

Parameters:
  • srm (float) – SRM Color
  • dilution (float) – The dilution factor (D=1 for undiluted, D=2 for 1:1 dilution, etc)
Returns:

The attenuiation at 430nm

Return type:

float

Source:

color.ebc_to_a430(ebc, dilution=1.0)

Get attenuation at A430 from EBC and dilution

Parameters:
  • ebc (float) – EBC Color
  • dilution (float) – The dilution factor (D=1 for undiluted, D=2 for 1:1 dilution, etc)
Returns:

The attenuiation at 430nm

Return type:

float

Source:

brew.utilities.hops

class brew.utilities.hops.HopsUtilization(hop_addition, units=u'imperial')

http://www.boondocks-brewing.com/hops

Parameters:
  • hop_addition (HopAddition) – A hop addition
  • units (str) – The units
change_units()

Change units of the class from one type to the other

Returns:Hop Addition in new unit type
Return type:HopAddition
classmethod format_utilization_table()

Percent Alpha Acid Utilization - Boil Time vs Wort Original Gravity

Returns:The formatted utilization table
Return type:str

Source:

get_ibus(sg, final_volume)

Get the IBUs

Parameters:
  • sg (float) – Specific Gravity
  • final_volume (float) – The Final Volume of the wort
Returns:

The IBUs of the wort

Return type:

float

classmethod get_percent_utilization(sg, boil_time)

Get the percent utilization

Parameters:
  • sg (float) – Specific Gravity
  • boil_time (float) – The Boil Time in minutes
Raises:

NotImplementedError – This must be overridden

classmethod get_utilization_table(gravity_list, boil_time_list, sig=3)

Get the utilization table for the class

Parameters:
  • gravity_list (list) – A list of specific gravities
  • boil_time_list (list) – A list of boil times in minutes
  • sig (int) – Significant figures to round
Returns:

A table of utilization for specific gravity vs boil time

Return type:

list

set_units(units)

Set the units and unit types

Parameters:units (str) – The units
class brew.utilities.hops.HopsUtilizationJackieRager(hop_addition, units=u'imperial')

Jackie Rager Hops Utilization Method

Best for extract and partial mash brewing.

Parameters:
  • hop_addition (HopAddition) – A hop addition
  • units (str) – The units
change_units()

Change units of the class from one type to the other

Returns:Hop Addition in new unit type
Return type:HopAddition
format_utilization_table()

Percent Alpha Acid Utilization - Boil Time vs Wort Original Gravity

Returns:The formatted utilization table
Return type:str

Source:

classmethod get_c_gravity(sg)

Get Cgravity

Parameters:sg (float) – Specific Gravity
Returns:Cgravity
Return type:float

Cgravity is a constant to adjust the boil size when dealing with specific gravity greater than 1.050 in the calculation of IBUs.

get_ibus(sg, final_volume)

Get the IBUs

Parameters:
  • sg (float) – Specific Gravity
  • final_volume (float) – The Final Volume of the wort
Returns:

The IBUs of the wort

Return type:

float

classmethod get_percent_utilization(sg, boil_time)

Get the percent utilization

Parameters:
  • sg (float) – Specific Gravity
  • boil_time (float) – The Boil Time in minutes
Returns:

The percent utilization

Return type:

float

Source:

get_utilization_table(gravity_list, boil_time_list, sig=3)

Get the utilization table for the class

Parameters:
  • gravity_list (list) – A list of specific gravities
  • boil_time_list (list) – A list of boil times in minutes
  • sig (int) – Significant figures to round
Returns:

A table of utilization for specific gravity vs boil time

Return type:

list

set_units(units)

Set the units and unit types

Parameters:units (str) – The units
class brew.utilities.hops.HopsUtilizationGlennTinseth(hop_addition, units=u'imperial')

Glenn Tinseth Hops Utilization Method

Best for all grain brewing.

Parameters:
  • hop_addition (HopAddition) – A hop addition
  • units (str) – The units
change_units()

Change units of the class from one type to the other

Returns:Hop Addition in new unit type
Return type:HopAddition
format_utilization_table()

Percent Alpha Acid Utilization - Boil Time vs Wort Original Gravity

Returns:The formatted utilization table
Return type:str

Source:

classmethod get_bigness_factor(sg)

Get Bigness Factor

Parameters:sg (float) – Specific Gravity
Returns:Bigness Factor
Return type:float

The Bigness factor accounts for reduced utilization due to higher wort gravities. Use an average gravity value for the entire boil to account for changes in the wort volume.

\(\text{Bigness factor} = 1.65 \times 0.000125^{\text{wort gravity} - 1}\)

classmethod get_boil_time_factor(boil_time)

Get Boil Time Factor

Parameters:boil_time (float) – Boil Time in minutes
Returns:Boil Time Factor
Return type:float

The Boil Time factor accounts for the change in utilization due to boil time:

\(\text{Boil Time factor} = \frac{1 - e^{-0.04 \times \text{time in mins}}}{4.15}\)

get_ibus(sg, final_volume)

Get the IBUs

Parameters:
  • sg (float) – Specific Gravity
  • final_volume (float) – The Final Volume of the wort
Returns:

The IBUs of the wort

Return type:

float

classmethod get_percent_utilization(sg, boil_time)

Get the percent utilization

Parameters:
  • sg (float) – Specific Gravity
  • boil_time (float) – The Boil Time in minutes
Returns:

The percent utilization

Return type:

float

Source:

get_utilization_table(gravity_list, boil_time_list, sig=3)

Get the utilization table for the class

Parameters:
  • gravity_list (list) – A list of specific gravities
  • boil_time_list (list) – A list of boil times in minutes
  • sig (int) – Significant figures to round
Returns:

A table of utilization for specific gravity vs boil time

Return type:

list

set_units(units)

Set the units and unit types

Parameters:units (str) – The units

brew.utilities.malt

malt.dry_to_liquid_malt_weight(malt)

DME to LME Weight

Parameters:malt (float) – Weight of DME
Returns:LME Weight
Return type:float

Source:

malt.liquid_to_dry_malt_weight(malt)

LME to DME Weight

Parameters:malt (float) – Weight of LME
Returns:DME Weight
Return type:float
malt.grain_to_liquid_malt_weight(grain)

Grain to LME Weight

Parameters:grain (float) – Weight of Grain
Returns:LME Weight
Return type:float
malt.liquid_malt_to_grain_weight(malt)

LME to Grain Weight

Parameters:malt (float) – Weight of LME
Returns:Grain Weight
Return type:float
malt.dry_malt_to_grain_weight(malt)

DME to Grain Weight

Parameters:malt (float) – Weight of DME
Returns:Grain Weight
Return type:float
malt.grain_to_dry_malt_weight(malt)

Grain to DME Weight

Parameters:grain (float) – Weight of Grain
Returns:DME Weight
Return type:float
malt.specialty_grain_to_liquid_malt_weight(grain)

Specialty Grain to LME Weight

Parameters:grain (float) – Weight of Specialty Grain
Returns:LME Weight
Return type:float
malt.liquid_malt_to_specialty_grain_weight(malt)

LME to Specialty Grain Weight

Parameters:grain (float) – Weight of LME
Returns:Specialty Grain Weight
Return type:float
malt.fine_grind_to_coarse_grind(fine_grind, fc_diff=0.017)

Fine Grind to Coarse Grind Percentage

Parameters:
  • fine_grind (float) – A percentage from the malt bill
  • fc_diff (float) – The F/C difference percentage from the malt bill
Returns:

Coarse Grind Percentage

Return type:

float

malt.coarse_grind_to_fine_grind(coarse_grind, fc_diff=0.017)

Coarse Grind to Fine Grind Percentage

Parameters:
  • coarse_grind (float) – A percentage from the malt bill
  • fc_diff (float) – The F/C difference percentage from the malt bill
Returns:

Fine Grind Percentage

Return type:

float

malt.dry_basis_to_as_is_basis(dry_basis, moisture_content=0.04)

Dry Basis to As-Is Basis Percentage

Parameters:
  • dry_basis (float) – A percentage from the malt bill in decimal form
  • moisture_content (float) – A percentage of moisture content in finished malt in decimal form
Returns:

As-Is Basis

Return type:

float

malt.as_is_basis_to_dry_basis(as_is, moisture_content=0.04)

As-Is Basis to Dry Basis Percentage

Parameters:
  • as_is (float) – A percentage from the malt bill in decimal form
  • moisture_content (float) – A percentage of moisture content in finished malt in decimal form
Returns:

Dry Basis

Return type:

float

malt.sg_from_dry_basis(dbcg, moisture_content=0.04, moisture_correction=0.0, brew_house_efficiency=0.9)

Specific Gravity from Dry Basis Percentage

Parameters:
  • dbcg (float) – Dry Basis Coarse Grain in decimal form
  • moisture_content (float) – A percentage of moisture content in finished malt in decimal form
  • moisture_correction (float) – A percentage correction in decimal form
  • brew_house_efficiency (float) – The efficiency in decimal form
Returns:

Specific Gravity available from Malt

Return type:

float

malt.plato_from_dry_basis(dbcg, moisture_content=0.04, moisture_correction=0.0, brew_house_efficiency=0.9)

Degrees Plato from Dry Basis Percentage

Parameters:
  • dbcg (float) – Dry Basis Coarse Grain in decimal form
  • moisture_content (float) – A percentage of moisture content in finished malt in decimal form
  • moisture_correction (float) – A percentage correction in decimal form
  • brew_house_efficiency (float) – The efficiency in decimal form
Returns:

Specific Gravity available from Malt

Return type:

float

malt.basis_to_hwe(basis_percentage)

Basis Percentage to Hot Water Extract

Parameters:basis_percentage (float) – Basis as percentage
Returns:Hot Water Extract as Ldeg/kg, dry basis
Return type:float

Ldeg/kg means how many litres of wort with a specific gravity of 1.001 you could produce from a kilogram of the fermentable

For example, if you had a kilogram of sucrose, you could make up 386 litres of wort with a specific gravity of 1.001.

malt.hwe_to_basis(hwe)

Hot Water Extract to Basis Percentage

Parameters:hwe (float) – Hot Water Extract as Ldeg/kg, dry basis
Returns:Basis as percentage
Return type:float
malt.ppg_to_hwe(ppg)

Points Per Gallon to Hot Water Extract

Parameters:ppg (float) – Points Per Gallon
Returns:Hot Water Extract
Return type:float
malt.hwe_to_ppg(hwe)

Hot Water Extract to Points Per Gallon

Parameters:hwe (float) – Hot Water Extract
Returns:Points Per Gallon
Return type:float

brew.utilities.sugar

sugar.sg_to_gu(sg)

Specific Gravity to Gravity Units

Parameters:sg (float) – Specific Gravity
Returns:Gravity Units
Return type:float
sugar.gu_to_sg(gu)

Gravity Units to Specific Gravity

Parameters:gu (float) – Gravity Units
Returns:Specific Gravity
Return type:float
sugar.plato_to_sg(deg_plato)

Degrees Plato to Specific Gravity

Parameters:deg_plato (float) – Degrees Plato
Returns:Specific Gravity
Return type:float

The simple formula for S.G. is:

\(\text{SG} = 1 + 0.004 \times \text{Plato}\)

The more precise calculation of SG is:

\(\text{SG} = \frac{Plato}{258.6 - \big(\frac{Plato}{258.2} \times 227.1\big)} + 1\)

Source:

sugar.sg_to_plato(sg)

Specific Gravity to Degrees Plato

Parameters:sg (float) – Specific Gravity
Returns:Degrees Plato
Return type:float

\(\text{Plato} = \frac{\big(\text{SG} - 1\big) \times 1000}{4}\)

The more precise calculation of Plato is:

\(\text{Plato} = -616.868 + 1111.14 \times sg - 630.272 \times sg^2 + 135.997 \times sg^3\)

Source:

sugar.brix_to_sg(brix)

Degrees Brix to Specific Gravity

Parameters:brix (float) – Degrees Brix
Returns:Specific Gravity
Return type:float

Source:

sugar.sg_to_brix(sg)

Specific Gravity to Degrees Brix

Parameters:sg (float) – Specific Gravity
Returns:Degrees Brix
Return type:float

Source:

sugar.brix_to_plato(brix)

Degrees Brix to Degrees Plato

Parameters:brix (float) – Degrees Brix
Returns:Degrees Plato
Return type:float

The difference between the degBx and degP as calculated from the respective polynomials is:

\(\text{degP} - \text{degBx} = \big(\big(\big(-2.81615*sg + 8.79724\big) \times sg - 9.1626\big) \times sg + 3.18213\big)\)

The difference is generally less than +/-0.0005 degBx or degP with the exception being for weak solutions.

Source:

sugar.plato_to_brix(plato)

Degrees Plato to Degrees Brix

Parameters:brix (float) – Degrees Plato
Returns:Degrees Brix
Return type:float
sugar.apparent_extract_to_real_extract(original_extract, apparent_extract)

Apparent Extract to Real Extract in degrees Plato

Parameters:
  • original_extract (float) – Original degrees Plato
  • apparent_extract (float) – Apparent degrees Plato of finished beer
Returns:

Real degrees Plato of finished beer

Return type:

float

Source:

  • Formula from Balling: De Clerck, Jean, A Textbook Of Brewing, Chapman & Hall Ltd., 1958
sugar.hydrometer_adjustment(sg, temp, units=u'imperial')

Adjust the Hydrometer if the temperature deviates from 59degF.

Parameters:
  • sg (float) – Specific Gravity
  • temp (float) – Temperature
  • units (str) – The units
Returns:

Specific Gravity corrected for temperature

Return type:

float

Raises:

Exception – If temperature outside freezing to boiling range of water

The correction formula is from Lyons (1992), who used the following formula to fit data from the Handbook of Chemistry and Physics (CRC):

\(\text{Correction(@59F)} = 1.313454 - 0.132674 \times T + 2.057793e^{-3} \times T^2 - 2.627634e^{-6} \times T^3\)

where T is in degrees F.

Sources:

sugar.refractometer_adjustment(og, fg)

Adjust the Refractometer for the presence of alcohol.

Parameters:
  • og (float) – Original Gravity
  • fg (float) – Final Gravity
Returns:

Final Gravity adjusted

Return type:

float

NOTE: This calculation assumes using Brix or Plato, so the input will be converted from SG to Plato and then converted back.

Sources:

brew.utilities.temperature

temperature.fahrenheit_to_celsius(temp)

Convert degrees Fahrenheit to degrees Celsius

Parameters:temp (float) – The temperature in Fahrenheit
Returns:The temperature in Celsius
Return type:float
temperature.celsius_to_fahrenheit(temp)

Convert degrees Celsius to degrees Fahrenheit

Parameters:temp (float) – The temperature in Celsius
Returns:The temperature in Fahrenheit
Return type:float

brew.utilities.yeast

class brew.utilities.yeast.YeastModel(method, units=u'imperial')
METHOD_TO_GROWTH_ADJ = {u'shaking': 0.0, u'stir plate': 0.0, u'no agitation': 0.0}
get_growth_rate(inoculation_rate)
get_inoculation_rate(growth_rate)
get_resulting_pitch_rate(starter_cell_count, original_gravity=1.036, final_volume=5.0)
get_starter_volume(available_cells, starter_volume=0.5283443537159779, original_gravity=1.036)

Calculate the number of cells given a stater volume and gravity

get_viability(days_since_manufacture)

Yeast viability drops 21% each month or 0.7% per day from the date of manufacture. Assume linear change.

get_yeast_pitch_rate(original_gravity=1.05, final_volume=5.0, target_pitch_rate=1.42, yeast_type=u'liquid', cells_per_pack=100, num_packs=1, days_since_manufacture=30)

Determine yeast pitch rate

original_gravity - specific gravity of original beer final_volume - volume of the batch post fermentation target_pitch_rate - million cells / (ml * degP) yeast_type - liquid, dry cells_per_pack - Billions of cells num_packs - how many in units days_since_manufacture - the older the yeast the less viable units - imperial, metric

Yeast Viability: lose 20% viability / month or 0.66% / day

Imperial: B / Gal / GU Metric: M / ml / Plato

Sources: - http://beersmith.com/blog/2011/01/10/yeast-starters-for-home-brewing-beer-part-2/

set_units(units)
class brew.utilities.yeast.KaiserYeastModel(method=u'stir plate', units=u'imperial')

Kaiser Yeast Model

Only works for Stir Plage Growth

Sources:

METHOD_TO_GROWTH_ADJ = {u'stir plate': 0.0}
get_growth_rate(initial_cells)

initial_cells - Billion / gram extract (B/g)

get_inoculation_rate(growth_rate)
get_resulting_pitch_rate(starter_cell_count, original_gravity=1.036, final_volume=5.0)
get_starter_volume(available_cells, starter_volume=0.5283443537159779, original_gravity=1.036)

Calculate the number of cells given a stater volume and gravity

get_viability(days_since_manufacture)

Yeast viability drops 21% each month or 0.7% per day from the date of manufacture. Assume linear change.

get_yeast_pitch_rate(original_gravity=1.05, final_volume=5.0, target_pitch_rate=1.42, yeast_type=u'liquid', cells_per_pack=100, num_packs=1, days_since_manufacture=30)

Determine yeast pitch rate

original_gravity - specific gravity of original beer final_volume - volume of the batch post fermentation target_pitch_rate - million cells / (ml * degP) yeast_type - liquid, dry cells_per_pack - Billions of cells num_packs - how many in units days_since_manufacture - the older the yeast the less viable units - imperial, metric

Yeast Viability: lose 20% viability / month or 0.66% / day

Imperial: B / Gal / GU Metric: M / ml / Plato

Sources: - http://beersmith.com/blog/2011/01/10/yeast-starters-for-home-brewing-beer-part-2/

set_units(units)
class brew.utilities.yeast.WhiteYeastModel(method=u'no agitation', units=u'imperial')

Sources:

INOCULATION_CONST = [-0.999499, 12.547938, -0.459486]
METHOD_TO_GROWTH_ADJ = {u'shaking': 0.5, u'stir plate': 1.0, u'no agitation': 0.0}
get_growth_rate(inoculation_rate)

initial_cells - Billion / gram extract (B/g)

G = (12.54793776 * x^-0.4594858324) - 0.9994994906

get_inoculation_rate(growth_rate)
get_resulting_pitch_rate(starter_cell_count, original_gravity=1.036, final_volume=5.0)
get_starter_volume(available_cells, starter_volume=0.5283443537159779, original_gravity=1.036)

Calculate the number of cells given a stater volume and gravity

get_viability(days_since_manufacture)

Yeast viability drops 21% each month or 0.7% per day from the date of manufacture. Assume linear change.

get_yeast_pitch_rate(original_gravity=1.05, final_volume=5.0, target_pitch_rate=1.42, yeast_type=u'liquid', cells_per_pack=100, num_packs=1, days_since_manufacture=30)

Determine yeast pitch rate

original_gravity - specific gravity of original beer final_volume - volume of the batch post fermentation target_pitch_rate - million cells / (ml * degP) yeast_type - liquid, dry cells_per_pack - Billions of cells num_packs - how many in units days_since_manufacture - the older the yeast the less viable units - imperial, metric

Yeast Viability: lose 20% viability / month or 0.66% / day

Imperial: B / Gal / GU Metric: M / ml / Plato

Sources: - http://beersmith.com/blog/2011/01/10/yeast-starters-for-home-brewing-beer-part-2/

set_units(units)
yeast.pitch_rate_conversion(pitch_rate, units=u'imperial')

Pitch Rate Conversion

Input should be given in: Imperial: B / (Gal * GU) SI: B / (L * P)

Note: 1 M / (ml * P) == 1B / (L * P)

Glossary

Alpha Acid Units (AAU)
Defined as \(\text{AAU} = \text{weight of hops} \times \text{alpha acids}\).
Apparent Extract (AE)
The measured hydrometer reading for the finished beer.
BJCP
Beer Judge Certification Program. Publishes style guidelines for beer.
Boil Gravity
The specific gravity of the wort before it has been boiled and cooled. For a recipe where the boil volume is greater than the fermenting volume this can mean a gravity that is much lower than the Original Gravity. If the boil volume is less than the fermenting volume (ie water is ‘topped up’) then the gravity can be much higher than the OG.
Boil Volume
The volume of the wort during the boil.
Brew House Yield (BHY)

Brew house yield is a measurement that tells the efficiency of the brewing. The actual degrees Plato from the brew and the actual gallons collected out of the kettle are needed to calculate the BHY.

\(\text{BHY} = \frac{P_{actual} \times \text{vol}_{actual} \times \text{BHY}_{target}}{P_{target} \times \text{vol}_{target}}\)

Cereal
A type of whole grain used for brewing.
DME
Dry Malt Extract
Final Gravity
The specific gravity of the beer after it has been fermented. The final gravity measures the density of a sugar, alcohol, and water mixture which is different than the original gravity which only measures sugar and water. This is why refractometers must be adjusted when used to measure FG.
Final Volume
The volume of the wort at the finish of the boil.
Gravity Units (GU)

The gravity units of a recipe is defined as the total points of the recipe (as measured in PPG or HWE depending on units) divided by the volume of the wort.

\(\text{GU} = \text{PPG} \div \text{Wort Volume}\)

Hot Water Extract

The international unit for the total soluble extract of a malt, based on specific gravity. HWE is measured as liter*degrees per kilogram, and is equivalent to points/pound/gallon (PPG) when you apply metric conversion factors for volume and weight. The combined conversion factor is:

\(\text{HWE} = 8.3454 \times \text{PPG}\)

International Bitterness Units (IBUs)

IBUs or International Bittering Units measures a bitterness unit for hops. IBUs are the measurement in parts per million (ppm) of iso-alpha acids in the beer. For example, an IPA with 75 IBUs has 75 milligrams of isomerized alpha acids per liter. The equation used to calculate the weight of hops for the boil is as follows.

\(\text{Ounces hops} = \frac{\text{IBU Target} \times \text{galbeer} \times \text{IBU%}}{\text{%a-acid} \times \text{%Utilization} \times 7489}\)

The IBU target equals the total bitterness for the beer. (e.g. an IPA may have an IBU target of 75 IBUs) The percent IBU is equal to the percent of IBUs from each hop addition. You may wish for your first hop addition to contribute 95% of the total IBUs. This would make your IBU% 95%. The %a-acid is the amount of alpha acid in the hops and can be found on the hop packaging. The % Utilization is a measurement of the percentage of alpha acid units that will isomerize in the boil. The following chart outlines the typical utilizations and hop boil times.

Boil Time Utilization
60 min 30%
30 min 15%
5 min 2.5%

The 7489 is a conversion factor and used to cancel the units in the equation, converting oz/gallon to mg/l. For the hops equation, the units for the % must be expressed in decimal form. (e.g. 10%= .10)

Source:

LME
Liquid Malt Extract
Malt Color Units (MCU)

The color of malt as a function of weight, beer color, and wort volume.

\(\text{MCU} = \frac{\text{grain weight} \times \text{beer color in SRM}}{\text{wort volume}}\)

Mash Water Volume

To calculate the mash water volume you will need to know your liquor to grist ratio. The term liquor refers to the mash water and grist refers to the milled malt. We need to calculate the appropriate amount of water to allow for enzyme action and starch conversion take place.

\(\text{gallons H2O} = \frac{\text{Lbs malt} \times \text{L:G} \times \text{1 gallon H2O}}{\text{8.32 pounds water}}\)

Original Gravity
The specific gravity of the wort after it has been cooled and put into the fermenter.
Original Volume
Start Volume
The volume of the wort at the beginning of the process.
Plato
Degrees Plato
Degrees Plato is the weight of the extract in a 100gram solution at 64 degrees Fahrenheit.
Real Extract
The real extract content of the finished beer.
Specific Gravity
The ratio of the density of the wort against the density of water.
Standard Reference Method (SRM)
SRM is the standard unit of measure of the color of beer
Strike Water

As you know when you are mashing, your strike water has to be warmer than the target mash temperature because the cool malt will cool the temperature of the water. To correctly calculate the temperature of the strike water, use the following formula.

\(\text{Strike Temp} = \frac{0.4 \times \big(\text{T}_{mash} - \text{T}_{malt}\big)}{L:G} + \text{T}_{mash}\)

Weight of Extract

The weight of extract is the amount of malt extract present in the wort.

\(\text{Lbs extract} = \text{density of water} \times \text{gal of wort} \times \text{SG} \times \frac{P}{100}\)

The weight of one gallon of water in the above formula is 8.32 lbs/gal

To find the weight of a gallon of wort, multiply the specific gravity of the wort by the density of water.

Plato is a percentage of sugars by weight. So 10 Plato means solution is 10% sugars. In this equation we convert the degrees plato to a decimal number between 0.0 and 1.0 by dividing it by 100. This is multiplied by the weight of a gallon of wort.

Working Yield

The product of the Hot Water Extract multiplied by the Brew House Yield. This product will provide the percent of extract collected from the malt.

\(WY = \text{HWE as-is} \times \text{BHY}\)

Wort Color

The color of the wort

\(\text{Color of Wort} = \text{S} \times \text{% extract} \times \text{L of malt} \times \frac{\text{P wort}}{\text{8P reference}}\)

Source:

Indices and tables