void CPrecipitator::EvalLosses(MStream & Prod) { double T = Prod.T; double TA = AmbientTemp(); /// Heat Loss to internal cooling, eg Barriquands if (!m_bCoolerOn && iCoolMethod==COOL_Hx) { m_dLiquorTout = Prod.T; m_dLMTD = 0.0; m_dCoolWaterTout = CoolIn.T; } m_dIntCoolRate=0.0; if (m_bCoolerOn && iCoolType==COOL_INTERNAL && bCoolIn && iCoolMethod == COOL_Hx ) { MStreamI TubeIn; MStreamI TubeOut; MStreamI CoolOut; if (m_bByVolFlow) m_dCoolFlow = m_dIntCoolVolFlow*Prod.Density(); else m_dIntCoolVolFlow = m_dCoolFlow/Prod.Density(); TubeIn.SetM(Prod, MP_All, m_dCoolFlow); TubeOut.SetF(TubeIn, MP_All, 1.0); CoolOut.SetF(CoolIn, MP_All, 1.0); if (TubeIn.MassFlow()>0 && CoolIn.MassFlow()>0) { m_dUA=m_dCoolArea*m_dCoolHTC; CCoolerFn Fn(m_dUA, TubeIn, CoolIn, TubeOut, CoolOut); double TubeOutT; double MxTbOutT=TubeIn.T;// No Transfer double MnTbOutT=CoolIn.T+0.001; double qShell=-CoolIn.totHz()+CoolOut.totHz(MP_All, TubeIn.T); double qTube= -TubeIn.totHz(MP_All, CoolIn.T)+TubeIn.totHz(); if (qShell<qTube) // Limited By Shell - Tube TOut Limited MnTbOutT=MxTbOutT - (qShell)/GTZ(qTube)*(MxTbOutT-MnTbOutT); switch (Fn.FindRoot(0, MnTbOutT, MxTbOutT)) { case RF_OK: TubeOutT = Fn.Result(); break; case RF_LoLimit: TubeOutT = MnTbOutT; break; case RF_HiLimit: TubeOutT = MxTbOutT; break; default: Log.Message(MMsg_Error, "TubeOutT not found - RootFinder:%s", Fn.ResultString(Fn.Error())); TubeOutT=Fn.Result(); break; } TubeOut.T = TubeOutT; m_dIntCoolRate = TubeIn.totHz() - TubeOut.totHz(); m_dCoolWaterTout = CoolOut.T; m_dCoolWaterTin = CoolIn.T; m_dCoolWaterFlow = CoolIn.Mass(); m_dCoolWaterFlowVol = CoolIn.Volume(); m_dLiquorTout = TubeOut.T; m_dLMTD=fabs(LMTD(TubeIn.T, TubeOut.T, CoolIn.T, CoolOut.T)); } } if (iCoolType==COOL_INTERNAL && iCoolMethod == COOL_dQ ) { m_dIntCoolRate = m_dCooldQ; } switch (iCoolType) { case COOL_NONE: m_dCoolRate = 0; break; case COOL_EXTERNAL: m_dCoolRate = m_dExtCoolRate; // kW break; case COOL_INTERNAL: /// Heat Loss to internal cooling, eg draft tube coolers m_dCoolRate = m_dIntCoolRate; break; } /// Evaporation Rate switch (iEvapMethod) { case EVAP_dT: m_dEvapRate = m_dEvapRateK*(T-TA); // kg/s x[3] = m_dEvapRate; break; case EVAP_FIXED: x[3] = m_dEvapRate; break; case EVAP_NONE: x[3] = 0; m_dEvapRate = 0.0; } /// Evaporative Heat Loss ... need to fix this up using stream enthalpies m_dEvapThermalLoss = 2300*m_dEvapRate; // kW... /// Heat Loss to ambient cooling m_dEnvironmentLoss = 0.0; switch (iThermalLossMethod) { case THL_Ambient: m_dEnvironmentLoss = (T-TA)*dThermalLossAmbient; //kW break; case THL_FixedHeatFlow: m_dEnvironmentLoss = dThermalLossRqd; break; } m_dTotThermalLoss = m_dEnvironmentLoss + m_dEvapThermalLoss + m_dCoolRate; }
void BatchPrecip::EvalProducts() { #if ForceOptimizeOff static Cnt = 0; if (stricmp(getTag(), "SpeciauxUE")==0) { Cnt++;//place breakpoint here to stop for specified model } #endif if (!IsSolveDirect)//(!IsProbal) return; bool Err = true; try { MStreamI Slurry; MStreamI Liquor; FlwIOs.AddMixtureIn_Id(Slurry, idSlurry); //sum all input slurry streams m_bHasLiquor = (FlwIOs.First[idLiquor]>=0); //test if liquor stream connected if (m_bHasLiquor) { FlwIOs.AddMixtureIn_Id(Liquor, idLiquor); //sum all input liquor streams //double LiquorSolids = Liquor.Mass(); } MStream & Prod = FlwIOs[FlwIOs.First[idDrawOff]].Stream; //get a reference to the single output stream Prod = Slurry; //set output stream = input stream (including PSD data, etc) m_dThermalLoss = 0.0; m_dYield = 0.0; m_dTempIn = Slurry.T; MIBayer & SlurryB = *Slurry.FindIF<MIBayer>(); //get access to bayer properties interface for stream if (!IsNothing(SlurryB)) { m_dACIn = SlurryB.AtoC(); m_dSolidConcIn = SlurryB.SolidsConc(C2K(25.0)); } else { m_dACIn = 0.0; m_dSolidConcIn = 0.0; } if (m_bOn && Slurry.Mass()>UsableMass) { MIBayer & ProdB = *Prod.FindIF<MIBayer>(); Log.SetCondition(IsNothing(ProdB), 1, MMsg_Warning, "Bad Slurry Stream - Not Bayer Model"); //expect stream to have bayer properties if (!IsNothing(SlurryB) && !IsNothing(ProdB)) { // --- --- Thermal Losses. ------------ // !! this should be done COMBINED with FLAHSING/evaporation as the Caustic concentration should increase Slurry.SetTP(Slurry.T -m_dFillingTempDrop, Slurry.P); Liquor.SetTP(Liquor.T -m_dFillingTempDrop, Liquor.P); double SlurryH0 = Slurry.totHf(MP_All, Slurry.T, Slurry.P); double LiquorH0 = (m_bHasLiquor ? Liquor.totHf(MP_All, Liquor.T, Liquor.P) : 0.0); //Prod.SetTemp(Prod.Temp()-m_dTempDrop); RunSteady(Slurry, Liquor, Prod); m_dThermalLoss = (SlurryH0+LiquorH0)-Prod.totHf(MP_All, Prod.T, Prod.P); m_dACOut = ProdB.AtoC(); m_dSolidConcOut = ProdB.SolidsConc(C2K(25.0)); //m_dResTime = TankVol/GTZ(Prod.Volume(som_SL)); double Cout = ProdB.CausticConc(Prod.T); m_dYield = Cout*(m_dACIn-m_dACOut); } if (sm_bCompletePopulation) m_QProd = Prod;//copy the content of the stream in the equipement only if STILL in Pop mode } else { if (m_bHasLiquor) { Prod.AddF(Liquor, MP_All, 1.0); } MIBayer & ProdB = *Prod.FindIF<MIBayer>(); if (!IsNothing(ProdB)) { m_dACOut = ProdB.AtoC(); m_dSolidConcOut = ProdB.SolidsConc(C2K(25.0)); } else { m_dACOut = 0.0; m_dSolidConcOut = 0.0; } } m_dTempOut = Prod.T; Err = false; } catch (MMdlException &e) { Log.Message(MMsg_Error, e.Description); } catch (MFPPException &e) { e.ClearFPP(); Log.Message(MMsg_Error, e.Description); } catch (MSysException &e) { Log.Message(MMsg_Error, e.Description); } catch (...) { Log.Message(MMsg_Error, "Some Unknown Exception occured"); } if (Err) {//Something is wrong!!! Bug needs fixing!!! //So lets set product=feed.... MStreamI Feed; MStream & Prod = FlwIOs[FlwIOs.First[idDrawOff]].Stream; //get a reference to the single output stream FlwIOs.AddMixtureIn_Id(Feed, idSlurry); //sum all input slurry streams Prod = Feed; if (FlwIOs.First[idLiquor]>=0) { MStreamI Liquor; FlwIOs.AddMixtureIn_Id(Liquor, idLiquor); //sum all input liquor streams Prod.AddF(Liquor, MP_All, 1.0); } CString ProblemModel; ProblemModel = getTag(); //SetStopRequired("Phone Denis!"); } Log.SetCondition(Err, 5, MMsg_Error, "Error needs fixing!!!"); }
void FlotationCell::EvalProducts() { try { dPrimaryRecovery = dReqPrimaryRecovery; dPrimaryGrade = dReqPrimaryGrade; for (unsigned int i = 0; i < vSecondaryRecoveries.size(); i++) vSecondaryRecoveries.at(i) = vReqSecondaryRecoveries.at(i); //sum all input streams into a working copy MStreamI QI; FlwIOs.AddMixtureIn_Id(QI, idFeed); MStreamI QAir; FlwIOs.AddMixtureIn_Id(QAir, idAir); bool bAirOn = FlwIOs.Count[idAir] > 0; //get handles to output streams... MStream & QOT = FlwIOs[FlwIOs.First[idTail]].Stream; MStream & QOC = FlwIOs[FlwIOs.First[idConc]].Stream; MStream & QOV = FlwIOs[FlwIOs.First[idVent]].Stream; QOT = QI; QOT.ZeroMass(); QOC = QI; QOC.ZeroMass(); if (!m_bOn) { QOT = QI; QOV = QAir; dPrimaryRecovery = dPrimaryGrade = 0; for (vector<double>::iterator it = vSecondaryRecoveries.begin(); it != vSecondaryRecoveries.end(); it++) *it = 0.0; return; } if (QI.Mass(MP_Sol) > QI.Mass(MP_Liq)) Log.SetCondition(true, LC_BadInput, MMsg_Warning, "Input stream contains more solid than liquid."); if (bAirOn && (QAir.Mass(MP_Sol) > UsableMass || QAir.Mass(MP_Liq) > UsableMass)) Log.SetCondition(true, LC_BadAir, MMsg_Warning, "Air stream contains liquids or solids"); if (bAirOn && QAir.Volume(MP_Gas) < 2 * QI.Volume(MP_Sol)) Log.SetCondition(true, LC_LittleAir, MMsg_Warning, "Air stream contains very little gas"); QI.AddF(QAir, MP_All, 1); //do the work... const int NumSpecies = gs_MVDefn.Count(); //Primaries: double dPrimaryMassConc = 0.0; double dPrimaryElementConc = 0.0; for (unsigned int i = 0; i < vPrimaryIndices.size(); i++) { double temp = QI.M[vPrimaryIndices.at(i)]; dPrimaryMassConc += temp * dReqPrimaryRecovery; if (eSpecType == FTST_ByElement) dPrimaryElementConc += temp * dPrimaryRecovery * ElementMassFrac(vPrimaryIndices.at(i), nPrimary1); else dPrimaryElementConc += temp * dPrimaryRecovery; QOT.M[vPrimaryIndices.at(i)] = temp * (1 - dReqPrimaryRecovery); QOC.M[vPrimaryIndices.at(i)] = temp * dReqPrimaryRecovery; } if (dPrimaryElementConc == 0) { dPrimaryRecovery = dNAN; Log.SetCondition(true, LC_NoPrimaries, MMsg_Warning, "No primary compounds found in feed."); } //Secondaries: double dSecondaryMassConc = 0.0; for (unsigned int i = 0; i < vSecondaryIndices.size(); i++) { double temp = QI.M[vSecondaryIndices.at(i)]; dSecondaryMassConc += temp * vReqSecondaryRecoveries.at(i); QOT.M[vSecondaryIndices.at(i)] = temp * (1 - vReqSecondaryRecoveries.at(i)); QOC.M[vSecondaryIndices.at(i)] = temp * vReqSecondaryRecoveries.at(i); if (eSpecType == FTST_ByElement) dPrimaryElementConc += temp * vReqSecondaryRecoveries.at(i) * ElementMassFrac(vSecondaryIndices.at(i), nPrimary1); } //Those not included in either primaries or secondaries. double dPrimaryElementInGangue = 0; double dOtherMassIn = 0.0; for (unsigned int i = 0; i < vOtherIndices.size(); i++) { dOtherMassIn += QI.M[vOtherIndices.at(i)]; if (eSpecType == FTST_ByElement) dPrimaryElementInGangue += QI.M[vOtherIndices.at(i)] * ElementMassFrac(vOtherIndices.at(i), nPrimary1); } double dPrimaryGangueFrac = dPrimaryElementInGangue / NZ(dOtherMassIn); double dReqOtherMassOut = (dPrimaryElementConc - dReqPrimaryGrade * (dPrimaryMassConc + dSecondaryMassConc)) / NZ(dReqPrimaryGrade - dPrimaryGangueFrac); if (dReqOtherMassOut < 0) //If this is the case, we will put in no more impurities. { if (dPrimaryElementConc / QOC.Mass(MP_Sol) < dReqPrimaryGrade) { Log.SetCondition(true, LC_NoGrade, MMsg_Warning, "Secondary products prevent required primary grade"); dReqOtherMassOut = 0; } else { Log.SetCondition(true, LC_NoGrade, MMsg_Warning, "Grade of gangue to concentrate higher than required grade"); if (dPrimaryElementConc / QOC.Mass(MP_Sol) > dPrimaryGangueFrac) //We will get as close as possible... dReqOtherMassOut = dOtherMassIn; } } if (dReqOtherMassOut > dOtherMassIn) { Log.SetCondition(true, LC_NoGrade, MMsg_Warning, "Not enough unspecified input mass to achieve required primary grade"); dReqOtherMassOut = dOtherMassIn; } for (unsigned int i = 0; i < vOtherIndices.size(); i++) { QOC.M[vOtherIndices.at(i)] = QI.M[vOtherIndices.at(i)] * dReqOtherMassOut / NZ(dOtherMassIn); QOT.M[vOtherIndices.at(i)] = QI.M[vOtherIndices.at(i)] * (1 - dReqOtherMassOut / NZ(dOtherMassIn)); } dPrimaryElementConc += dReqOtherMassOut * dPrimaryGangueFrac; //Finally: We deal with non-solid species: /*for (int i = 0; i < gs_MVDefn.Count(); i++) { if (gs_MVDefn[i].IsSolid()) continue; QOC.M[i] = QI.M[i] * dReqWaterFrac; QOT.M[i] = QI.M[i] * (1 - dReqWaterFrac); }*/ QOC.AddM(QI, MP_Liq, QI.Mass(MP_Liq) * dReqWaterFrac); QOT.AddM(QI, MP_Liq, QI.Mass(MP_Liq) * (1 - dReqWaterFrac)); if (FlwIOs.Count[idVent] > 0) { QOV = QI; QOV.ZeroMass(); QOV.AddM(QI, MP_Gas, QI.Mass(MP_Gas)); } else { QOC.AddM(QI, MP_Gas, QI.Mass(MP_Gas)); if (FlwIOs.Count[idAir] > 0) Log.SetCondition(true, LC_Vent, MMsg_Warning, "Stream connected to air, but no stream connected to vent."); } if (QOC.Mass(MP_Sol) > 0) dPrimaryGrade = dPrimaryElementConc / NZ(QOC.Mass(MP_Sol)); else dPrimaryGrade = dNAN; dSfConcentrate = QOC.MassFrac(MP_Sol); dSfTailings = QOT.MassFrac(MP_Sol); } catch (MMdlException &e) { Log.Message(MMsg_Error, e.Description); } catch (MFPPException &e) { e.ClearFPP(); Log.Message(MMsg_Error, e.Description); } catch (MSysException &e) { Log.Message(MMsg_Error, e.Description); } catch (...) { Log.Message(MMsg_Error, "Some Unknown Exception occured"); } }