Skip to content

Commit

Permalink
Fix calculations for dry and cereal weight in recipes. Remove old mas…
Browse files Browse the repository at this point in the history
…h volume equation
  • Loading branch information
chrisgilmerproj committed Sep 26, 2017
1 parent 51d4e5c commit 35a6328
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 230 deletions.
9 changes: 9 additions & 0 deletions brew/constants.py
Expand Up @@ -117,6 +117,15 @@
#: Moisture correction factor
MOISTURE_CORRECTION = 0.0

# Commonly used PPG numbers exist for DME and LME but they should be considered
# as guidelines.
#: Common PPG for DME
PPG_DME = 44.0
#: Common PPG for LME
PPG_LME = 36.0
#: Common PPG for Cereal
PPG_CEREAL = 30.0

# Sucrose is considered 100% extractable in water, so the maximum PPG and
# Plato are listed here
#: Maximum Plato for 100% sugar dissolved in water
Expand Down
99 changes: 47 additions & 52 deletions brew/grains.py
Expand Up @@ -6,25 +6,20 @@
from .constants import GRAIN_TYPE_CEREAL
from .constants import GRAIN_TYPE_DME
from .constants import GRAIN_TYPE_LME
from .constants import GRAIN_TYPE_SPECIALTY
from .constants import IMPERIAL_TYPES
from .constants import IMPERIAL_UNITS
from .constants import KG_PER_POUND
from .constants import POUND_PER_KG
from .constants import PPG_CEREAL
from .constants import PPG_DME
from .constants import PPG_LME
from .constants import SI_TYPES
from .constants import SI_UNITS
from .constants import WEIGHT_TOLERANCE
from .exceptions import GrainException
from .utilities.malt import dry_malt_to_grain_weight
from .utilities.malt import dry_to_liquid_malt_weight
from .utilities.malt import grain_to_dry_malt_weight
from .utilities.malt import grain_to_liquid_malt_weight
from .utilities.malt import hwe_to_basis
from .utilities.malt import hwe_to_ppg
from .utilities.malt import liquid_malt_to_grain_weight
from .utilities.malt import liquid_to_dry_malt_weight
from .utilities.malt import ppg_to_hwe
from .utilities.malt import specialty_grain_to_liquid_malt_weight
from .validators import validate_optional_fields
from .validators import validate_percentage
from .validators import validate_required_fields
Expand Down Expand Up @@ -133,11 +128,16 @@ def get_working_yield(self, brew_house_yield):
return (hwe_to_basis(self.hwe) *
brew_house_yield)

def convert_to_lme(self):
return Grain(self.name, color=self.color, ppg=36.0)
def convert_to_cereal(self, ppg=None):
if not ppg:
raise GrainException(u'Must provide PPG to convert to cereal')
return Grain(self.name, color=self.color, ppg=ppg)

def convert_to_dme(self):
return Grain(self.name, color=self.color, ppg=44.0)
def convert_to_lme(self, ppg=PPG_LME):
return Grain(self.name, color=self.color, ppg=ppg)

def convert_to_dme(self, ppg=PPG_DME):
return Grain(self.name, color=self.color, ppg=ppg)


class GrainAddition(object):
Expand Down Expand Up @@ -226,21 +226,15 @@ def __eq__(self, other):
def __ne__(self, other):
return not self.__eq__(other)

def get_cereal_weight(self):
def get_cereal_weight(self, ppg=PPG_CEREAL):
"""
Get the weight of the addition in cereal weight
:param float ppg: The potential points per gallon
:return: Cereal weight
:rtype: float
"""
if self.grain_type == GRAIN_TYPE_CEREAL:
return self.weight
elif self.grain_type == GRAIN_TYPE_DME:
return self.weight * (self.grain.ppg / 44.0)
elif self.grain_type == GRAIN_TYPE_LME:
return self.weight * (self.grain.ppg / 36.0)
elif self.grain_type == GRAIN_TYPE_SPECIALTY:
return self.weight
return self.convert_to_cereal(ppg=ppg).weight

