Ejemplo n.º 1
0
 QString Mash::toXml() {

    calcMashSchedule();
    QString sb;
    sb.append("  <MASH>\n");
    sb.append(SBStringUtils::xmlElement("NAME", name, 4));
    sb.append(SBStringUtils::xmlElement("MASH_VOLUME", SBStringUtils::format(Quantity::convertUnit(CONVERTER_QT, volUnits, volQts), 2) , 4));
    sb.append(SBStringUtils::xmlElement("MASH_VOL_U", volUnits, 4));
    sb.append(SBStringUtils::xmlElement("MASH_RATIO", mashRatio, 4));
    sb.append(SBStringUtils::xmlElement("MASH_RATIO_U", mashRatioU, 4));
    sb.append(SBStringUtils::xmlElement("MASH_TIME", totalTime, 4));
    sb.append(SBStringUtils::xmlElement("MASH_TMP_U", tempUnits, 4));
    sb.append(SBStringUtils::xmlElement("THICK_DECOCT_RATIO", thickDecoctRatio, 4));
    sb.append(SBStringUtils::xmlElement("THIN_DECOCT_RATIO", thinDecoctRatio, 4));
    if (tempUnits == "C"){
        sb.append(SBStringUtils::xmlElement("MASH_TUNLOSS_TEMP", (tunLossF/1.8), 4));
        sb.append(SBStringUtils::xmlElement("GRAIN_TEMP", BrewCalcs::fToC(grainTempF), 4));
        sb.append(SBStringUtils::xmlElement("BOIL_TEMP", BrewCalcs::fToC(boilTempF), 4));
    }
    else {
        sb.append(SBStringUtils::xmlElement("MASH_TUNLOSS_TEMP", tunLossF, 4));
        sb.append(SBStringUtils::xmlElement("GRAIN_TEMP", grainTempF, 4));
        sb.append(SBStringUtils::xmlElement("BOIL_TEMP", boilTempF, 4));
    }

    for (int i = 0; i < steps.size(); i++) {
        MashStep st = steps.at(i);
        sb.append("    <ITEM>\n");
        sb.append(SBStringUtils::xmlElement("TYPE", st.getType(), 6));
        sb.append(SBStringUtils::xmlElement("TEMP", st.getStartTemp(), 6));

        double sTemp;
        double eTemp;

        if (tempUnits == "C") {
            sTemp = BrewCalcs::fToC(st.getStartTemp());
            eTemp = BrewCalcs::fToC(st.getEndTemp());
        } else {
            sTemp = st.getStartTemp();
            eTemp = st.getEndTemp();
        }

        sb.append(SBStringUtils::xmlElement("DISPL_TEMP", SBStringUtils::format(sTemp, 1), 6));

        sb.append(SBStringUtils::xmlElement("END_TEMP", st.getEndTemp(), 6));
        sb.append(SBStringUtils::xmlElement("DISPL_END_TEMP", SBStringUtils::format(eTemp, 1), 6));

        sb.append(SBStringUtils::xmlElement("MIN", st.getMinutes(), 6));
        sb.append(SBStringUtils::xmlElement("RAMP_MIN", st.getRampMin(), 6));
        sb.append(SBStringUtils::xmlElement("METHOD", st.getMethod(), 6));
        sb.append(SBStringUtils::xmlElement("WEIGHT_LBS", st.getWeightLbs(), 6));
        sb.append(SBStringUtils::xmlElement("DIRECTIONS", st.getDirections(), 6));
        sb.append("    </ITEM>\n");
    }

    sb.append("  </MASH>\n");
    return sb;
}
Ejemplo n.º 2
0
void Mash::calcMashSchedule() {
    // Method to run through the mash table and calculate values
    if (!this->allowRecalcs) {
        return;
    }

    double strikeTemp = 0;
    double targetTemp = 0;
    double waterAddedQTS = 0;
    double waterEquiv = 0;
    double mr = mashRatio;
    double currentTemp = getGrainTemp();

    double displTemp = 0;
    double tunLoss; // figure out a better way to do this, eg: themal mass
    double decoct = 0;
    int totalMashTime = 0;
    int totalSpargeTime = 0;
    double mashWaterQTS = 0;
    double mashVolQTS = 0;
    int numSparge = 0;
    double totalWeightLbs = 0;
    double totalCerealLbs = 0;


    // convert mash ratio to qts/lb if in l/kg
    if (mashRatioU.compare(L_PER_KG) == 0) {
        mr *= 0.479325;
    }

    // convert CurrentTemp to F
    if (tempUnits == "C") {
        currentTemp = BrewCalcs::cToF(currentTemp);
        tunLoss = tunLossF * 1.8;
    }
    else
        tunLoss = tunLossF;

    // perform calcs on first record
    if (steps.empty())
        return;

    // sort the list
    //Collections.sort(steps);

    // add up the cereal mash lbs
    for (int i = 1; i < steps.size(); i++) {
        MashStep *stp = &steps[i];
        if (stp->getMethod() == "cereal mash"){
            totalCerealLbs += stp->getWeightLbs();
        }
    }
    totalWeightLbs = maltWeightLbs - totalCerealLbs;


    // the first step is always an infusion
    MashStep *stp = &steps[0];
    targetTemp = stp->getStartTemp();
    strikeTemp = calcStrikeTemp(targetTemp, currentTemp, mr, tunLoss);
    waterAddedQTS = mr * totalWeightLbs;
    waterEquiv = totalWeightLbs * (0.192 + mr);
    mashVolQTS = calcMashVol(totalWeightLbs, mr);
    totalMashTime += stp->getMinutes();
    mashWaterQTS += waterAddedQTS;
    stp->getInVol()->setUnits(CONVERTER_QT);
    stp->getInVol()->setAmount(waterAddedQTS);
    stp->setStrikeTemp(strikeTemp);
    stp->setMethod(INFUSION);
    stp->setWeightLbs(totalWeightLbs);

    qDebug() << "Subtracting cereal " <<  totalCerealLbs << " from " <<maltWeightLbs;
    totalWeightLbs = maltWeightLbs - totalCerealLbs;
    // subtract the water added from the Water Equiv so that they are correct when added in the next part of the loop
    waterEquiv -= waterAddedQTS;

    // Updated the water added

    if (tempUnits == "C") {
        strikeTemp = BrewCalcs::fToC(strikeTemp);
    }

    stp->setDirections("Mash in with " + SBStringUtils::format(stp->getInVol()->getValueAs(volUnits),1 ) + " " + volUnits
            + " of water at " + SBStringUtils::format(strikeTemp, 1) + " " + tempUnits);

    // set TargetTemp to the end temp
    targetTemp = stp->getEndTemp();

    for (int i = 1; i < steps.size(); i++) {
        stp = &steps[i];
        currentTemp = targetTemp; // switch
        targetTemp = stp->getStartTemp();

        // if this is a former sparge step that's been changed, change
        // the method to infusion
        qDebug() << "Mash step Type: " << stp->getType() << " Method: " << stp->getMethod();
        if (stp->getType() != SPARGE && ( stp->getMethod() == FLY || stp->getMethod() == BATCH))
                stp->setMethod(INFUSION);

        // do calcs
        if (stp->getMethod() == INFUSION) { // calculate an infusion step
            decoct = 0;
            waterEquiv += waterAddedQTS; // add previous addition to get WE
            strikeTemp = boilTempF; // boiling water

            // Updated the water added
            waterAddedQTS = calcWaterAddition(targetTemp, currentTemp,
                    waterEquiv, boilTempF);

            stp->getOutVol()->setAmount(0);
            stp->getInVol()->setUnits(CONVERTER_QT);
            stp->getInVol()->setAmount(waterAddedQTS);
            stp->setStrikeTemp(strikeTemp);
            stp->setWeightLbs(totalWeightLbs);

            if (tempUnits == "C")
                strikeTemp = 100;
            stp->setDirections("Add " + SBStringUtils::format(stp->getInVol()->getValueAs(volUnits), 1) + " " + volUnits
                    + " of water at " + SBStringUtils::format(strikeTemp, 1) + " " + tempUnits);

            mashWaterQTS += waterAddedQTS;
            mashVolQTS += waterAddedQTS;

        }

        else if (stp->getMethod() == DECOCTION) { // calculate a decoction step
            waterEquiv += waterAddedQTS; // add previous addition to get WE
            waterAddedQTS = 0;
            strikeTemp = boilTempF; // boiling water
            double ratio=0.75;

            if (stp->getMethod() == DECOCTION_THICK)
                ratio = thickDecoctRatio;
            else if (stp->getMethod() == DECOCTION_THIN)
                ratio = thinDecoctRatio;

            // Calculate volume (qts) of mash to remove
            decoct = calcDecoction2(targetTemp, currentTemp, mashWaterQTS, ratio, totalWeightLbs);
            stp->getOutVol()->setUnits(CONVERTER_QT);
            stp->getOutVol()->setAmount(decoct);
            stp->getInVol()->setAmount(0);
            stp->setStrikeTemp(boilTempF);
            stp->setWeightLbs(totalWeightLbs);

            // Updated the decoction, convert to right units & make directions
            stp->setDirections("Remove " + SBStringUtils::format(stp->getOutVol()->getValueAs(volUnits), 1) + " " + volUnits
                    + " of mash, boil, and return to mash.");

        } else if (stp->getMethod() == DIRECT) { // calculate a direct heat step
            decoct = 0;
            waterEquiv += waterAddedQTS; // add previous addition to get WE
            waterAddedQTS = 0;
            displTemp = stp->getStartTemp();
            if (tempUnits == "C")
                displTemp = BrewCalcs::fToC(displTemp);
            stp->setDirections("Add direct heat until mash reaches " + QString::number(displTemp)
                    + " " + tempUnits + ".");
            stp->getInVol()->setAmount(0);
            stp->getOutVol()->setAmount(0);
            stp->setStrikeTemp(0);
            stp->setWeightLbs(totalWeightLbs);

        } else if (stp->getMethod() == CEREAL_MASH) { // calculate a cereal mash step

            waterEquiv += waterAddedQTS; // add previous addition to get WE
            waterAddedQTS = 0;
            targetTemp = stp->getStartTemp();
            double extraWaterQTS = 0;
            double cerealTemp = boilTempF;
            double cerealTargTemp = cerealMashTemp;
            QString addStr = "";

            /*
             * 1. check the temp of the mash when you add the boiling cereal mash @ default ratio back
             * 2. if it's > than the step temp, adjust the step temp
             * 3. if it's < than the step temp, add extra water to increase the "heat equivalencey" of the cereal mash
             */

            double cerealWaterEquiv = stp->getWeightLbs() * (0.192 + mr);
            waterAddedQTS = mr * stp->getWeightLbs();
            strikeTemp = calcStrikeTemp(cerealMashTemp, grainTempF, mr, 0);

            double newTemp = ((waterEquiv * currentTemp) + (cerealWaterEquiv * cerealTemp)) / (waterEquiv + cerealWaterEquiv);

            if (newTemp > targetTemp){
                stp->setStartTemp(newTemp);
            }

            if (newTemp < targetTemp){
                double addQts = ((waterEquiv * (targetTemp - currentTemp)) / (cerealTemp - targetTemp)) - 0.192;
                extraWaterQTS = addQts - waterAddedQTS;
                addStr = " Add " + SBStringUtils::format(Quantity::convertUnit(CONVERTER_QT, volUnits, extraWaterQTS), 1)
                  + " " + volUnits + " water to the cereal mash.";
            }

            // Calculate final temp of cereal mash
            // cerealTemp = (targetTemp * (waterEquiv + cerealWaterEquiv) - (waterEquiv * currentTemp)) / cerealWaterEquiv;
            totalMashTime += stp->getMinutes();
            mashWaterQTS += waterAddedQTS + extraWaterQTS;
            stp->getInVol()->setUnits(CONVERTER_QT);
            stp->getInVol()->setAmount(waterAddedQTS);
            stp->getOutVol()->setAmount(0);
            stp->setStrikeTemp(strikeTemp);

            // make directions

            QString weightStr = SBStringUtils::format(Quantity::convertUnit(CONVERTER_LB, this->maltUnits, stp->getWeightLbs()), 1)
            + " " + this->maltUnits;
            QString volStr = SBStringUtils::format(Quantity::convertUnit(CONVERTER_QT, volUnits, waterAddedQTS), 1)
            + " " + volUnits;

            if (tempUnits == "C"){
                strikeTemp = BrewCalcs::fToC(strikeTemp);
                cerealTemp = BrewCalcs::fToC(cerealTemp);
                targetTemp = BrewCalcs::fToC(targetTemp);
                cerealTargTemp = BrewCalcs::fToC(cerealTargTemp);
            }

            QString tempStr = SBStringUtils::format(strikeTemp, 1) + tempUnits;
            QString tempStr2 = SBStringUtils::format(cerealTemp, 1) + tempUnits;
            QString tempStr3 = SBStringUtils::format(targetTemp, 1) + tempUnits;
            QString tempStr4 = SBStringUtils::format(cerealTargTemp, 1) + tempUnits;
            QString newDirection = "Cereal mash: mash " + weightStr + " grain with " + volStr + " water at " +
                tempStr + " to hit " + tempStr4 + " and rest.";
            newDirection.append(addStr);
            newDirection.append(" Raise to " +  tempStr2 + " and add to the main mash to reach " + tempStr3);

            stp->setDirections(newDirection);
            // add cereal mash to total weight
            totalWeightLbs += stp->getWeightLbs();

        } else {

            qDebug() << "Unrecognised mash step: " << stp->getMethod();
        }

        if (stp->getType() == SPARGE)
            numSparge++;
        else {
            totalMashTime += stp->getMinutes();
        }
        // set target temp to end temp for next step
        targetTemp = stp->getEndTemp();

    } // for steps.size()

    waterEquiv += waterAddedQTS; // add previous addition to get WE
    totalTime = totalMashTime;
    volQts = mashVolQTS;

    // water use stats:
    qDebug() << "Total Weight " << totalWeightLbs;
    absorbedQTS = totalWeightLbs * 0.52; // figure from HBD

    // spargeTotalQTS = (myRecipe.getPreBoilVol("qt")) - (mashWaterQTS - absorbedQTS);
    totalWaterQTS = mashWaterQTS;
    spargeQTS = this->preBoilQts -
            (mashWaterQTS - absorbedQTS - deadSpace.getValueAs(CONVERTER_QT));
    qDebug() << "Sparge QTs: " << spargeQTS;
    // Now let's figure out the sparging:
    if (numSparge == 0)
        return;

    // Amount to collect per sparge
    double col = this->preBoilQts / numSparge;
    QList<double> charge = QList<double>();
    QList<double> collect = QList<double>();

    for (int i = 0; i < numSparge; i++) {
        charge.append(0.0);
        collect.append(0.0);
    }
    double totalCollectQts = this->preBoilQts;


    // do we need to add more water to charge up
    // is the amount we need to collect less than the initial mash volume - absorbption
    if (col <= (mashWaterQTS - absorbedQTS)) {
        charge[0] = 0;
        collect[0] = mashWaterQTS - absorbedQTS; // how much is left over from the mash
        totalCollectQts = totalCollectQts - collect[0];
    } else {
        charge[0] = col - (mashWaterQTS - absorbedQTS); // add the additional water to get out the desired first collection amount PER sparge
        collect[0] = col;
        totalCollectQts = totalCollectQts - collect[0];
    }

   // do we need any more steps?
    if(numSparge > 1) {
        /*
        batch_1_sparge_liters = (boil_size_l/<total number of steps> ) - mash_water_l + grain_wt_kg * 0.625)
                batch_2_liters = boil_size_l / <total number of steps>
            */
        for (int i = 1; i < numSparge; i++) {
            charge[i] = col;
            collect[i] = col;
        }
    }

    int j=0;
    for (int i = 1; i < steps.size(); i++) {
        stp = &steps[i];
        if (stp->getType() == SPARGE) {

            stp->getInVol()->setUnits(CONVERTER_QT);
            stp->getInVol()->setAmount(charge[j]);

            stp->getOutVol()->setUnits(CONVERTER_QT);
            stp->getOutVol()->setAmount(collect[j]);

            stp->setStrikeTemp(SPARGETMPF);
            totalSpargeTime += stp->getMinutes();
            QString collectStr = SBStringUtils::format(Quantity::convertUnit(CONVERTER_QT, volUnits, collect[j]), 2) +
            " " + volUnits;
            QString tempStr = "";

            if (tempUnits == "F"){
                tempStr = "" + SBStringUtils::format(SPARGETMPF, 1) + "F";
            } else {
                tempStr = SBStringUtils::format(BrewCalcs::fToC(SPARGETMPF), 1) + "C";
            }

            if (numSparge > 1){
                stp->setMethod(BATCH);
                QString add = SBStringUtils::format(Quantity::convertUnit(CONVERTER_QT, volUnits, charge[j]), 2) +
                " " + volUnits;
                stp->setDirections("Add " + add + " at " + tempStr + " to collect " + collectStr);

            } else {
                stp->getInVol()->setUnits(CONVERTER_QT);
                stp->getInVol()->setAmount(spargeQTS);

                stp->getOutVol()->setUnits(CONVERTER_QT);
                stp->getOutVol()->setAmount(collect[j]);

                stp->setMethod(FLY);
                stp->setDirections("Sparge with " +
                        SBStringUtils::format(Quantity::convertUnit(CONVERTER_QT, volUnits, spargeQTS), 1) +
                        " " + volUnits + " at " + tempStr + " to collect " + collectStr);
            }
            j++;
        }
    }
}