//---------------------------------------------------------------------------- int SimplePendulum::Main (int, char**) { msImage = new0 ImageRGB82D(SIZE, SIZE); SolveMethod(ExplicitEuler, "Data/explicit.im", "Data/explicit.txt"); SolveMethod(ImplicitEuler, "Data/implicit.im", "Data/implicit.txt"); SolveMethod(RungeKutta, "Data/runge.im", "Data/runge.txt"); SolveMethod(LeapFrog, "Data/leapfrog.im", "Data/leapfrog.txt"); Stiff1(); Stiff2True(); Stiff2Approximate(); delete0(msImage); return 0; }
void BeltCnv::EvalProducts(CNodeEvalIndex & NEI) { switch (SolveMethod()) { case SM_Direct: if (NoProcessJoins()>=1) Xfer_EvalProducts(0, Joins[0].Pressure(), NULL, NULL, NULL, NULL, NULL); #pragma chFIXIT(Correct conveyor output split in ProBal needs implementing) break; case SM_Inline: case SM_Buffered: { int Prod=0; for (int i=0; i<NoFlwIOs(); i++) if (IOId_Self(i)==idProd) m_Q.SetProductQmEst(Prod++, IOQmEst_Out(i)); m_Q.EvalProducts(); Prod=0; for (i=0; i<NoFlwIOs(); i++) if (IOId_Self(i)==idProd) m_Q.GetProduct(Prod++, IOConduit(i)); } break; } }
flag MN_Xfer::InitialiseSolution() { switch (SolveMethod()) { case SM_Direct: break; case SM_Inline: case SM_Buffered: if (m_NetworkIsolator) m_Accumulator->SetStateAction(IE_SaveState); break; } return 1; };
void MN_Xfer::EvalProducts(CNodeEvalIndex & NEI) { if (NoProcessJoins()>0) { switch (SolveMethod()) { case SM_Direct: if (NoProcessJoins()>0) Xfer_EvalProducts(0, Joins[0].Pressure(), &m_QFeed, &m_QProd, GSM(), &m_PCtrl0, &m_BlkEval); break; case SM_Inline: case SM_Buffered: { if (NoProcessJoins()>=1) Xfer_EvalProducts(0, Joins[0].Pressure(), &m_QFeed, &m_QProd, GSM(), NULL, &m_BlkEval); } break; } } };
void CElectroCell::ConvergeStates(CConvergeStateBlk &CSB) { ASSERT(NetDynamicMethod()); if (SolveMethod()==SM_Buffered) { if (m_RB()) {//only call this code if the surge is actualy used //#if dbgMSurge //if (dbgEvalConverge()) // dbgpln("CElectroCell::ConvergeStates - RB %s", FullObjTag()); //#endif //double TempKFeed=Contents.Temp(); double TotHfAtFeedT_Before = Contents.totHf(som_ALL, m_TempKFeed, Contents.Press()); Contents.SetMeanResTimeCalcsReqd(true); m_RB.ConvergeStates(CSB, Contents, Contents.MeanResTimes()); double TotHfAtFeedT_After = Contents.totHf(som_ALL, m_TempKFeed, Contents.Press()); double emf = TotHfAtFeedT_After-TotHfAtFeedT_Before; emf = (emf / dCellEff);// * -1.0; Contents.Set_totHf(Contents.totHf() + emf); //#if dbgMSurge //if (dbgEvalConverge()) // dbgpln("-------------------------------------- "); //#endif }; if (!IntegralDone() && !GetActiveHold()) m_VLE.VFlash(Contents, 0.0, VLEF_Null);//, NULL, NULL, NULL); m_EqThermals.ConvergeStates(CSB); } else MN_Surge::ConvergeStates(CSB); }
/*#F: This determines what material should leave through each outlet, and rate of change of the contents resulting from the flow, of material, out of each outlet of the surge unit. */ void CElectroCell::EvalProducts(CNodeEvalIndex & NEI) { StkSpConduit Sd("Sd", chLINEID(), this); dCellEff = Range(0.01, dCellEff, 1.0); dPreHeatFrac = Range(0.0, dPreHeatFrac, 1.0); switch (SolveMethod()) { case SM_Direct: { const int iAn = IOWithId_Self(ioidAn); const int iCa = IOWithId_Self(ioidCath); SpConduit & An = *IOConduit(iAn); SpConduit & Ca = *IOConduit(iCa); SigmaQInPMin(Sd(), som_ALL, Id_2_Mask(ioidFd)); m_TempKFeed = Sd().Temp(); const double TotHfAtFeedT_Before = Sd().totHf(som_ALL, m_TempKFeed, Sd().Press()); m_RB.EvalProducts(Sd()); const double TotHfAtFeedT_After = Sd().totHf(som_ALL, m_TempKFeed, Sd().Press()); if (m_RB()) { // Input energy from the electrical work applied to the cell... m_dElecEnergyReact = TotHfAtFeedT_After - TotHfAtFeedT_Before; m_dElecEnergyHeat = (m_dElecEnergyReact/ dCellEff)-m_dElecEnergyReact; m_dElecEnergyTotal = m_dElecEnergyReact + m_dElecEnergyHeat; double dTotHf = Sd().totHf(); Sd().Set_totHf(dTotHf + m_dElecEnergyTotal); //add remaining energy AFTER reaction } else { m_dElecEnergyTotal = 0.0; m_dElecEnergyReact = 0.0; m_dElecEnergyHeat = 0.0; } double Qs = Sd().QMass(som_Sol); double Ql = Sd().QMass(som_Liq); double Qg = Sd().QMass(som_Gas); m_TempKProd = Sd().Temp(); An.QSetM(Sd(), som_Sol, 0.0, Std_P); An.QAddM(Sd(), som_Liq, Ql); Ca.QSetM(Sd(), som_Sol, Qs, Std_P); Ca.QAddM(Sd(), som_Liq, 0.0); //put all vapours (if any) to vent (if present) const int iVent = IOWithId_Self(ioidVent); if (iVent>=0) { SpConduit & Cvent = *IOConduit(iVent); Cvent.QSetM(Sd(), som_Gas, Qg, Std_P); } else { An.QAddM(Sd(), som_Gas, Qg); //put vapours somewhere! } An.SanityCheck(); Ca.SanityCheck(); break; } case SM_Inline: case SM_Buffered: { Contents.ZeroDeriv(); m_TempKFeed=Sd().Temp(); if (SolveMethod()==SM_Inline) { m_RB.EvalProducts(Sd()); } else { //RB.EvalDerivs(Contents, Sd(), Sd()); } if (m_RB()) { // Input energy from the electrical work applied to the cell... m_dElecEnergyReact = m_RB()->HfSumTot(); m_dElecEnergyHeat = (m_dElecEnergyReact/ dCellEff)-m_dElecEnergyReact; m_dElecEnergyTotal = m_dElecEnergyReact + m_dElecEnergyHeat; double dTotHf = Sd().totHf(); Sd().Set_totHf(dTotHf + m_dElecEnergyTotal); //add remaining energy AFTER reaction } else { m_dElecEnergyTotal = 0.0; m_dElecEnergyReact = 0.0; m_dElecEnergyHeat = 0.0; } //double emf = 0.0; //if (RB()) // { // emf = RB()->HfSumTot(); // emf = (emf / dCellEff) * -1.0; // Sd().Set_totHf(Sd().totHf() + emf); // } //dHeatFlow = emf; double SolMass = Contents.Mass(som_Sol); double LiqMass = Contents.Mass(som_Liq); double ETemp = Contents.Temp(); // This prevents any solids leaving with the electrolyte. const int ioAn = IOWithId_Self(ioidAn); if (ioAn>=0) { SpMArray AnolyteFilter; //Rem.Set(Contents, 0.0, 1.0, 0.0); AnolyteFilter.SetToValue(som_Sol, 0.0); AnolyteFilter.SetToValue(som_Liq|som_Gas, 1.0); SetProdMakeup(PMU_IOId | PMU_Filter, ioidAn, AnolyteFilter, ETemp, Std_P, Contents.Model()); } // This allows the metal and solids to leave in the metal outlet. // Solids other than the plated metal leave as sludge. const int ioCa = IOWithId_Self(ioidCath); if (ioCa>=0) { SpMArray MetalFilter; //Met.Set(Contents, 1.0, 0.0, 0.0); MetalFilter.SetToValue(som_Sol, 1.0); MetalFilter.SetToValue(som_Liq|som_Gas, 0.0); SetProdMakeup(PMU_IOId | PMU_Filter, ioidCath, MetalFilter, ETemp, Std_P, Contents.Model()); } SigmaQInPMin(m_QFeed, som_ALL, First64IOIds); EvalProducts_SurgeLevel(SolveInlineMethod(), false, ContentHgtOrd/*, &m_QFeed*/); m_TempKProd=Sd().Temp(); break; } } };
/*This determines what material should leave through each outlet, and rate of change of the contents resulting from the flow, of material, out of each outlet of the surge unit. */ void CentrifugeMB::EvalProducts(CNodeEvalIndex & NEI) { StkSpConduit Sd("Sd", chLINEID(), this); StkSpConduit Wd("Wd", chLINEID(), this); SpConduit Dummy1; SpConduit Dummy2; switch (SolveMethod()) { case SM_Direct: { //RqdSolidsToFiltrate = Range(1.0e-8, RqdSolidsToFiltrate, 0.3); RqdSolidsToFiltrate = Range(0.0, RqdSolidsToFiltrate, 1.0); RqdCakeMoist = Range(0.0, RqdCakeMoist, 0.99); WashEff = Range(0.01, WashEff, 1.0); const int iCl = IOWithId_Self(ioidLiq); const int iCs = IOWithId_Self(ioidSol); SpConduit & Cl = *IOConduit(iCl); SpConduit & Cs = *IOConduit(iCs); const int iWw = IOWithId_Self(ioidwash); const int iW2 = IOWithId_Self(ioidwsh2); const int iFW = IOWithId_Self(ioidFW); const int iSW = IOWithId_Self(ioidSW); SpConduit & QWw = (iWw >= 1 ? *IOConduit(iWw) : Dummy1); SpConduit & QW2 = (iW2 >= 1 ? *IOConduit(iW2) : Dummy2); SigmaQInPMin(QFd, som_ALL, Id_2_Mask(ioidFd)); const bool HasFeed = (QFd.QMass(som_ALL)>UsableMass); const double TtlWashSol = QWw.QMass(som_Sol) + QW2.QMass(som_Sol); //double CFeed = QFd.SpecieConc(QFd.Temp(), iScanEffSpecie, som_Liq); double CFeed = QFd.SpecieConc(QFd.Temp(), iWashEffSpecie, som_Liq); double CWash1 = QWw.SpecieConc(QWw.Temp(), iWashEffSpecie, som_Liq); double CWash2 = QW2.SpecieConc(QW2.Temp(), iWashEffSpecie, som_Liq); bool FeedLiqLow = false; double CCake1 = 0.0; if (fOn/* && HasFeed*/) { m_RB.EvalProducts(QFd); m_EHX.EvalProducts(QFd); const double MSol = QFd.QMass(som_Sol); const double MLiq = QFd.QMass(som_Liq); const double wash1 = QWw.QMass(som_Liq); const double wash2 = QW2.QMass(som_Liq); const double TtlLiqIn = MLiq + wash1 + wash2; //will not achieve requirements if there are any solids in wash!!! const double MSol2Filt = MSol * RqdSolidsToFiltrate; const double RSol = MSol - MSol2Filt; double MLiq2Cake = (RqdCakeMoist * RSol)/(1 - RqdCakeMoist); FeedLiqLow = (TtlLiqIn<MLiq2Cake); SetCI(1, /*fOn && */HasFeed && FeedLiqLow); if (FeedLiqLow) {//force all liquids to cake MLiq2Cake = TtlLiqIn; } const double TotLiq = max(TtlLiqIn - MLiq2Cake, 1e-6); const double Sol2Filt = GEZ(MLiq - MLiq2Cake) * MSol2Filt/TotLiq; Cs.QSetM(QFd, som_Sol, RSol, IOP_Self(iCs)); Cs.QAddM(QWw, som_Sol, QWw.QMass(som_Sol)); Cs.QAddM(QW2, som_Sol, QW2.QMass(som_Sol)); Cl.QSetM(QFd, som_Liq, GEZ(MLiq - MLiq2Cake), IOP_Self(iCl)); Cl.QAddM(QFd, som_Sol, Sol2Filt); //======================================================= if ((MSol > 1e-6) && (MLiq > 1e-6)) { double Cf, Cw1, Cw2, e1, e2, w1, w2; w1 = min(MLiq2Cake*WashEff, wash1); w2 = min(MLiq2Cake*WashEff, wash2); e1 = w1/max(MLiq2Cake,1e-6); e2 = w2/max(MLiq2Cake,1e-6); // The liquid composition of the solids. Cf = MLiq2Cake * (1.0 - e1 - e2 + e1*e2); Cw1 = w1 - w2*e1; Cw2 = w2; Cs.QAddM(QFd, som_Liq, Cf); Cs.QAddM(QWw, som_Liq, Cw1); CCake1 = Cs.SpecieConc(Cs.Temp(), iWashEffSpecie, som_Liq); Cs.QAddM(QW2, som_Liq, Cw2); // The filtrate and washing streams double Ff, Fw1, Fw2; Ff = MLiq2Cake - Cf; Fw1 = wash1 - Cw1; Fw2 = wash2 - Cw2; if (iFW >= 1) { rSpConduit FW =*IOConduit(iFW); if (iSW >= 1) {//more than one washing output stream connected double Sw1, Sw2; rSpConduit SW =*IOConduit(iSW); Sw1 = wash1 * MSol2Filt/TotLiq; Sw2 = wash2 * MSol2Filt/TotLiq; FW.QSetM(QFd, som_Liq, w1, IOP_Self(iFW)); FW.QAddM(QWw, som_Liq, wash1 - w1); FW.QAddM(QFd, som_Sol, Sw1); SW.QSetM(QFd, som_Liq, w2 - w1*e2, IOP_Self(iSW)); SW.QAddM(QWw, som_Liq, w1*e2); SW.QAddM(QW2, som_Liq, Fw2); SW.QAddM(QFd, som_Sol, Sw2); } else // everything goes to the first washings stream { FW.QSetM(QFd, som_Liq, Ff, IOP_Self(iFW)); FW.QAddM(QWw, som_Liq, Fw1); FW.QAddM(QW2, som_Liq, Fw2); FW.QAddM(QFd, som_Sol, MSol2Filt - Sol2Filt); } } else // everything goes to the filtrate { Cl.QAddM(QFd, som_Liq, Ff); Cl.QAddM(QWw, som_Liq, Fw1); Cl.QAddM(QW2, som_Liq, Fw2); Cl.QAddM(QFd, som_Sol, MSol2Filt - Sol2Filt); } } else { Cs.QAddM(QFd, som_Sol, MSol2Filt); Cl.QAddM(QFd, som_Liq, MLiq2Cake); Cl.QAddM(QWw, som_Liq, wash1); Cl.QAddM(QW2, som_Liq, wash2); } } else {//off SigmaQInPMin(Cs, som_ALL, Id_2_Mask(ioidFd)); SigmaQInPMin(Cl, som_ALL, Id_2_Mask(ioidwash)|Id_2_Mask(ioidwsh2)); if (iFW >= 1) { rSpConduit FW =*IOConduit(iFW); FW.QZero(); } if (iSW >= 1) { rSpConduit SW =*IOConduit(iSW); SW.QZero(); } } ActCakeLiq = Cs.MassFrac(som_Liq); ActCakeSolids = Cs.MassFrac(som_Sol); ActFiltSolids = Cl.MassFrac(som_Sol); ActFiltSolConc = Cl.PhaseConc(C_2_K(25.0), som_Sol, som_ALL); ActFiltSolConcT = Cl.PhaseConc(Cl.Temp(), som_Sol, som_ALL); //double Cuf = Cs.SpecieConc(Cs.Temp(), iScanEffSpecie, som_Liq); //double Cof = Cl.SpecieConc(Cl.Temp(), iScanEffSpecie, som_Liq); //ActScandrettEff = (CFeed - Cuf)/NZ(CFeed - Cof); const double CCake2 = Cs.SpecieConc(Cs.Temp(), iWashEffSpecie, som_Liq); dSpWashEff = (CFeed - CCake1)/NZ(CFeed - CWash1); dSpWashEff2 = (CFeed - CCake2)/NZ(CFeed - CWash2); const double CMErr = fabs(ActCakeLiq - RqdCakeMoist); //CakeMoisture error const bool SolInWashErr = (CMErr > 1.0e-5 && TtlWashSol>1.0e-6); SetCI(2, fOn && /*bTrackStatus && */HasFeed && !FeedLiqLow && !SolInWashErr && CMErr > 1.0e-5); SetCI(3, fOn && /*bTrackStatus && */HasFeed && SolInWashErr); break; } case SM_Inline: case SM_Buffered: { Contents.ZeroDeriv(); //double CFeed = Sd().SpecieConc(Sd().Temp(), iScanEffSpecie, som_Liq); //double CFeed = Sd().SpecieConc(Sd().Temp(), iWashEffSpecie, som_Liq); //double CWash1 = QWw.SpecieConc(QWw.Temp(), iWashEffSpecie, som_Liq); //double CWash2 = QW2.SpecieConc(QW2.Temp(), iWashEffSpecie, som_Liq); m_RB.EvalProducts(Sd()); m_EHX.EvalProducts(Sd()); double SolMass = Contents.Mass(som_Sol); double LiqMass = Contents.Mass(som_Liq); //RqdSolidsToFiltrate = Range(1.0e-8, RqdSolidsToFiltrate, 0.3); RqdSolidsToFiltrate = Range(0.0, RqdSolidsToFiltrate, 1.0); RqdCakeMoist = Range(0.0, RqdCakeMoist, 1.0); double CakeSol, LiqSol; LiqSol = RqdSolidsToFiltrate / GTZ(1.0 - RqdSolidsToFiltrate); CakeSol = (1.0 - RqdCakeMoist) / GTZ(RqdCakeMoist); if (SolMass>1.0e-12 && LiqMass>1.0e-12) { SetProdMakeup(PMU_IOId | PMU_SLRatio, ioidLiq, Contents, LiqSol); SetProdMakeup(PMU_IOId | PMU_SLRatio, ioidSol, Contents, CakeSol); } else { // Just Operate as a Tank } SigmaQInPMin(m_QFeed, som_ALL, First64IOIds); EvalProducts_SurgeLevel(SolveInlineMethod(), false, ContentHgtOrd/*, &m_QFeed*/); if (NoProcessJoins()>=1) Xfer_EvalProducts(0, Joins[0].Pressure(), NULL, &m_QProdSrg, NULL, NULL, NULL); ActCakeLiq = IOConduit(IOWithId_Self(ioidSol))->MassFrac(som_Liq); ActCakeSolids = IOConduit(IOWithId_Self(ioidSol))->MassFrac(som_Sol); ActFiltSolids = IOConduit(IOWithId_Self(ioidSol))->MassFrac(som_Sol); ActFiltSolConc = IOConduit(IOWithId_Self(ioidSol))->PhaseConc(C_2_K(25.0), som_Sol, som_ALL); ActFiltSolConcT = IOConduit(IOWithId_Self(ioidSol))->PhaseConc(IOConduit(IOWithId_Self(ioidSol))->Temp(), som_Sol, som_ALL); //double Cuf = IOConduit(IOWithId_Self(ioidSol))->SpecieConc(IOConduit(IOWithId_Self(ioidSol))->Temp(), iScanEffSpecie, som_Liq); //double Cof = IOConduit(IOWithId_Self(ioidSol))->SpecieConc(IOConduit(IOWithId_Self(ioidSol))->Temp(), iScanEffSpecie, som_Liq); //ActScandrettEff = (CFeed - Cuf)/NZ(CFeed - Cof); break; } } }
void CCWasher::EvalProducts(CNodeEvalIndex & NEI) { switch (SolveMethod()) { case SM_Direct: { const int ioUFlw = IOWithId_Self(ioidUFlw); const int ioOFlw = IOWithId_Self(ioidOFlw); SpConduit & Qu = *IOConduit(ioUFlw); SpConduit & Qo = *IOConduit(ioOFlw); Qm.QZero(); Qw.QZero(); SigmaQInPMin(Qm, som_SL, Id_2_Mask(ioidMud)); SigmaQInPMin(Qw, som_SL, Id_2_Mask(ioidWWater)|Id_2_Mask(ioidSStream)); double CFeed = Qm.SpecieConc(Qm.Temp(), iScanEffSpecie, som_Liq); flag HasFlwIn = (Qm.QMass()>UsableMass || Qw.QMass()>UsableMass); if (fOn && HasFlwIn) { Qt.QZero(); // Select Model Transfer Qu.SelectModel(&Qm, False); Qo.SelectModel(&Qm, False); ScandrettEff = Range(0.0, ScandrettEff, 1.0); MixEff = Range(0.0, MixEff, 1.0); RqdUFSolidsConc25 = Range(1.0, RqdUFSolidsConc25, 5000.0); RqdUFSolids = Range(0.1, RqdUFSolids, 1.0); Reqd_UFlowSolidsCorr = 0.0; double Cf = Qm.SpecieConc(Qm.Temp(), iScanEffSpecie, som_Liq); double Cw = Qw.SpecieConc(Qw.Temp(), iScanEffSpecie, som_Liq); m_RB.EvalProducts(Qm); m_EHX.EvalProducts(Qm); //double Qg = Qm.QMass(som_Gas); //Qg += Qw.QMass(som_Gas); //TODO CC Washer may NOT work for some reactions! double UFTemp = (SA ? RqdUFSolidsConc25 : RqdUFSolids); //double POut = AtmosPress(); //force outlet to Atmos P double POut = Std_P; //force outlet to Std_P if (iEffMethod==MEM_Scandrett) { CCWashEffFnd WEF(Cf, Cw, Qm, Qw, Qu, Qo, Qt, UFTemp, 0.0, Reqd_UFlowSolidsCorr, SA, iScanEffSpecie, POut); WEF.SetTarget(ScandrettEff); if (Valid(ByPassGuess)) { WEF.SetEstimate(ByPassGuess, -1.0); ByPassGuess = dNAN; } flag Ok = false; int iRet=WEF.Start(0.0, 1.0); if (iRet==RF_EstimateOK) //estimate is good, solve not required { ByPassGuess = WEF.Result(); Ok = true; } else { if (iRet==RF_BadEstimate) iRet = WEF.Start(0.0, 1.0); // Restart if (iRet==RF_OK) if (WEF.Solve_Brent()==RF_OK) { ByPassGuess = WEF.Result(); Ok = true; } } SetCI(1, !Ok); } else //if (iEffMethod==MEM_Mixing) { CCWashMixEffFnd WF(Qm, Qw, Qu, Qo, Qt, MixEff, SA, POut); WF.SetTarget(UFTemp); if (Valid(WashByPassGuess)) { WF.SetEstimate(WashByPassGuess, -1.0); WashByPassGuess = dNAN; } flag Ok = false; int iRet=WF.Start(WF.LoLimit, 1.0); if (iRet==RF_EstimateOK) //estimate is good, solve not required { WashByPassGuess = WF.Result(); Ok = true; } else { if (iRet==RF_BadEstimate) iRet = WF.Start(WF.LoLimit, 1.0); // Restart if (iRet==RF_OK) if (WF.Solve_Brent()==RF_OK) { WashByPassGuess = WF.Result(); Ok = true; } } if (SA) { SetCI(2, !Ok); ClrCI(3); } else { ClrCI(2); SetCI(3, !Ok); } } //put all vapours (if any) to vent (if present) const int iVent = IOWithId_Self(ioidVent); if (iVent>=0) { SpConduit & Cvent = *IOConduit(iVent); SigmaQInPMin(Cvent, som_Gas, Id_2_Mask(ioidMud)|Id_2_Mask(ioidWWater)|Id_2_Mask(ioidSStream)); double Qg = Qm.QMass(som_Gas); Cvent.QAddM(Qm, som_Gas, Qg); Qg = Qw.QMass(som_Gas); Cvent.QAddM(Qw, som_Gas, Qg); } } else { Qo.QZero(); Qu.QZero(); const int iVent = IOWithId_Self(ioidVent); if (iVent>=0) { SpConduit & Cvent = *IOConduit(iVent); SigmaQInPMin(Cvent, som_Gas, Id_2_Mask(ioidMud)|Id_2_Mask(ioidWWater)|Id_2_Mask(ioidSStream)); SigmaQInPMin(Qu, som_SL, Id_2_Mask(ioidMud)); SigmaQInPMin(Qo, som_SL, Id_2_Mask(ioidWWater)|Id_2_Mask(ioidSStream)); } else { SigmaQInPMin(Qu, som_ALL, Id_2_Mask(ioidMud)); SigmaQInPMin(Qo, som_ALL, Id_2_Mask(ioidWWater)|Id_2_Mask(ioidSStream)); } } UFCaustic = Qu.SpecieConc(Qu.Temp(), iScanEffSpecie, som_Liq); OFCaustic = Qo.SpecieConc(Qo.Temp(), iScanEffSpecie, som_Liq); //double solid = Qu.QMass(som_Sol); //double Totmass = Max(1e-6, Qu.QMass(som_ALL)); //UFSolids = solid/Totmass; UFSolids = Qu.MassFrac(som_Sol); //OFSolids = Qo.MassFrac(som_Sol); ActUFSolidsConc25 = Qu.PhaseConc(C_2_K(25.0), som_Sol, som_ALL); //ActOFSolidsConc25 = Qo.PhaseConc(C_2_K(25.0), som_Sol, som_ALL); double Cuf = Qu.SpecieConc(Qu.Temp(), iScanEffSpecie, som_Liq); double Cof = Qo.SpecieConc(Qo.Temp(), iScanEffSpecie, som_Liq); ActScandrettEff = (CFeed - Cuf)/NZ(CFeed - Cof); break; } default: MN_Surge::EvalProducts(NEI); } }
void CoolingTower::EvalProducts(CNodeEvalIndex & NEI) { switch (SolveMethod()) { case SM_Direct: { const int wi = H2OLiq(); const int si = H2OVap(); const int ioLiq = IOWithId_Self(ioid_Liq); const int ioVap = IOWithId_Self(ioid_Vap); const int ioLoss = IOWithId_Self(ioid_LiqLoss); const int ioDrift = IOWithId_Self(ioid_DriftLoss); SpConduit & Ql=*IOConduit(ioLiq); SpConduit & Qv=*IOConduit(ioVap); StkSpConduit QMix("Mix", chLINEID(), this); iCycles = Max(2L, iCycles); dLGRatio = Range(0.01, dLGRatio, 10.0); ClrCI(3); ClrCI(4); ClrCI(5); ClrCI(6); dDuty = 0.0; SigmaQInPMin(QMix(), som_ALL, Id_2_Mask(ioid_Feed)); Ql.QCopy(QMix()); Qv.QCopy(QMix()); const double QmWaterLiqIn = QMix().VMass[wi]; const double QmWaterVapIn = QMix().VMass[si]; const double QmVapIn = QMix().QMass(som_Gas); dQmIn = QMix().QMass(som_ALL); const flag HasFlw = (dQmIn>UsableMass); dTempKFeed = QMix().Temp(); const double TotHfAtFeedT_Before = QMix().totHf(som_ALL, dTempKFeed, QMix().Press()); //RB.EvalProducts(QMix); //EHX.EvalProducts(QMix); //double POut = AtmosPress(); //force outlet to Atmos P double POut = Std_P; //force outlet to Std_P //double AvgCellAgeY=AvgCellAge/(365.25*24.0*60.0*60.0); //double TimeSinceDescaleM=TimeSinceDescale/(365.25*24.0*60.0*60.0/12.0); //double RqdLiqTemp = AirWetBulbT + Approach + AvgCellAgeY*1.1 + TimeSinceDescaleM/12.0*4.0; double RqdLiqTemp = dAirWetBulbT + dApproachT; double T1 = QMix().Temp(); double T2 = RqdLiqTemp; dLossQm = 0.0; bool ValidData; switch (iMethod) { case CTM_Simple: ValidData = (RqdLiqTemp<T1); break; case CTM_Merkel: ValidData = (iMerkelCalcType==MCT_TOut ? (dAirWetBulbT<T1) : (RqdLiqTemp<T1)); break; } if (!HasFlw) ValidData = false; double RqdLiqTempUsed; if (ValidData) { m_VLE.SetHfInAtZero(QMix()); if (iMethod==CTM_Simple) { //const double h1 = QMix().totHf(); RqdLiqTempUsed = RqdLiqTemp; EvapFnd EF(QMix(), RqdLiqTempUsed, POut, m_VLE);//QMix().Press()); EF.SetTarget(QMix().totHf()); if (Valid(dEvapFrac)) { EF.SetEstimate(dEvapFrac, 1.0); //dEvapFrac = dNAN; } flag Ok = false; dMaxEvapFrac = Range(0.01, dMaxEvapFrac, 1.0); int iRet=EF.Start(0.0, dMaxEvapFrac); if (iRet==RF_EstimateOK) //estimate is good, solve not required { Ok = true; } else { if (iRet==RF_BadEstimate) iRet = EF.Start(0.0, dMaxEvapFrac); // Restart if (iRet==RF_OK) if (EF.Solve_Brent()==RF_OK) Ok = true; } dEvapFrac = EF.Result(); //use result regardless if (!Ok) { SigmaQInPMin(QMix(), som_ALL, Id_2_Mask(ioid_Feed)); m_VLE.SetSatPVapFrac(QMix(), dEvapFrac, 0); QMix().SetPress(POut); RqdLiqTempUsed = QMix().Temp(); } //const double h2 = QMix().totHf(); //dDuty = h1-h2; this gives 0 (as expected) //dDuty is zero because there is no heattransfer with the air } else { dAirCp = Max(0.000001, dAirCp); dAirWetBulbT = Range(MerkelTMn, dAirWetBulbT, MerkelTMx-10.0); if (T1<MerkelTMn || T1>MerkelTMx) { SetCI(6, true); T1 = Range(MerkelTMn, T1, MerkelTMx); } MerkelTempFnd Fnd(QMix(), *this, T1); if (iMerkelCalcType==MCT_KaVL) { if (T2<MerkelTMn || T2>MerkelTMx) { SetCI(5, true); T2 = Range(MerkelTMn, T2, MerkelTMx); RqdLiqTemp = T2; } dKaVL = Fnd.Function(T2); } else { Fnd.SetTarget(dKaVL); //Note that for high LG_Ratio (eg>1.0) then ApproachT is higher. double Mn = dAirWetBulbT+(T1-dAirWetBulbT)*0.005; const double Mx = dAirWetBulbT+(T1-dAirWetBulbT)*0.999;//T1-0.001; if (Valid(RqdLiqTemp) && RqdLiqTemp>Mn && RqdLiqTemp<Mx) { Fnd.SetEstimate(RqdLiqTemp, 1.0); //RqdLiqTemp = dNAN; } flag Ok = false; int iRet=Fnd.Start(Mn, Mx); if (iRet==RF_EstimateOK) //estimate is good, solve not required { Ok = true; } else { double KaVL_MnTest = Fnd.Function(Mn); if (KaVL_MnTest<0.0) { //Crude fix to find min temp that doesn't cause KaV/L to be negative... double fr = 0.01; while (KaVL_MnTest<0.0 && fr<0.9) { Mn = dAirWetBulbT+(T1-dAirWetBulbT)*fr; KaVL_MnTest = Fnd.Function(Mn); fr += 0.01; } iRet = Fnd.Start(Mn, Mx); // Restart } if (iRet==RF_OK) if (Fnd.Solve_Brent()==RF_OK) Ok = true; } RqdLiqTemp = Fnd.Result(); //use result regardless T2 = RqdLiqTemp; if (!Ok) { const double KaVL_Calc = Fnd.Function(T2); SetCI(3, fabs(KaVL_Calc-dKaVL)>1.0e-6); } dApproachT = RqdLiqTemp - dAirWetBulbT; } dEvapFactor = Min(dEvapFactor, 0.1); //prevent user from puting a silly large number for this dEvapLossQm = dQmIn * dEvapFactor * (C2F(T1)-C2F(T2));//Evaporation Loss: WLe = Wc * EvapFactor * dT dEvapLossQm = Min(dEvapLossQm, QmWaterLiqIn); //limit the amount that can be evaporated RqdLiqTempUsed = RqdLiqTemp; dEvapFrac = Min(dMaxEvapFrac, (dEvapLossQm+QmWaterVapIn)/GTZ(QmWaterLiqIn+QmWaterVapIn)); const double h1 = QMix().totHf(); m_VLE.SetSatPVapFrac(QMix(), dEvapFrac, 0); QMix().SetPress(POut); QMix().SetTemp(RqdLiqTempUsed); const double h2 = QMix().totHf(); dDuty = h1-h2; //dAirEnthOut = AirEnth(T2); dAirEnthOut = Fnd.h2 / 0.430210432; //convert from Btu/lb to kJ/kg dAirQmIn = dQmIn/dLGRatio; dAirTRise = dDuty/GTZ(dAirQmIn)/dAirCp; dAirTOut = dAirDryBulbT + dAirTRise; dAirMixQm = dAirQmIn + dEvapLossQm; const double EvapLossCp = Qv.msCp(); dAirMixCp = dAirQmIn/GTZ(dAirMixQm)*dAirCp + dEvapLossQm/GTZ(dAirMixQm)*EvapLossCp; dAirMixT = dAirQmIn/GTZ(dAirMixQm)*dAirTOut + dEvapLossQm/GTZ(dAirMixQm)*T2; } double QmWaterVapOut = QMix().VMass[si]; dQmWaterEvap = Max(0.0, QmWaterVapOut - QmWaterVapIn); if (iMethod==CTM_Simple) dEvapLossQm=dQmWaterEvap; switch (iLossMethod) { case WLM_None: dDriftLossQm = 0.0; dBlowdownLossQm = 0.0; dLossQm = 0.0; break; case WLM_Frac: dRqdDriftLossFrac = Range(0.0, dRqdDriftLossFrac, 1.0); dRqdLossFrac = Range(0.0, dRqdLossFrac, 0.9); dLossQm = dQmIn * dRqdLossFrac; dDriftLossQm = dLossQm * dRqdDriftLossFrac; dBlowdownLossQm = dLossQm - dDriftLossQm; break; case WLM_Qm: dRqdDriftLossFrac = Range(0.0, dRqdDriftLossFrac, 1.0); dLossQm = dRqdLossQm; dDriftLossQm = dLossQm * dRqdDriftLossFrac; dBlowdownLossQm = dLossQm - dDriftLossQm; break; case WLM_DriftBlowdown: dDriftLossQm = dQmIn * dDriftLossFrac;//Drift Loss: WLd = % of water flow dBlowdownLossQm = dEvapLossQm/(iCycles-1);//Blowdown Loss: WLb = WLe / (cycles - 1) dLossQm = dDriftLossQm+dBlowdownLossQm; break; } if (dLossQm>dQmIn-dEvapLossQm-QmVapIn) { SetCI(4, true); dLossQm = dQmIn-dEvapLossQm-QmVapIn; } m_VLE.AddHfOutAtZero(QMix()); } else { RqdLiqTempUsed = T1; dEvapFrac = 0.0; dLossQm = 0.0; dDriftLossQm = 0.0; dBlowdownLossQm = 0.0; dEvapLossQm = 0.0; dQmWaterEvap = 0.0; //if (iMethod==CTM_Merkel) { dAirEnthOut = TotHfAtFeedT_Before / 0.430210432; //convert from Btu/lb to kJ/kg dAirQmIn = 0.0; dAirTRise = 0.0; dAirTOut = dTempKFeed; dAirMixQm = 0.0; dAirMixCp = 0.0; dAirMixT = dTempKFeed; } } //QMix.ChangeModel(&SMSteamClass); const double TotHfAtFeedT_After = QMix().totHf(som_ALL, dTempKFeed, QMix().Press()); Qv.QSetF(QMix(), som_Gas, 1.0); Qv.SetPress(POut); Qv.SetTemp(RqdLiqTempUsed); const double Qsl = QMix().QMass(som_SL); if (ioLoss<0) { if (ioDrift<0) { Ql.QSetF(QMix(), som_SL, 1.0); Ql.SetPress(POut); Ql.SetTemp(RqdLiqTempUsed); } else { SpConduit & Qdrift=*IOConduit(ioDrift); const double f = dDriftLossQm/GTZ(Qsl); Ql.QSetF(QMix(), som_SL, 1.0-f); Ql.SetPress(POut); Ql.SetTemp(RqdLiqTempUsed); Qdrift.QCopy(QMix()); Qdrift.QSetF(QMix(), som_SL, f); Qdrift.SetPress(POut); Qdrift.SetTemp(RqdLiqTempUsed); } } else { SpConduit & Qloss=*IOConduit(ioLoss); const double f = dLossQm/GTZ(Qsl); Ql.QSetF(QMix(), som_SL, 1.0-f); Ql.SetPress(POut); Ql.SetTemp(RqdLiqTempUsed); if (ioDrift<0) { Qloss.QCopy(QMix()); Qloss.QSetF(QMix(), som_SL, f); Qloss.SetPress(POut); Qloss.SetTemp(RqdLiqTempUsed); } else { const double fd = dDriftLossQm/GTZ(Qsl); const double fl = f - fd; SpConduit & Qdrift=*IOConduit(ioDrift); Qdrift.QCopy(QMix()); Qdrift.QSetF(QMix(), som_SL, fd); Qdrift.SetPress(POut); Qdrift.SetTemp(RqdLiqTempUsed); Qloss.QCopy(QMix()); Qloss.QSetF(QMix(), som_SL, fl); Qloss.SetPress(POut); Qloss.SetTemp(RqdLiqTempUsed); } } //results... dTotalLossQm = dLossQm+dEvapLossQm; dHeatFlow = TotHfAtFeedT_After - TotHfAtFeedT_Before; //what exactly is this??? dFinalP = Ql.Press(); dFinalT = Ql.Temp(); dTempDrop = T1 - dFinalT; SetCI(1, HasFlw && RqdLiqTemp>T1); SetCI(2, HasFlw && RqdLiqTempUsed>RqdLiqTemp); break; } default: MN_Surge::EvalProducts(NEI); } }