Skip to content

Commit

Permalink
Complete hop addition calculator. Create tests in SI units. Change __…
Browse files Browse the repository at this point in the history
…eq__ for Grain and Hop additions to look at the overall difference between weights and make them equal if below a half percent tolerance
  • Loading branch information
chrisgilmerproj committed Oct 5, 2016
1 parent 38bb2d4 commit 4b23fb9
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 10 deletions.
3 changes: 3 additions & 0 deletions brew/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,6 @@
DENSITY_ETHANOL = 0.78945 # g/ml @ 20C
#: Alcohol by Volume Constant
ABV_CONST = RATIO_C2H6O_TO_CO2 / ALCOHOL_SPECIFIC_GRAVITY * 100.0

#: Weight Tolerance, considered equal within this range
WEIGHT_TOLERANCE = 0.005
3 changes: 2 additions & 1 deletion brew/grains.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .constants import POUND_PER_KG
from .constants import SI_TYPES
from .constants import SI_UNITS
from .constants import WEIGHT_TOLERANCE
from .utilities.malt import dry_to_liquid_malt_weight
from .utilities.malt import dry_malt_to_grain_weight
from .utilities.malt import grain_to_dry_malt_weight
Expand Down Expand Up @@ -187,7 +188,7 @@ def __repr__(self):
def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
if (round(self.weight, 2) == round(other.weight, 2)) and \
if (abs(1.0 - self.weight / other.weight) < WEIGHT_TOLERANCE) and \
(self.grain_type == other.grain_type) and \
(self.units == other.units) and \
(self.grain == other.grain):
Expand Down
3 changes: 2 additions & 1 deletion brew/hops.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .constants import OZ_PER_MG
from .constants import SI_TYPES
from .constants import SI_UNITS
from .constants import WEIGHT_TOLERANCE
from .utilities.hops import HopsUtilizationGlennTinseth
from .validators import validate_hop_type
from .validators import validate_optional_fields
Expand Down Expand Up @@ -166,7 +167,7 @@ def __eq__(self, other):
return False
if (self.hop == other.hop) and \
(self.boil_time == other.boil_time) and \
(round(self.weight, 2) == round(other.weight, 2)) and \
(abs(1.0 - self.weight / other.weight) < WEIGHT_TOLERANCE) and \
(self.hop_type == other.hop_type) and \
(self.units == other.units):
return True
Expand Down
21 changes: 19 additions & 2 deletions brew/recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,8 @@ def get_grain_additions(self, percent_list):
grain_additions.append(grain_add)
return grain_additions

