Add a bunch of properties for beer calculation.

This commit is contained in:
Chris Giacofei 2024-06-18 15:15:22 -04:00
parent 8a3c80b517
commit d6aa8e8d6a

View File

@ -82,6 +82,97 @@ class BatchRecipe(CustomModel):
efficiency = models.DecimalField(max_digits=6, decimal_places=2, default=75)
batch_size = models.DecimalField(max_digits=6, decimal_places=2, default=11)
@property
def fermentables(self):
return [x for x in list(self.recipefermentable_set.all())]
@property
def hops(self):
return [x for x in list(self.recipehop_set.all())]
@property
def final_volume(self):
return float(self.batch_size) + self.hop_water_loss + self.net_kettle_deadspace + self.kettle_hose_loss
@property
def ferm_yield(self):
ferm_yield = 0
for f in self.fermentables:
if f.fermentable.fermentable_type == 3: # Is sugar
ferm_yield += f.quantity * (f.fermentable.potential - 1) * 1000
else:
ferm_yield += f.quantity * (self.efficiency / 100) * (f.fermentable.potential - 1) * 1000
return float(ferm_yield)
@property
def mash_yield(self):
mash_yield = 0
for f in self.fermentables:
if f.fermentable.fermentable_type != 3: # Is not sugar
mash_yield += f.quantity * (self.efficiency / 100) * (f.fermentable.potential - 1) * 1000
return float(mash_yield)
@property
def original_sg(self):
return round(1 + self.ferm_yield / self.final_volume / 1000, 3)
@property
def pre_boil_sg(self):
return self.ferm_yield / (self.final_volume + self.boil_off_gph)
@property
def hop_water_loss(self):
hop_absorption = .025 # gallons per ounce
return sum([float(x.quantity) * hop_absorption for x in self.hops])
@property
def net_kettle_deadspace(self):
# If hops in kettle deadspace
result = self.kettle_dead_space - self.hop_water_loss
return float(max(0, result)) # No deadspace if its all filled with hop trub)
# Else hops in bag or removed
return 0
@property
def kettle_hose_loss(self):
return .25 # TODO
@property
def kettle_dead_space(self):
return .25 # TODO
@property
def boil_off_gph(self):
return .8 # TODO
@property
def ibu_tinseth(self):
return sum([x.ibu_tinseth for x in self.hops])
@property
def srm(self):
color_total = sum([x.srm for x in self.fermentables])
return 1.4922*(color_total**0.6859)
@property
def srm_hex(self):
SRM_HEX = {
1: 'F3F993',2: 'F5F75C',3: 'F6F513',4: 'EAE615',5: 'E0D01B',
6: 'D5BC26',7: 'CDAA37',8: 'C1963C',9: 'BE8C3A',10: 'BE823A',
11: 'C17A37',12: 'BF7138',13: 'BC6733',14: 'B26033',15: 'A85839',
16: '985336',17: '8D4C32',18: '7C452D',19: '6B3A1E',20: '5D341A',
21: '4E2A0C',22: '4A2727',23: '361F1B',24: '261716',25: '231716',
26: '19100F',27: '16100F',28: '120D0C',29: '100B0A',30: '050B0A'
}
return '#{}'.format(SRM_HEX[int(self.srm)])
def __str__(self):
return self.name
@ -97,6 +188,7 @@ class Fermentable(CustomIngredient):
types = {
1: 'Grain',
2: 'Adjunct',
3: 'Sugar'
}
grain_category = models.IntegerField(choices=categories, default=1)
@ -119,6 +211,10 @@ class RecipeFermentable(CustomModel):
fermentable = models.ForeignKey(Fermentable, on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=6, decimal_places=4)
@property
def srm(self):
return round(float(self.fermentable.lovibond) * float(self.quantity) / self.recipe.final_volume, 1)
class Hop(CustomIngredient):
uses = {
1: 'Bittering',
@ -155,6 +251,25 @@ class RecipeHop(CustomModel):
time = models.IntegerField(default=60, validators=[MinValueValidator(0)])
use = models.IntegerField(choices=uses, default=1)
@property
def ibu_tinseth(self):
type_bonus = {
1: 1.1, # Pellet
2: 1.0, # Leaf
3: 1.1, # Cryo
4: 1.4, # CO2 Extract
}
ibu = 0
average_wort_sg = (self.recipe.pre_boil_sg/1000 + (self.recipe.original_sg-1)) / 2
if self.use == 1:
conc = ((float(self.hop.alpha) / 100) * float(self.quantity)) * 7490 / self.recipe.final_volume
util = (type_bonus[self.hop.hop_type] * 1.65 * (0.000125**average_wort_sg)) * ((1-2.71828182845904**(-0.04 * self.time))/4.15)
ibu = conc * util
return float(ibu)
class Misc(CustomIngredient):
uses = {
1: 'Mash',