358 |
358 |
359 double factor = og * 3157 * pow(10, -5) + 9.716 * pow(10, -2); |
359 double factor = og * 3157 * pow(10, -5) + 9.716 * pow(10, -2); |
360 return round((og * 1000 - fg * 1000) * factor * 100) / 100; |
360 return round((og * 1000 - fg * 1000) * factor * 100) / 100; |
361 } |
361 } |
362 |
362 |
|
363 |
|
364 double Utils::brewery_hPa() |
|
365 { |
|
366 return Seapressure * exp( - MolMassAir * Gravacc * my_height / (Gasconst * (20 + Kelvin))); |
|
367 } |
|
368 |
|
369 |
|
370 double Utils::boilPoint() |
|
371 { |
|
372 double P2 = brewery_hPa(); |
|
373 |
|
374 return (1 / (1/(100 + Kelvin) - Gasconst * log(P2 / Seapressure) / EoVwater)) - Kelvin; |
|
375 } |
|
376 |
|
377 |
363 /* |
378 /* |
|
379 * Formula is from the 'Mash Made Easy' spreadsheet. |
|
380 * https://mashmadeeasy.yolasite.com/ |
|
381 * https://www.homebrewtalk.com/threads/a-rather-simplified-whirlpool-hop-ibu-computation-method.701093/ |
|
382 */ |
|
383 double Utils::IBU_reduction(double Tc) |
|
384 { |
|
385 return (2.39 * pow(10, 11) * pow(2.71828182846, - (9773 / (Tc + Kelvin))) ) * 1/1.009231744; |
|
386 } |
|
387 |
|
388 |
364 double Utils::boilIBU(int Form, double SG, double Volume, double Amount, double Time, double Alpha, int Method) |
389 double Utils::boilIBU(int Form, double SG, double Volume, double Amount, double Time, double Alpha, int Method) |
365 { |
390 { |
|
391 double ibu = 0.0, sgfactor, boilfactor; |
|
392 |
|
393 double alpha = Alpha / 100.0; |
|
394 double mass = Amount * 1000.0; |
|
395 |
366 if (Method == 0) { // Tinseth |
396 if (Method == 0) { // Tinseth |
367 */ /* http://realbeer.com/hops/research.html */ |
397 /* http://realbeer.com/hops/research.html */ |
368 /* AddedAlphaAcids = (alpha * mass * 1000) / liters; |
398 double AddedAlphaAcids = (alpha * mass * 1000) / Volume; |
369 Bigness_factor = 1.65 * pow(0.000125, gravity - 1); |
399 double Bigness_factor = 1.65 * pow(0.000125, SG - 1); |
370 BoilTime_factor = ((1 - exp(-0.04 * time)) / 4.15); |
400 double BoilTime_factor = ((1 - exp(-0.04 * Time)) / 4.15); |
371 utiisation = Bigness_factor * BoilTime_factor; |
401 ibu = Bigness_factor * BoilTime_factor * AddedAlphaAcids; |
372 ibu = round((utiisation * AddedAlphaAcids * fmoment * pfactor + whirlibus) * 100) / 100; |
|
373 } |
402 } |
374 if (Method == 2) { // Daniels |
403 if (Method == 2) { // Daniels |
375 if (Form == 2) // Leaf |
404 if (Form == 2) // Leaf |
376 boilfactor = -(0.0041 * time * time) + (0.6162 * time) + 1.5779; |
405 boilfactor = -(0.0041 * Time * Time) + (0.6162 * Time) + 1.5779; |
377 else |
406 else |
378 boilfactor = -(0.0051 * time * time) + (0.7835 * time) + 1.9348; |
407 boilfactor = -(0.0051 * Time * Time) + (0.7835 * Time) + 1.9348; |
379 if (gravity < 1050) |
408 if (SG < 1.050) |
380 sgfactor = 0; |
409 sgfactor = 0; |
381 else |
410 else |
382 sgfactor = (gravity - 1050) / 200; |
411 sgfactor = ((SG * 1000) - 1050) / 200; |
383 ibu = round((fmoment * ((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor))) + whirlibus) * 100) / 100; |
412 ibu = (mass * (alpha * 100) * boilfactor * 0.1) / (Volume * (1 + sgfactor)); |
384 } |
413 } |
385 if (Method == 1) { // Rager |
414 if (Method == 1) { // Rager |
386 boilfactor = fmoment * 18.11 + 13.86 * tanh((time * 31.32) / 18.27); |
415 boilfactor = 18.11 + 13.86 * tanh((Time * 31.32) / 18.27); |
387 if (gravity < 1050) |
416 if (SG < 1.050) |
388 sgfactor = 0; |
417 sgfactor = 0; |
389 else |
418 else |
390 sgfactor = (gravity - 1050) / 200; |
419 sgfactor = ((SG * 1000) - 1050) / 200; |
391 ibu = round(((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor)) + whirlibus) * 100) / 100; |
420 ibu = (mass * (alpha * 100) * boilfactor * 0.1) / (Volume * (1 + sgfactor)); |
392 } |
421 } |
393 } |
422 |
394 */ |
423 //qDebug() << "boilIBU" << Form << SG << Volume << Amount << Time << Alpha << Method << "IBU:" << ibu; |
|
424 return ibu; |
|
425 } |
|
426 |
395 |
427 |
396 double Utils::toIBU(int Use, int Form, double SG, double Volume, double Amount, double Boiltime, double Alpha, |
428 double Utils::toIBU(int Use, int Form, double SG, double Volume, double Amount, double Boiltime, double Alpha, |
397 int Method, double Whirlpool9, double Whirlpool7, double Whirlpool6, double Fulltime) |
429 int Method, double Whirlpool9, double Whirlpool7, double Whirlpool6, double Fulltime) |
398 { |
430 { |
399 double fmoment = 1.0, pfactor = 1.0, ibu = 0, boilfactor; |
431 double ibu = 0.0, whirlibus = 0.0; |
400 double sgfactor, AddedAlphaAcids, Bigness_factor, BoilTime_factor, utiisation; |
|
401 |
|
402 double gravity = SG; |
|
403 double liters = Volume; |
|
404 double alpha = Alpha / 100.0; |
432 double alpha = Alpha / 100.0; |
405 double mass = Amount * 1000.0; |
433 double mass = Amount * 1000.0; |
406 double time = Boiltime; |
|
407 |
|
408 if ((Use == HOP_USEAT_AROMA) || (Use == HOP_USEAT_WHIRLPOOL) || (Use == HOP_USEAT_DRY_HOP) || (Use == HOP_USEAT_BOTTLING)) { |
|
409 fmoment = 0.0; |
|
410 } else if (Use == HOP_USEAT_MASH) { |
|
411 fmoment += my_factor_mashhop / 100.0; // Brouwhulp |
|
412 time = Fulltime; // Take the full boiltime |
|
413 } else if (Use == HOP_USEAT_FWH) { |
|
414 fmoment += my_factor_fwh / 100.0; // Brouwhulp, Louis, Ozzie |
|
415 time = Fulltime; |
|
416 } |
|
417 |
|
418 if (Form == HOP_FORMS_PELLET) { |
|
419 pfactor += my_factor_pellet / 100.0; |
|
420 } else if (Form == HOP_FORMS_PLUG) { |
|
421 pfactor += my_factor_plug / 100.0; |
|
422 } else if (Form == HOP_FORMS_LEAF_WET) { |
|
423 pfactor += my_factor_wethop / 100.0; // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py |
|
424 } else if (Form == HOP_FORMS_CRYO) { |
|
425 pfactor += my_factor_cryohop / 100.0; |
|
426 } else if (Form == HOP_FORMS_EXTRACT) { |
|
427 // Nothing for now. |
|
428 } |
|
429 |
434 |
430 // Ideas from Zymurgy March-April 2018. These are not exact formulas! |
435 // Ideas from Zymurgy March-April 2018. These are not exact formulas! |
431 double whirlibus = 0.0; |
|
432 if (Use == HOP_USEAT_AROMA) { |
436 if (Use == HOP_USEAT_AROMA) { |
433 if (Whirlpool9) { // Flameout hops are 2 minutes in this range. |
437 if (Whirlpool9) { // Flameout hops are 2 minutes in this range. |
434 whirlibus += (alpha * mass * 20) / liters * (2.0 / 50.0); |
438 whirlibus += (alpha * mass * 20) / Volume * (2.0 / 50.0); |
435 } |
439 } |
436 if (Whirlpool7) { // Flameout hops are 4 minutes in this range. |
440 if (Whirlpool7) { // Flameout hops are 4 minutes in this range. |
437 whirlibus += (alpha * mass * 6) / liters * (4.0 / 50.0); |
441 whirlibus += (alpha * mass * 6) / Volume * (4.0 / 50.0); |
438 } |
442 } |
|
443 // Experiment. |
|
444 // double wibu = boilIBU(Form, SG, Volume, Amount, 6, Alpha, Method); // IBU's for 6 minutes full |
|
445 // double fibu = wibu * 0.067 * IBU_reduction(94); // Add timed segments |
|
446 // fibu += wibu * 0.1 * IBU_reduction(84); |
|
447 // fibu += wibu * 0.167 * IBU_reduction(74); |
|
448 // fibu += wibu * 0.250 * IBU_reduction(64); |
|
449 // fibu += wibu * 0.417 * IBU_reduction(54); |
|
450 //qDebug() << " 94" << wibu * 0.067 * IBU_reduction(94); |
|
451 //qDebug() << " 84" << wibu * 0.1 * IBU_reduction(84); |
|
452 //qDebug() << " 74" << wibu * 0.167 * IBU_reduction(74); |
|
453 //qDebug() << " 64" << wibu * 0.250 * IBU_reduction(64); |
|
454 //qDebug() << " 54" << wibu * 0.417 * IBU_reduction(54); |
|
455 // qDebug() << "flamout" << wibu << fibu; |
439 } |
456 } |
440 if (Use == HOP_USEAT_WHIRLPOOL) { // Flameout or any whirlpool |
457 if (Use == HOP_USEAT_WHIRLPOOL) { // Flameout or any whirlpool |
441 if (Whirlpool9) { |
458 if (Whirlpool9) { |
442 // 20 mg/l/50 min |
459 // 20 mg/l/50 min |
443 whirlibus += (alpha * mass * 20) / liters * (Whirlpool9 / 50.0); |
460 whirlibus += (alpha * mass * 20) / Volume * (Whirlpool9 / 50.0); |
444 //qDebug() << "Whirlpool9" << alpha * mass * 20 << " liter:" << liters << " time:" << Whirlpool9 << " ibu" << (alpha * mass * 20) / liters * (Whirlpool9 / 50.0); |
461 //qDebug() << "Whirlpool9" << alpha * mass * 20 << " liter:" << liters << " time:" << Whirlpool9 << " ibu" << (alpha * mass * 20) / liters * (Whirlpool9 / 50.0); |
445 } |
462 } |
446 if (Whirlpool7) { |
463 if (Whirlpool7) { |
447 // 6 mg/l/50 min |
464 // 6 mg/l/50 min |
448 whirlibus += (alpha * mass * 6) / liters * (Whirlpool7 / 50.0); |
465 whirlibus += (alpha * mass * 6) / Volume * (Whirlpool7 / 50.0); |
449 //qDebug() << "Whirlpool7" << alpha * mass * 6 << " liter:" << liters << " time:" << Whirlpool7 << " ibu" << (alpha * mass * 6) / liters * (Whirlpool7 / 50.0); |
466 //qDebug() << "Whirlpool7" << alpha * mass * 6 << " liter:" << liters << " time:" << Whirlpool7 << " ibu" << (alpha * mass * 6) / liters * (Whirlpool7 / 50.0); |
450 } |
467 } |
451 if (Whirlpool6) { |
468 if (Whirlpool6) { |
452 // 2 mg/l/50 min |
469 // 2 mg/l/50 min |
453 whirlibus += (alpha * mass * 2) / liters * (Whirlpool6 / 50.0); |
470 whirlibus += (alpha * mass * 2) / Volume * (Whirlpool6 / 50.0); |
454 } |
471 } |
455 } |
472 // double wibu = boilIBU(Form, SG, Volume, Amount, Boiltime, Alpha, Method); |
456 |
473 // qDebug() << "whirpool" << wibu << wibu * IBU_reduction(74); |
457 if (Method == 0) { // Tinseth |
474 } |
458 /* http://realbeer.com/hops/research.html */ |
475 |
459 AddedAlphaAcids = (alpha * mass * 1000) / liters; |
476 /* |
460 Bigness_factor = 1.65 * pow(0.000125, gravity - 1); |
477 * IBU's from hops during Mash, FWH and boil. |
461 BoilTime_factor = ((1 - exp(-0.04 * time)) / 4.15); |
478 */ |
462 utiisation = Bigness_factor * BoilTime_factor; |
479 if ((Use == HOP_USEAT_MASH) || (Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { |
463 ibu = round((utiisation * AddedAlphaAcids * fmoment * pfactor + whirlibus) * 100) / 100; |
480 ibu = boilIBU(Form, SG, Volume, Amount, Fulltime, Alpha, Method); |
464 } |
481 /* |
465 if (Method == 2) { // Daniels |
482 * Corrections for Mash and FWH |
466 if (Form == 2) // Leaf |
483 */ |
467 boilfactor = -(0.0041 * time * time) + (0.6162 * time) + 1.5779; |
484 if (Use == HOP_USEAT_MASH) { |
468 else |
485 ibu *= (1 + my_factor_mashhop / 100.0); |
469 boilfactor = -(0.0051 * time * time) + (0.7835 * time) + 1.9348; |
486 } |
470 if (gravity < 1050) |
487 if (Use == HOP_USEAT_FWH) { |
471 sgfactor = 0; |
488 ibu *= (1 + my_factor_fwh / 100.0); |
472 else |
489 } |
473 sgfactor = (gravity - 1050) / 200; |
490 |
474 ibu = round((fmoment * ((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor))) + whirlibus) * 100) / 100; |
491 /* |
475 } |
492 * Correction for hop forms |
476 if (Method == 1) { // Rager |
493 */ |
477 boilfactor = fmoment * 18.11 + 13.86 * tanh((time * 31.32) / 18.27); |
494 if (Form == HOP_FORMS_PELLET) { |
478 if (gravity < 1050) |
495 ibu *= (1 + my_factor_pellet / 100.0); |
479 sgfactor = 0; |
496 } else if (Form == HOP_FORMS_PLUG) { |
480 else |
497 ibu *= (1 + my_factor_plug / 100.0); |
481 sgfactor = (gravity - 1050) / 200; |
498 } else if (Form == HOP_FORMS_LEAF_WET) { |
482 ibu = round(((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor)) + whirlibus) * 100) / 100; |
499 ibu *= (1 + my_factor_wethop / 100.0); // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py |
483 } |
500 } else if (Form == HOP_FORMS_CRYO) { |
484 |
501 ibu *= (1 + my_factor_cryohop / 100.0); |
485 return ibu; |
502 } else if (Form == HOP_FORMS_EXTRACT) { |
|
503 // Nothing for now. |
|
504 } |
|
505 // ibu *= IBU_reduction(boilPoint()); |
|
506 // qDebug() << "ibu" << ibu << IBU_reduction(boilPoint()); |
|
507 |
|
508 // } else if (Use == HOP_USEAT_AROMA) { |
|
509 /* |
|
510 * At flameout. The cooling method is important. |
|
511 * Emersion chiller, Counterflow chiller, Au bain marie or natural. |
|
512 * Assume the hop is removed for all methods except Emersion chilling. |
|
513 */ |
|
514 } else { |
|
515 // qDebug() << "whirlibus" << whirlibus << Use; |
|
516 } |
|
517 |
|
518 |
|
519 return round((ibu + whirlibus) * 100.0) / 100.0; |
486 } |
520 } |
487 |
521 |
488 |
522 |
489 double Utils::hopFlavourContribution(double bt, double vol, int use, double amount) |
523 double Utils::hopFlavourContribution(double bt, double vol, int use, double amount) |
490 { |
524 { |