389 */ |
389 */ |
390 return 2.39 * pow(10, 11) * exp(-(9773 / (Tc + Kelvin))) * 1 / 1.009231743647; |
390 return 2.39 * pow(10, 11) * exp(-(9773 / (Tc + Kelvin))) * 1 / 1.009231743647; |
391 } |
391 } |
392 |
392 |
393 |
393 |
394 double Utils::TinsethIBU(int Form, double SG, double Volume, double Amount, double T1, double T2, double Alpha) |
394 double Utils::TinsethIBU(int Form, double SG, double Volume, double Amount, double T1, double T2, double Alpha, double Utilisation, double BU_factor) |
395 { |
395 { |
396 double alpha = Alpha / 100.0; |
396 double alpha = Alpha / 100.0; |
397 double mass = Amount * 1000.0; |
397 double mass = Amount * 1000.0; |
|
398 double ibu = 0.0; |
|
399 |
|
400 /* |
|
401 * Most suppliers of CO2 hop extract (not isomerized) use formulas |
|
402 * like this. For now, manual set a utilisation factor of 35%. |
|
403 */ |
|
404 if (Form == HOP_FORMS_CO2EXTRACT) { |
|
405 double utilisation = 0.35; // 35% |
|
406 double factor_sg = 1.0; |
|
407 double factor_bt = 1.0; |
|
408 double bt = T2 - T1; |
|
409 |
|
410 /* Table from Wildabouthops */ |
|
411 if (SG >= 1.150) |
|
412 factor_sg = 1.3; |
|
413 else if (SG >= 1.110) |
|
414 factor_sg = 1.2; |
|
415 else if (SG >= 1.080) |
|
416 factor_sg = 1.1; |
|
417 |
|
418 if (bt > 60) { |
|
419 factor_bt = ((bt - 60) / 300) + 1.0; |
|
420 } else { |
|
421 factor_bt = (bt / 60.0); |
|
422 } |
|
423 qDebug() << "factor_sg" << factor_sg << "factor_bt" << factor_bt; |
|
424 |
|
425 ibu = ((Utilisation / 100.0) * alpha * mass * 1000.0) / Volume; |
|
426 ibu = (ibu * factor_bt) / factor_sg; |
|
427 qDebug() << "TinsethIBU CO2" << Amount << Alpha << Volume << Utilisation << BU_factor << "SG" << SG << "T" << T1 << T2 << "ibu:" << ibu; |
|
428 return ibu; |
|
429 } |
|
430 |
|
431 if (Form == HOP_FORMS_ISOEXTRACT) { |
|
432 |
|
433 double dosageHl = ((100.0 / Utilisation) * (100.0 / Alpha) * 0.001) / BU_factor; // IBU per liter |
|
434 qDebug() << (100 / Utilisation) << (100 / Alpha) << dosageHl << dosageHl * Volume << mass / (dosageHl * Volume); |
|
435 ibu = mass / (dosageHl * Volume); |
|
436 qDebug() << "TinsethIBU ISO" << Amount << Alpha << Volume << Utilisation << BU_factor << "ibu:" << ibu; |
|
437 return ibu; |
|
438 } |
398 |
439 |
399 /* |
440 /* |
400 * Basic Tinseth formula. |
441 * Basic Tinseth formula. |
401 * http://realbeer.com/hops/research.html |
442 * http://realbeer.com/hops/research.html |
402 */ |
443 */ |
403 double AddedAlphaAcids = (alpha * mass * 1000) / Volume; |
444 double AddedAlphaAcids = (alpha * mass * 1000) / Volume; |
404 double Bigness_factor = 1.65 * pow(0.000125, SG - 1); |
445 double Bigness_factor = 1.65 * pow(0.000125, SG - 1); |
405 double BoilTime_factor1 = ((1 - exp(-0.04 * T1)) / 4.15); |
446 double BoilTime_factor1 = ((1 - exp(-0.04 * T1)) / 4.15); |
406 double BoilTime_factor2 = ((1 - exp(-0.04 * T2)) / 4.15); |
447 double BoilTime_factor2 = ((1 - exp(-0.04 * T2)) / 4.15); |
407 double ibu = Bigness_factor * (BoilTime_factor2 - BoilTime_factor1) * AddedAlphaAcids; |
448 ibu = Bigness_factor * (BoilTime_factor2 - BoilTime_factor1) * AddedAlphaAcids; |
|
449 qDebug() << "TinsethIBU nor" << SG << Amount << Alpha << Volume << Bigness_factor * (BoilTime_factor2 - BoilTime_factor1) << "ibu:" << ibu; |
408 |
450 |
409 /* |
451 /* |
410 * Correction for hop forms |
452 * Correction for hop forms |
411 */ |
453 */ |
412 if (Form == HOP_FORMS_PELLET) { |
454 if (Form == HOP_FORMS_PELLET) { |
415 ibu *= (1 + my_factor_plug / 100.0); |
457 ibu *= (1 + my_factor_plug / 100.0); |
416 } else if (Form == HOP_FORMS_LEAF_WET) { |
458 } else if (Form == HOP_FORMS_LEAF_WET) { |
417 ibu *= (1 + my_factor_wethop / 100.0); // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py |
459 ibu *= (1 + my_factor_wethop / 100.0); // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py |
418 } else if (Form == HOP_FORMS_CRYO) { |
460 } else if (Form == HOP_FORMS_CRYO) { |
419 ibu *= (1 + my_factor_cryohop / 100.0); |
461 ibu *= (1 + my_factor_cryohop / 100.0); |
420 } else if (Form == HOP_FORMS_EXTRACT) { |
462 } |
421 // Nothing for now. |
463 |
422 } |
464 qDebug() << "boilIBU" << Form << SG << Volume << Amount << BoilTime_factor2 << BoilTime_factor1 << Alpha << "IBU:" << ibu; |
423 |
|
424 //qDebug() << "boilIBU" << Form << SG << Volume << Amount << T1 << T2 << Alpha << "IBU:" << ibu; |
|
425 return ibu; |
465 return ibu; |
426 } |
466 } |
427 |
467 |
428 |
468 |
429 double Utils::toIBU(int Use, int Form, double SG, double Volume, double Amount, double Boiltime, double Alpha, |
469 double Utils::toIBU(int Use, int Form, double preSG, double postSG, double Volume, double Amount, double Boiltime, double Alpha, |
430 int Method, double Whirlpool9, double Whirlpool7, double Whirlpool6, double Fulltime, int Cooltype, double Coolparm1, double Coolparm2) |
470 int Method, double Whirlpool9, double Whirlpool7, double Whirlpool6, double Fulltime, |
|
471 int Cooltype, double Coolparm1, double Coolparm2, double Utilisation, double BU_factor) |
431 { |
472 { |
432 double ibu = 0.0; |
473 double ibu = 0.0; |
433 |
474 |
434 if (Use == HOP_USEAT_MASH) { |
475 if (Use == HOP_USEAT_MASH) { |
435 /* |
476 /* |
438 * Almost all these hops will be gone after removing the malt. From |
479 * Almost all these hops will be gone after removing the malt. From |
439 * pellets there may be some dust left, but that has minor effects. |
480 * pellets there may be some dust left, but that has minor effects. |
440 * |
481 * |
441 * http://scottjanish.com/the-locksmith-utilizing-bioengineered-yeast-and-high-bound-thiol-precersour-hops-and-phantasm-powder-to-thiol-drive-beer/ |
482 * http://scottjanish.com/the-locksmith-utilizing-bioengineered-yeast-and-high-bound-thiol-precersour-hops-and-phantasm-powder-to-thiol-drive-beer/ |
442 */ |
483 */ |
443 ibu = TinsethIBU(Form, SG, Volume, Amount, 0, 60, Alpha) * (1 + my_factor_mashhop / 100.0); |
484 ibu = TinsethIBU(Form, preSG, Volume, Amount, 0, 60, Alpha, Utilisation, BU_factor) * (1 + my_factor_mashhop / 100.0); |
444 |
485 |
445 } else if ((Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { |
486 } else if ((Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { |
446 /* |
487 /* |
447 * IBU's from hops during FWH and boil. |
488 * IBU's from hops during FWH and boil. |
448 */ |
489 */ |
449 double boil_time = Fulltime; |
490 double boil_time = Fulltime; |
450 if (Use == HOP_USEAT_BOIL) |
491 if (Use == HOP_USEAT_BOIL) |
451 boil_time = Boiltime; |
492 boil_time = Boiltime; |
452 ibu = TinsethIBU(Form, SG, Volume, Amount, 0, boil_time, Alpha); |
493 double fromSG = postSG - ((boil_time / Fulltime) * (postSG - preSG)); /* SG when this hop addition starts */ |
|
494 double avgSG = (postSG + fromSG) / 2; /* Average SG during this addition */ |
|
495 ibu = TinsethIBU(Form, avgSG, Volume, Amount, 0, boil_time, Alpha, Utilisation, BU_factor); |
453 |
496 |
454 /* |
497 /* |
455 * Corrections for Mash and FWH |
498 * Correction for FWH |
456 */ |
499 */ |
457 if (Use == HOP_USEAT_FWH) { |
500 if (Use == HOP_USEAT_FWH) { |
458 ibu *= (1 + my_factor_fwh / 100.0); |
501 ibu *= (1 + my_factor_fwh / 100.0); |
459 } |
502 } |
460 |
503 |
464 |
507 |
465 /* |
508 /* |
466 * Flameout, currently fixed 1 minute. |
509 * Flameout, currently fixed 1 minute. |
467 */ |
510 */ |
468 double flameout_time = 1; |
511 double flameout_time = 1; |
469 double fibu = TinsethIBU(Form, SG, Volume, Amount, boil_time, boil_time + flameout_time, Alpha); |
512 double fibu = TinsethIBU(Form, postSG, Volume, Amount, boil_time, boil_time + flameout_time, Alpha, Utilisation, BU_factor); |
470 fibu *= IBU_reduction(98.0); |
513 fibu *= IBU_reduction(98.0); |
471 //qDebug() << "during flameout" << fibu; |
514 //qDebug() << "during flameout" << fibu; |
472 nibu += fibu; |
515 nibu += fibu; |
473 |
516 |
474 // Add this hop during cooling |
517 // Add this hop during cooling |
475 /* |
518 /* |
476 * Hopstands, this boil hop adds some IBU's too. |
519 * Hopstands, this boil hop adds some IBU's too. |
477 */ |
520 */ |
478 if (Whirlpool9) { |
521 if (Whirlpool9) { |
479 double wibu9 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool9, Alpha); |
522 double wibu9 = TinsethIBU(Form, postSG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool9, Alpha, Utilisation, BU_factor); |
480 wibu9 *= IBU_reduction(87.0); |
523 wibu9 *= IBU_reduction(87.0); |
481 //qDebug() << "during whirlpool9" << wibu9; |
524 //qDebug() << "during whirlpool9" << wibu9; |
482 nibu += wibu9; |
525 nibu += wibu9; |
483 } |
526 } |
484 if (Whirlpool7) { |
527 if (Whirlpool7) { |
485 double wibu7 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool7, Alpha); |
528 double wibu7 = TinsethIBU(Form, postSG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool7, Alpha, Utilisation, BU_factor); |
486 wibu7 *= IBU_reduction(74.0); |
529 wibu7 *= IBU_reduction(74.0); |
487 //qDebug() << "during whirlpool7" << wibu7; |
530 //qDebug() << "during whirlpool7" << wibu7; |
488 nibu += wibu7; |
531 nibu += wibu7; |
489 } |
532 } |
490 if (Whirlpool6) { |
533 if (Whirlpool6) { |
491 double wibu6 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool6, Alpha); |
534 double wibu6 = TinsethIBU(Form, postSG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool6, Alpha, Utilisation, BU_factor); |
492 wibu6 *= IBU_reduction(63.0); |
535 wibu6 *= IBU_reduction(63.0); |
493 //qDebug() << "during whirlpool6" << wibu6; |
536 //qDebug() << "during whirlpool6" << wibu6; |
494 nibu += wibu6; |
537 nibu += wibu6; |
495 } |
538 } |
496 //qDebug() << "Old IBU" << ibu << "New IBU" << nibu; |
539 //qDebug() << "Old IBU" << ibu << "New IBU" << nibu; |
500 } else if ((Use == HOP_USEAT_AROMA) && (Method > 0)) { |
543 } else if ((Use == HOP_USEAT_AROMA) && (Method > 0)) { |
501 /* |
544 /* |
502 * At flameout, and only using extended calculation. |
545 * At flameout, and only using extended calculation. |
503 */ |
546 */ |
504 double flameout_time = 1; |
547 double flameout_time = 1; |
505 ibu = TinsethIBU(Form, SG, Volume, Amount, 0, flameout_time, Alpha); |
548 ibu = TinsethIBU(Form, postSG, Volume, Amount, 0, flameout_time, Alpha, Utilisation, BU_factor); |
506 ibu *= IBU_reduction(98.0); |
549 ibu *= IBU_reduction(98.0); |
507 /* |
550 /* |
508 * Hopstands, this flameout hop adds some IBU's too. |
551 * Hopstands, this flameout hop adds some IBU's too. |
509 */ |
552 */ |
510 if (Whirlpool9) { |
553 if (Whirlpool9) { |
511 double wibu9 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool9, Alpha); |
554 double wibu9 = TinsethIBU(Form, postSG, Volume, Amount, flameout_time, flameout_time + Whirlpool9, Alpha, Utilisation, BU_factor); |
512 wibu9 *= IBU_reduction(87.0); |
555 wibu9 *= IBU_reduction(87.0); |
513 //qDebug() << "during whirlpool9" << wibu9; |
556 //qDebug() << "during whirlpool9" << wibu9; |
514 ibu += wibu9; |
557 ibu += wibu9; |
515 } |
558 } |
516 if (Whirlpool7) { |
559 if (Whirlpool7) { |
517 double wibu7 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool7, Alpha); |
560 double wibu7 = TinsethIBU(Form, postSG, Volume, Amount, flameout_time, flameout_time + Whirlpool7, Alpha, Utilisation, BU_factor); |
518 wibu7 *= IBU_reduction(74.0); |
561 wibu7 *= IBU_reduction(74.0); |
519 //qDebug() << "during whirlpool7" << wibu7; |
562 //qDebug() << "during whirlpool7" << wibu7; |
520 ibu += wibu7; |
563 ibu += wibu7; |
521 } |
564 } |
522 if (Whirlpool6) { |
565 if (Whirlpool6) { |
523 double wibu6 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool6, Alpha); |
566 double wibu6 = TinsethIBU(Form, postSG, Volume, Amount, flameout_time, flameout_time + Whirlpool6, Alpha, Utilisation, BU_factor); |
524 wibu6 *= IBU_reduction(63.0); |
567 wibu6 *= IBU_reduction(63.0); |
525 //qDebug() << "during whirlpool6" << wibu6; |
568 //qDebug() << "during whirlpool6" << wibu6; |
526 ibu += wibu6; |
569 ibu += wibu6; |
527 } |
570 } |
528 |
571 |
529 } else if ((Use == HOP_USEAT_WHIRLPOOL) && (Method > 0)) { |
572 } else if ((Use == HOP_USEAT_WHIRLPOOL) && (Method > 0)) { |
530 /* |
573 /* |
531 * Hopstands. |
574 * Hopstands. |
532 */ |
575 */ |
533 if (Whirlpool9) { |
576 if (Whirlpool9) { |
534 double wibu9 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool9, Alpha); |
577 double wibu9 = TinsethIBU(Form, postSG, Volume, Amount, 0, Whirlpool9, Alpha, Utilisation, BU_factor); |
535 wibu9 *= IBU_reduction(87.0); |
578 wibu9 *= IBU_reduction(87.0); |
536 //qDebug() << "during whirlpool9" << wibu9; |
579 //qDebug() << "during whirlpool9" << wibu9; |
537 ibu = wibu9; |
580 ibu = wibu9; |
538 } |
581 } |
539 if (Whirlpool7) { |
582 if (Whirlpool7) { |
540 double wibu7 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool7, Alpha); |
583 double wibu7 = TinsethIBU(Form, postSG, Volume, Amount, 0, Whirlpool7, Alpha, Utilisation, BU_factor); |
541 wibu7 *= IBU_reduction(74.0); |
584 wibu7 *= IBU_reduction(74.0); |
542 //qDebug() << "during whirlpool7" << wibu7; |
585 //qDebug() << "during whirlpool7" << wibu7; |
543 ibu = wibu7; |
586 ibu = wibu7; |
544 } |
587 } |
545 if (Whirlpool6) { |
588 if (Whirlpool6) { |
546 double wibu6 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool6, Alpha); |
589 double wibu6 = TinsethIBU(Form, postSG, Volume, Amount, 0, Whirlpool6, Alpha, Utilisation, BU_factor); |
547 wibu6 *= IBU_reduction(63.0); |
590 wibu6 *= IBU_reduction(63.0); |
548 //qDebug() << "during whirlpool6" << wibu6; |
591 //qDebug() << "during whirlpool6" << wibu6; |
549 ibu = wibu6; |
592 ibu = wibu6; |
550 } |
593 } |
|
594 } else if (Use == HOP_USEAT_BOTTLING) { |
|
595 /* |
|
596 * Isomerized hop extracts to use at bottling. |
|
597 * Assume 10% volume is lost during fermentation and transfers. |
|
598 */ |
|
599 ibu = TinsethIBU(Form, postSG, Volume * 0.9, Amount, 0, 0, Alpha, Utilisation, BU_factor); |
551 } |
600 } |
552 |
601 |
553 double rc = round(ibu * 1000.0) / 1000.0; |
602 double rc = round(ibu * 1000.0) / 1000.0; |
554 |
603 |
555 qDebug() << "toIBU" << Use << Form << SG << Volume << Amount << Boiltime << Alpha << Method << Whirlpool9 << Whirlpool7 << Whirlpool6 << Fulltime << Cooltype << Coolparm1 << Coolparm2 << "rc:" << rc; |
604 qDebug() << "toIBU" << Use << Form << preSG << postSG << Volume << Amount << Boiltime << Alpha << Method << Whirlpool9 << Whirlpool7 << Whirlpool6 << Fulltime << Cooltype << Coolparm1 << Coolparm2 << Utilisation << BU_factor << "rc:" << rc; |
556 return rc; |
605 return rc; |
557 } |
606 } |
558 |
607 |
559 |
608 |
560 double Utils::hopFlavourContribution(double bt, double vol, int use, double amount) |
609 double Utils::hopFlavourContribution(double bt, double vol, int use, double amount, int form) |
561 { |
610 { |
562 double result; |
611 double result; |
563 |
612 |
564 if (use == HOP_USEAT_WHIRLPOOL || use == HOP_USEAT_DRY_HOP) |
613 if (use == HOP_USEAT_WHIRLPOOL || use == HOP_USEAT_DRY_HOP) |
565 return 0; |
614 return 0; |