def get_lme_weight(self):
"""
Expand All @@ -249,14 +243,7 @@ def get_lme_weight(self):
:return: LME weight
:rtype: float
"""
if self.grain_type == GRAIN_TYPE_CEREAL:
return grain_to_liquid_malt_weight(self.weight)
elif self.grain_type == GRAIN_TYPE_DME:
return dry_to_liquid_malt_weight(self.weight)
elif self.grain_type == GRAIN_TYPE_LME:
return self.weight
elif self.grain_type == GRAIN_TYPE_SPECIALTY:
return specialty_grain_to_liquid_malt_weight(self.weight)
return self.convert_to_lme().weight

def get_dme_weight(self):
"""
Expand All @@ -265,15 +252,7 @@ def get_dme_weight(self):
:return: Dry weight
:rtype: float
"""
if self.grain_type == GRAIN_TYPE_CEREAL:
return grain_to_dry_malt_weight(self.weight)
elif self.grain_type == GRAIN_TYPE_DME:
return self.weight
elif self.grain_type == GRAIN_TYPE_LME:
return liquid_to_dry_malt_weight(self.weight)
elif self.grain_type == GRAIN_TYPE_SPECIALTY:
lme = specialty_grain_to_liquid_malt_weight(self.weight)
return liquid_to_dry_malt_weight(lme)
return self.convert_to_dme().weight

def get_weight_map(self):
"""
Expand All @@ -288,37 +267,49 @@ def get_weight_map(self):
u'dry_weight': round(self.get_dme_weight(), 2),
}

def convert_to_cereal(self, brew_house_yield=1.0):
def convert_to_cereal(self, ppg=None, brew_house_yield=1.0):
"""
Convert Grain Addition to GRAIN_TYPE_CEREAL
:param float ppg: The potential points per gallon
:param float brew_house_yield: The brew house yield as a percentage
:return: GrainAddition of type GRAIN_TYPE_CEREAL
:rtype: GrainAddition
"""
validate_percentage(brew_house_yield)
if self.grain_type == GRAIN_TYPE_CEREAL:
brew_house_yield = 1.0
return self
if not ppg:
raise GrainException('Must provide PPG to convert to cereal')

validate_percentage(brew_house_yield)
new_grain = self.grain.convert_to_cereal(ppg=ppg)
ppg_factor = self.grain.ppg / new_grain.ppg

# When converting away from cereal BHY works in reverse
weight = self.weight * ppg_factor / brew_house_yield
return GrainAddition(
self.grain,
weight=self.get_cereal_weight() / brew_house_yield,
new_grain,
weight=weight,
grain_type=GRAIN_TYPE_CEREAL,
units=self.units)

def convert_to_lme(self, brew_house_yield=1.0):
def convert_to_lme(self, ppg=PPG_LME, brew_house_yield=1.0):
"""
Convert Grain Addition to GRAIN_TYPE_LME
:param float ppg: The potential points per gallon
:param float brew_house_yield: The brew house yield as a percentage
:return: GrainAddition of type GRAIN_TYPE_LME
:rtype: GrainAddition
"""
if self.grain_type == GRAIN_TYPE_LME:
return self

# BHY applies to cereal grains
validate_percentage(brew_house_yield)
if self.grain_type in [GRAIN_TYPE_DME, GRAIN_TYPE_LME]:
if self.grain_type == GRAIN_TYPE_DME:
brew_house_yield = 1.0
new_grain = self.grain.convert_to_lme()
new_grain = self.grain.convert_to_lme(ppg=ppg)
ppg_factor = self.grain.ppg / new_grain.ppg
weight = self.weight * ppg_factor * brew_house_yield
return GrainAddition(
Expand All @@ -327,19 +318,23 @@ def convert_to_lme(self, brew_house_yield=1.0):
grain_type=GRAIN_TYPE_LME,
units=self.units)

def convert_to_dme(self, brew_house_yield=1.0):
def convert_to_dme(self, ppg=PPG_DME, brew_house_yield=1.0):
"""
Convert Grain Addition to GRAIN_TYPE_DME
:param float ppg: The potential points per gallon
:param float brew_house_yield: The brew house yield as a percentage
:return: GrainAddition of type GRAIN_TYPE_DME
:rtype: GrainAddition
"""
if self.grain_type == GRAIN_TYPE_DME:
return self

# BHY applies to cereal grains
validate_percentage(brew_house_yield)
if self.grain_type in [GRAIN_TYPE_DME, GRAIN_TYPE_LME]:
if self.grain_type == GRAIN_TYPE_LME:
brew_house_yield = 1.0
new_grain = self.grain.convert_to_dme()
new_grain = self.grain.convert_to_dme(ppg=ppg)
ppg_factor = self.grain.ppg / new_grain.ppg
weight = self.weight * ppg_factor * brew_house_yield
return GrainAddition(
Expand Down
25 changes: 4 additions & 21 deletions brew/recipes.py
Expand Up @@ -14,6 +14,7 @@
from .constants import IMPERIAL_TYPES
from .constants import IMPERIAL_UNITS
from .constants import LITER_PER_GAL
from .constants import PPG_CEREAL
from .constants import SI_TYPES
from .constants import SI_UNITS
from .constants import WATER_WEIGHT_IMPERIAL
Expand Down Expand Up @@ -340,8 +341,7 @@ def get_grain_add_dry_weight(self, grain_add):
the brew house yield will decrease the size of the DME
accordingly.
"""
return grain_add.convert_to_dme(
brew_house_yield=self.brew_house_yield).weight
return grain_add.get_dme_weight()