def get_hop_additions(self, boil_time_list,
def get_hop_additions(self, percent_list, boil_time_list,
hop_type=HOP_TYPE_PELLET,
utilization_cls=HopsUtilizationGlennTinseth):
"""
Calculate HopAdditions from list of boil times
Expand All @@ -859,8 +860,18 @@ def get_hop_additions(self, boil_time_list,
:param HopsUtilization utilization_cls: The utilization class used for calculation
:return: A list of Hop Additions
:rtype: list(HopAddition)
:raises Exception: If sum of percentages does not equal 1.0
:raises Exception: If length of percent_list does not match length of self.grain_list
:raises Exception: If length of boil_time_list does not match length of self.hop_list
""" # nopep8
for percent in percent_list:
validate_percentage(percent)

if sum(percent_list) != 1.0:
raise Exception("Percentages must sum to 1.0")

if len(percent_list) != len(self.grain_list):
raise Exception("The length of percent_list must equal length of self.grain_list") # nopep8

if len(boil_time_list) != len(self.hop_list):
raise Exception("The length of boil_time_list must equal length of self.hop_list") # nopep8
Expand All @@ -871,16 +882,22 @@ def get_hop_additions(self, boil_time_list,

hop_additions = []
for index, hop in enumerate(self.hop_list):
percent = percent_list[index]
boil_time = boil_time_list[index]

# Calculate utilization from boil gravity
bg = gu_to_sg(sg_to_gu(self.original_gravity) * self.final_volume / self.start_volume) # nopep8
utilization = utilization_cls.get_percent_utilization(bg, boil_time) # nopep8
if hop_type == HOP_TYPE_PELLET:
utilization *= HOP_UTILIZATION_SCALE_PELLET

num = (self.target_ibu * self.final_volume)
num = (self.target_ibu * percent * self.final_volume)
den = (utilization * hop.percent_alpha_acids * hops_constant)
weight = num / den
hop_add = HopAddition(hop,
weight=weight,
boil_time=boil_time,
hop_type=hop_type,
utilization_cls=utilization_cls,
units=self.units)
hop_additions.append(hop_add)
Expand Down
26 changes: 20 additions & 6 deletions tests/test_recipes_imperial.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,23 +336,37 @@ def test_get_grain_additions(self):
expected = grain_additions
self.assertEquals(out, expected)

def test_get_grain_additions_raises_sum_invalid(self):
def test_get_grain_additions_raises_percent_sum_invalid(self):
percent_list = [0.90, 0.05]
with self.assertRaises(Exception):
self.builder.get_grain_additions(percent_list)

def test_get_grain_additions_raises_length_mismatch(self):
def test_get_grain_additions_raises_percent_length_mismatch(self):
percent_list = [0.90, 0.05, 0.05]
with self.assertRaises(Exception):
self.builder.get_grain_additions(percent_list)

def test_get_hop_additions(self):
percent_list = [0.8827, 0.1173]
boil_time_list = [60.0, 5.0]
out = self.builder.get_hop_additions(boil_time_list)
out = self.builder.get_hop_additions(percent_list, boil_time_list)
expected = hop_additions
# self.assertEquals(out, expected)
self.assertEquals(out, expected)

def test_get_hop_additions_raises_percent_sum_invalid(self):
percent_list = [0.8827, 0.2173]
boil_time_list = [60.0, 5.0]
with self.assertRaises(Exception):
self.builder.get_hop_additions(percent_list, boil_time_list)

def test_get_hop_additions_raises_percent_length_mismatch(self):
percent_list = [0.8827, 0.0173, 0.10]
boil_time_list = [60.0, 5.0]
with self.assertRaises(Exception):
self.builder.get_hop_additions(percent_list, boil_time_list)

def test_get_hop_additions_raises_length_mismatch(self):
def test_get_hop_additions_raises_boil_time_length_mismatch(self):
percent_list = [0.8827, 0.1173]
boil_time_list = [60.0, 5.0, 5.0]
with self.assertRaises(Exception):
self.builder.get_hop_additions(boil_time_list)
self.builder.get_hop_additions(percent_list, boil_time_list)
25 changes: 25 additions & 0 deletions tests/test_recipes_si.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,28 @@ def test_get_grain_additions_raises_length_mismatch(self):
percent_list = [0.90, 0.05, 0.05]
with self.assertRaises(Exception):
self.builder.get_grain_additions(percent_list)

def test_get_hop_additions(self):
percent_list = [0.8827, 0.1173]
boil_time_list = [60.0, 5.0]
out = self.builder.get_hop_additions(percent_list, boil_time_list)
expected = [ha.change_units() for ha in hop_additions]
self.assertEquals(out, expected)

def test_get_hop_additions_raises_percent_sum_invalid(self):
percent_list = [0.8827, 0.2173]
boil_time_list = [60.0, 5.0]
with self.assertRaises(Exception):
self.builder.get_hop_additions(percent_list, boil_time_list)

def test_get_hop_additions_raises_percent_length_mismatch(self):
percent_list = [0.8827, 0.0173, 0.10]
boil_time_list = [60.0, 5.0]
with self.assertRaises(Exception):
self.builder.get_hop_additions(percent_list, boil_time_list)

def test_get_hop_additions_raises_boil_time_length_mismatch(self):
percent_list = [0.8827, 0.1173]
boil_time_list = [60.0, 5.0, 5.0]
with self.assertRaises(Exception):
self.builder.get_hop_additions(percent_list, boil_time_list)

0 comments on commit 4b23fb9

Please sign in to comment.