src/Utils.cpp

changeset 337
8133cdb19aa1
parent 336
e97f9e87d94b
child 339
2f3cfb983fcc
equal deleted inserted replaced
336:e97f9e87d94b 337:8133cdb19aa1
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 {

mercurial