def get_total_dry_weight(self):
"""
Expand All @@ -355,7 +355,7 @@ def get_total_dry_weight(self):
weights.append(self.get_grain_add_dry_weight(grain_add))
return sum(weights)

def get_grain_add_cereal_weight(self, grain_add):
def get_grain_add_cereal_weight(self, grain_add, ppg=PPG_CEREAL):
"""
Get Grain Addition as Cereal
Expand All @@ -368,10 +368,7 @@ def get_grain_add_cereal_weight(self, grain_add):
the brew house yield will increase the size of the grain
accordingly.
"""
if grain_add.grain_type in [GRAIN_TYPE_DME, GRAIN_TYPE_LME]:
return grain_add.get_cereal_weight() / self.brew_house_yield # noqa
else:
return grain_add.get_cereal_weight()
return grain_add.get_cereal_weight(ppg=ppg)

def get_total_grain_weight(self):
"""
Expand Down Expand Up @@ -422,20 +419,6 @@ def get_bu_to_gu(self):
"""
return self.get_total_ibu() / self.get_boil_gravity_units()

def get_mash_water_volume(self, liquor_to_grist_ratio):
"""
Get the Mash Water Volume
:param float liquor_to_grist_ratio: The Liquor to Grist Ratio
:return: The mash water volume
:rtype: float
"""
water_weight = WATER_WEIGHT_IMPERIAL
if self.units == SI_UNITS:
water_weight = WATER_WEIGHT_SI
return (self.get_total_dry_weight() * liquor_to_grist_ratio /
water_weight)

@property
def abv(self):
return alcohol_by_volume_standard(self.og, self.fg)
Expand Down
6 changes: 4 additions & 2 deletions tests/fixtures.py
Expand Up @@ -11,15 +11,17 @@
BHY = 0.70

# Define Grains
ppg_pale = 37.0
pale = Grain(u'pale 2-row',
color=2.0,
ppg=37.0)
ppg=ppg_pale)
pale_lme = pale.convert_to_lme()
pale_dme = pale.convert_to_dme()

ppg_crystal = 35.0
crystal = Grain(u'crystal C20',
color=20.0,
ppg=35.0)
ppg=ppg_crystal)
grain_list = [pale, crystal]

pale_add = GrainAddition(pale,
Expand Down

0 comments on commit 35a6328

Please sign in to comment.