src/Utils.cpp

changeset 343
67e645b9d23f
parent 342
0d14f1f565c4
child 344
9ffac162000c
equal deleted inserted replaced
342:0d14f1f565c4 343:67e645b9d23f
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(double SG, double Volume, double Amount, double Time, double Alpha) 394 double Utils::TinsethIBU(int Form, double SG, double Volume, double Amount, double T1, double T2, double Alpha)
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 398
399 /* 399 /*
400 * Basic Tinseth formula. 400 * Basic Tinseth formula.
401 * http://realbeer.com/hops/research.html 401 * http://realbeer.com/hops/research.html
402 */ 402 */
403 double AddedAlphaAcids = (alpha * mass * 1000) / Volume; 403 double AddedAlphaAcids = (alpha * mass * 1000) / Volume;
404 double Bigness_factor = 1.65 * pow(0.000125, SG - 1); 404 double Bigness_factor = 1.65 * pow(0.000125, SG - 1);
405 double BoilTime_factor = ((1 - exp(-0.04 * Time)) / 4.15); 405 double BoilTime_factor1 = ((1 - exp(-0.04 * T1)) / 4.15);
406 double ibu = Bigness_factor * BoilTime_factor * AddedAlphaAcids; 406 double BoilTime_factor2 = ((1 - exp(-0.04 * T2)) / 4.15);
407 407 double ibu = Bigness_factor * (BoilTime_factor2 - BoilTime_factor1) * AddedAlphaAcids;
408 qDebug() << "boilIBU" << SG << Volume << Amount << Time << Alpha << "IBU:" << ibu; 408
409 /*
410 * Correction for hop forms
411 */
412 if (Form == HOP_FORMS_PELLET) {
413 ibu *= (1 + my_factor_pellet / 100.0);
414 } else if (Form == HOP_FORMS_PLUG) {
415 ibu *= (1 + my_factor_plug / 100.0);
416 } 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
418 } else if (Form == HOP_FORMS_CRYO) {
419 ibu *= (1 + my_factor_cryohop / 100.0);
420 } else if (Form == HOP_FORMS_EXTRACT) {
421 // Nothing for now.
422 }
423
424 qDebug() << "boilIBU" << Form << SG << Volume << Amount << T1 << T2 << Alpha << "IBU:" << ibu;
409 return ibu; 425 return ibu;
410 } 426 }
411 427
412 428
413 double Utils::toIBU(int Use, int Form, double SG, double Volume, double Amount, double Boiltime, double Alpha, 429 double Utils::toIBU(int Use, int Form, double SG, double Volume, double Amount, double Boiltime, double Alpha,
414 int Method, double Whirlpool9, double Whirlpool7, double Whirlpool6, double Fulltime, int Cooltype, double Coolparm1, double Coolparm2) 430 int Method, double Whirlpool9, double Whirlpool7, double Whirlpool6, double Fulltime, int Cooltype, double Coolparm1, double Coolparm2)
415 { 431 {
416 double steep_time = 0; /* Total time a hop in the kettle. */
417 double loss_boiltemp = 1.0; /* Loss due to the lower boil temperature at higher altitude. */ 432 double loss_boiltemp = 1.0; /* Loss due to the lower boil temperature at higher altitude. */
418 433
419 double ibu = 0.0, whirlibus = 0.0; 434 double ibu = 0.0, whirlibus = 0.0;
420 double alpha = Alpha / 100.0; 435 double alpha = Alpha / 100.0;
421 double mass = Amount * 1000.0; 436 double mass = Amount * 1000.0;
422
423 // Ideas from Zymurgy March-April 2018. These are not exact formulas!
424 if (Use == HOP_USEAT_AROMA) {
425 if (Whirlpool9) { // Flameout hops are 2 minutes in this range.
426 whirlibus += (alpha * mass * 20) / Volume * (2.0 / 50.0);
427 }
428 if (Whirlpool7) { // Flameout hops are 4 minutes in this range.
429 whirlibus += (alpha * mass * 6) / Volume * (4.0 / 50.0);
430 }
431 // Experiment.
432 // double wibu = boilIBU(Form, SG, Volume, Amount, 6, Alpha, Method); // IBU's for 6 minutes full
433 // double fibu = wibu * 0.067 * IBU_reduction(94); // Add timed segments
434 // fibu += wibu * 0.1 * IBU_reduction(84);
435 // fibu += wibu * 0.167 * IBU_reduction(74);
436 // fibu += wibu * 0.250 * IBU_reduction(64);
437 // fibu += wibu * 0.417 * IBU_reduction(54);
438 //qDebug() << " 94" << wibu * 0.067 * IBU_reduction(94);
439 //qDebug() << " 84" << wibu * 0.1 * IBU_reduction(84);
440 //qDebug() << " 74" << wibu * 0.167 * IBU_reduction(74);
441 //qDebug() << " 64" << wibu * 0.250 * IBU_reduction(64);
442 //qDebug() << " 54" << wibu * 0.417 * IBU_reduction(54);
443 // qDebug() << "flamout" << wibu << fibu;
444 }
445 if (Use == HOP_USEAT_WHIRLPOOL) { // Flameout or any whirlpool
446 if (Whirlpool9) {
447 // 20 mg/l/50 min
448 whirlibus += (alpha * mass * 20) / Volume * (Whirlpool9 / 50.0);
449 //qDebug() << "Whirlpool9" << alpha * mass * 20 << " liter:" << liters << " time:" << Whirlpool9 << " ibu" << (alpha * mass * 20) / liters * (Whirlpool9 / 50.0);
450 }
451 if (Whirlpool7) {
452 // 6 mg/l/50 min
453 whirlibus += (alpha * mass * 6) / Volume * (Whirlpool7 / 50.0);
454 //qDebug() << "Whirlpool7" << alpha * mass * 6 << " liter:" << liters << " time:" << Whirlpool7 << " ibu" << (alpha * mass * 6) / liters * (Whirlpool7 / 50.0);
455 }
456 if (Whirlpool6) {
457 // 2 mg/l/50 min
458 whirlibus += (alpha * mass * 2) / Volume * (Whirlpool6 / 50.0);
459 }
460 // double wibu = boilIBU(Form, SG, Volume, Amount, Boiltime, Alpha, Method);
461 // qDebug() << "whirpool" << wibu << wibu * IBU_reduction(74);
462 }
463
464 if ((Use == HOP_USEAT_MASH) || (Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) {
465 steep_time += Boiltime;
466 }
467 437
468 /* 438 /*
469 * IBU's from hops during Mash, FWH and boil. 439 * IBU's from hops during Mash, FWH and boil.
470 */ 440 */
471 if ((Use == HOP_USEAT_MASH) || (Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) { 441 if ((Use == HOP_USEAT_MASH) || (Use == HOP_USEAT_FWH) || (Use == HOP_USEAT_BOIL)) {
472 ibu = TinsethIBU(SG, Volume, Amount, Fulltime, Alpha); 442 double boil_time = Fulltime;
443 if (Use == HOP_USEAT_BOIL)
444 boil_time = Boiltime;
445 ibu = TinsethIBU(Form, SG, Volume, Amount, 0, boil_time, Alpha);
446
473 /* 447 /*
474 * Corrections for Mash and FWH 448 * Corrections for Mash and FWH
475 */ 449 */
476 if (Use == HOP_USEAT_MASH) { 450 if (Use == HOP_USEAT_MASH) {
477 ibu *= (1 + my_factor_mashhop / 100.0); 451 ibu *= (1 + my_factor_mashhop / 100.0);
478 } 452 }
479 if (Use == HOP_USEAT_FWH) { 453 if (Use == HOP_USEAT_FWH) {
480 ibu *= (1 + my_factor_fwh / 100.0); 454 ibu *= (1 + my_factor_fwh / 100.0);
481 } 455 }
482 456
457 if (Method > 0) {
458 double nibu = ibu;
459 loss_boiltemp = IBU_reduction(boilPoint());
460 nibu *= loss_boiltemp;
461 qDebug() << "ibu" << nibu << "loss_boiltemp" << loss_boiltemp;
462
463 /*
464 * Flameout, currently fixed 1 minute.
465 */
466 double flameout_time = 1;
467 double fibu = TinsethIBU(Form, SG, Volume, Amount, boil_time, boil_time + flameout_time, Alpha);
468 fibu *= IBU_reduction(98.0);
469 qDebug() << "during flameout" << fibu;
470 nibu += fibu;
471
472 // Add this hop during cooling
473 /*
474 * Hopstands, this boil hop adds some IBU's too.
475 */
476 if (Whirlpool9) {
477 double wibu9 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool9, Alpha);
478 wibu9 *= IBU_reduction(87.0);
479 //qDebug() << "during whirlpool9" << wibu9;
480 nibu += wibu9;
481 }
482 if (Whirlpool7) {
483 double wibu7 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool7, Alpha);
484 wibu7 *= IBU_reduction(74.0);
485 //qDebug() << "during whirlpool7" << wibu7;
486 nibu += wibu7;
487 }
488 if (Whirlpool6) {
489 double wibu6 = TinsethIBU(Form, SG, Volume, Amount, boil_time + flameout_time, boil_time + flameout_time + Whirlpool6, Alpha);
490 wibu6 *= IBU_reduction(63.0);
491 //qDebug() << "during whirlpool6" << wibu6;
492 nibu += wibu6;
493 }
494 qDebug() << "Old IBU" << ibu << "New IBU" << nibu;
495 ibu = nibu;
496 }
497
498 } else if ((Use == HOP_USEAT_AROMA) && (Method > 0)) {
483 /* 499 /*
484 * Correction for hop forms 500 * At flameout, and only using extended calculation.
485 */ 501 */
486 if (Form == HOP_FORMS_PELLET) { 502 double flameout_time = 1;
487 ibu *= (1 + my_factor_pellet / 100.0); 503 ibu = TinsethIBU(Form, SG, Volume, Amount, 0, flameout_time, Alpha);
488 } else if (Form == HOP_FORMS_PLUG) { 504 ibu *= IBU_reduction(98.0);
489 ibu *= (1 + my_factor_plug / 100.0);
490 } else if (Form == HOP_FORMS_LEAF_WET) {
491 ibu *= (1 + my_factor_wethop / 100.0); // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py
492 } else if (Form == HOP_FORMS_CRYO) {
493 ibu *= (1 + my_factor_cryohop / 100.0);
494 } else if (Form == HOP_FORMS_EXTRACT) {
495 // Nothing for now.
496 }
497
498 if (Method > 0) {
499 loss_boiltemp = IBU_reduction(boilPoint());
500 ibu *= loss_boiltemp;
501 qDebug() << "ibu" << ibu << "loss_boiltemp" << loss_boiltemp;
502 }
503
504 // } else if (Use == HOP_USEAT_AROMA) {
505 /* 505 /*
506 * At flameout. The cooling method is important. 506 * Hopstands, this flameout hop adds some IBU's too.
507 * Emersion chiller, Counterflow chiller, Au bain marie or natural. 507 */
508 * Assume the hop is removed for all methods except Emersion chilling. 508 if (Whirlpool9) {
509 */ 509 double wibu9 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool9, Alpha);
510 } else { 510 wibu9 *= IBU_reduction(87.0);
511 // qDebug() << "whirlibus" << whirlibus << Use; 511 //qDebug() << "during whirlpool9" << wibu9;
512 } 512 ibu += wibu9;
513 513 }
514 return round((ibu + whirlibus) * 100.0) / 100.0; 514 if (Whirlpool7) {
515 double wibu7 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool7, Alpha);
516 wibu7 *= IBU_reduction(74.0);
517 //qDebug() << "during whirlpool7" << wibu7;
518 ibu += wibu7;
519 }
520 if (Whirlpool6) {
521 double wibu6 = TinsethIBU(Form, SG, Volume, Amount, flameout_time, flameout_time + Whirlpool6, Alpha);
522 wibu6 *= IBU_reduction(63.0);
523 //qDebug() << "during whirlpool6" << wibu6;
524 ibu += wibu6;
525 }
526
527 } else if ((Use == HOP_USEAT_WHIRLPOOL) && (Method > 0)) {
528 /*
529 * Hopstands.
530 */
531 if (Whirlpool9) {
532 double wibu9 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool9, Alpha);
533 wibu9 *= IBU_reduction(87.0);
534 //qDebug() << "during whirlpool9" << wibu9;
535 ibu = wibu9;
536 }
537 if (Whirlpool7) {
538 double wibu7 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool7, Alpha);
539 wibu7 *= IBU_reduction(74.0);
540 //qDebug() << "during whirlpool7" << wibu7;
541 ibu = wibu7;
542 }
543 if (Whirlpool6) {
544 double wibu6 = TinsethIBU(Form, SG, Volume, Amount, 0, Whirlpool6, Alpha);
545 wibu6 *= IBU_reduction(63.0);
546 //qDebug() << "during whirlpool6" << wibu6;
547 ibu = wibu6;
548 }
549 }
550
551 double rc = round((ibu + whirlibus) * 100.0) / 100.0;
552
553 qDebug() << "toIBU" << Use << Form << SG << Volume << Amount << Boiltime << Alpha << Method << Whirlpool9 << Whirlpool7 << Whirlpool6 << Fulltime << Cooltype << Coolparm1 << Coolparm2 << "rc:" << rc;
554 return rc;
515 } 555 }
516 556
517 557
518 double Utils::hopFlavourContribution(double bt, double vol, int use, double amount) 558 double Utils::hopFlavourContribution(double bt, double vol, int use, double amount)
519 { 559 {

mercurial