double CCWashMixEffFnd::Function(double x) {//x is fraction of wash to overflow //1) All solids go to underflow //2) Try meet user requirements for UF conc/liqFrac by adjusting wash fraction to overflow //3) Of the liquids reporting to the UF, we want (100%-RqdMixEff%) of liquids to retain mud liquids composition, // the rest is the wash liquids. Qu.QSetF(Qm, som_Sol, 1.0, POut); Qu.QAddF(Qw, som_Sol, 1.0); const double WashLiqToUF = (1.0 - x)*QwLiq; const double RqdMudLiqToUF = WashLiqToUF*(1.0/GTZ(RqdMixEff)-1.0); const double RqdMudFracToUF = Min(1.0, RqdMudLiqToUF/GTZ(QmLiq)); Qu.QAddF(Qm, som_Liq, RqdMudFracToUF); Qu.QAddF(Qw, som_Liq, (1.0 - x)); Qo.QSetF(Qm, som_Liq, (1.0-RqdMudFracToUF), POut); Qo.QAddF(Qw, som_Liq, x); if (1) {// Correct Enthalpy... Qu.SetTemp(FT); Qo.SetTemp(FT); double P = POut; double H = Qu.totHf()+Qo.totHf(); double dT = 0.0, H0, T0; for (int Hiter=0; Hiter<10; Hiter++) { if (ConvergedVV(HTot, H, 1.0e-12, 1.0e-12)) break; if (dT!=0.0) dT = (HTot-H)*(FT-T0)/NZ(H-H0); else dT = 0.1; T0 = FT; H0 = H; FT += dT; H = Qu.totHf(som_ALL, FT, P)+Qo.totHf(som_ALL, FT, P); } Qu.SetTemp(FT); Qo.SetTemp(FT); } if (SA) // Solids expressed as g/L. { double SolConc25 = Qu.PhaseConc(C_2_K(25.0), som_Sol, som_ALL); return SolConc25; } else // Solids expressed as % w/w. { double solid = Qu.QMass(som_Sol); double Totmass = Max(1e-6, Qu.QMass(som_ALL)); double SolUF = solid/Totmass; return SolUF; } }
DuctUnit::DuctUnit(pTagObjClass pClass_, pchar TagIn, pTaggedObject pAttach, TagObjAttachment eAttach) : MN_Surge(pClass_, TagIn, pAttach, eAttach) { AttachIOAreas(DuctUnitIOAreaList); Contents.SetClosed(False); Contents.SetPreset(&m_Preset, &m_PresetImg); FlashSplit=0.1; SS_Lvl=0.25; dTInLowest=C_2_K(20.0); dTInHighest=C_2_K(1000.0); dRqdTOutMin=C_2_K(20.0); dRqdTOutMax=C_2_K(1000.0); dRqdTOut=dNAN; dRqddT=dNAN; dStructMass=0.0; dStructTemp=C_2_K(25.0); dStructUA=1000.0; dStructCp=0.12; dAmbientTemp=C_2_K(25.0); dHeatXferCoeff=0.0; dHeatLossRate=0.0; dRqdH2OVapFrac=dNAN; dQmSink=0.0; dQmSinkMeas=dNAN; dQmSinkTau=2.0; };
virtual double RefTemp() { return C_2_K(0.0); };
void FilterPress::EvalProducts() { try { int idPressNote = 0, idWashNote = 1; MStreamI QFeed; FlwIOs.AddMixtureIn_Id(QFeed, idFeed); const double dFeedSolidMass = QFeed.Mass(MP_Sol); const double dFeedLiquidMass = QFeed.Mass(MP_Liq); MStreamI QWashWater; const bool bWashWaterConnected = (FlwIOs.getCount(idWash) > 0); if (bWashWaterConnected) FlwIOs.AddMixtureIn_Id(QWashWater, idWash); //MStream QUnwashedCake = QFeed; BAD idea, QUnwashedCake becomes a reference to QFeed!!!! MStreamI QUnwashedCake; QUnwashedCake = QFeed; QUnwashedCake.ZeroMass(); MStream& QFiltrate = FlwIOs[FlwIOs.First[idFiltrate]].Stream; QFiltrate = QFeed; MStream& QCake = FlwIOs[FlwIOs.First[idCake]].Stream; QCake = QFeed; const bool bWashingsConnected = (FlwIOs.Count[idWashings] > 0); //First off: The filtrate & UnwashedCake: /*Equations Solved using Mathematica 6.0, under the constrainsts of: - Conservation of mass - Cake Moisture Content - Filtrate Solid Concentration [Either in % or g/L] - When using g/L, assume solid and liquid densities don't change [The (Aq) conc's don't] */ double dSolConcConstFac, dLiqConcConstFac; bool bTooWet; switch (eFiltrateMethod) { case FM_SolidsToFiltrateFrac: dSolConcConstFac = dReqSolidsToFiltrate; dLiqConcConstFac = dReqSolidsToFiltrate; bTooWet = dFeedSolidMass / NZ(dFeedSolidMass + dFeedLiquidMass) < dReqSolidsToFiltrate; break; case FM_FiltrateConc: dSolConcConstFac = dReqFiltSolConc / QFeed.Density(MP_Sol, C_2_K(25)); dLiqConcConstFac= dReqFiltSolConc / QFeed.Density(MP_Liq, C_2_K(25)); bTooWet = dFeedSolidMass / NZ(QFeed.Volume(MP_SL, C_2_K(25))) < dReqFiltSolConc; break; } /*Situations where variables can be out of range: - Mass comes in too wet - required loss causes all mass to be sent to filtrate - Mass comes in too dry - mass is drier than required moisture frac. */ double dFiltSolidFactor, dFiltLiquidFactor, dCakeSolidFactor, dCakeLiquidFactor; if (bTooWet) { SetNote(idPressNote, "Input feed too wet: All feed solids sent to filtrate"); dFiltSolidFactor = dFiltLiquidFactor = 1; dCakeSolidFactor = dCakeLiquidFactor = 0; } else if (dFeedLiquidMass / NZ(dFeedSolidMass + dFeedLiquidMass) < dReqCakeMoisture) { SetNote(idPressNote, "Input feed too dry: All feed liquids sent to Cake"); dFiltSolidFactor = dFiltLiquidFactor = 0; dCakeSolidFactor = dCakeLiquidFactor = 1; } else { dFiltSolidFactor = (((dFeedLiquidMass * (dReqCakeMoisture - 1) + dFeedSolidMass * dReqCakeMoisture) * dLiqConcConstFac) / NZ(dReqCakeMoisture * dLiqConcConstFac - (dReqCakeMoisture - 1)*(dSolConcConstFac-1))) / dFeedSolidMass; dFiltLiquidFactor = - (((dFeedLiquidMass * (dReqCakeMoisture - 1) + dFeedSolidMass * dReqCakeMoisture) * (dSolConcConstFac - 1)) / NZ(dReqCakeMoisture * dLiqConcConstFac - (dReqCakeMoisture - 1)*(dSolConcConstFac - 1))) / dFeedLiquidMass; dCakeSolidFactor = 1 - dFiltSolidFactor; dCakeLiquidFactor = 1 - dFiltLiquidFactor; } for (int i = 0; i < gs_MVDefn.Count(); i++) if (gs_MVDefn[i].IsSolid()) { QFiltrate.M[i] = QFeed.M[i] * dFiltSolidFactor; QUnwashedCake.M[i] = QFeed.M[i] * dCakeSolidFactor; } else if (gs_MVDefn[i].IsLiquid()) { QFiltrate.M[i] = QFeed.M[i] * dFiltLiquidFactor; QUnwashedCake.M[i] = QFeed.M[i] * dCakeLiquidFactor; } //Now, wash it: //First, add the washwater solids, and record what mass of solids we add. //The equations associated with this are exceedingly ugly. if (FlwIOs.getCount(idWash) > 0) { //Short variables to create fewer errors translating the equations double WS = QWashWater.Mass(MP_Sol); double WL = QWashWater.Mass(MP_Liq); double CS = QUnwashedCake.Mass(MP_Sol); double CL = QUnwashedCake.Mass(MP_Liq); double a, rWS, rWL, rCS, rCL; double m = dReqCakeMoisture; double w; switch (eWashMethod) { case WM_ConstantEfficiency: w = dReqWashEfficiency; case WM_WashRatio: dWashRatio = WL / NZ(CL); w = 1 - pow(1-dReqWashEfficiency, dWashRatio); } switch (eFiltrateMethod) { case FM_SolidsToFiltrateFrac: a = dReqSolidsToFiltrate; rWS = rWL = rCS = rCL = 1; break; case FM_FiltrateConc: a = dReqFiltSolConc; rWS = QWashWater.Density(MP_Sol, C_2_K(25)); rWL = QWashWater.Density(MP_Liq, C_2_K(25)); rCS = QUnwashedCake.Density(MP_Sol, C_2_K(25)); rCL = QUnwashedCake.Density(MP_Liq, C_2_K(25)); break; } /*Possible situations where variables are out of range: - A: Too much wash liquid - all solids are sent out in washings ['Fix' by sending everyhing to washings. Assume we will not end up with only washing solids] - B: Not enough wash liquid - required wash efficiency impossible ['Fix' by putting all wash water in cake] - C: Cake comes in dry [And very dry] - wash efficiency is higher than requested even if no cake moisture is displaced. [Fix by sending everything]. - D: Wash water has FAAR too many solids - cake moisture is lower than required. [Fix by putting everything into cake] - Cake should never come in too wet. */ //CO - Cake Out. WO - Washings out. double dCLtoCO, dCStoCO, dWStoCO; double dTemp = (WS*rCL*rCS*rWL*(a-rWS)+(a*WL*rCL*rCS+(CS*rCL*(a-rCS)+a*CL*rCS)*rWL)*rWS) / NZ(CS*(m*a*(1-w)*rCS*rWL+rCL*((1-m)*a*rWL+rCS*(m*a*w-(1-m)*rWL)))*rWS + WS*rCL*rCS*((1-m)*rWL*(a-rWS)+m*a*rWS)); double dWLtoCO = m * (w*CS + WS) * dTemp; if (dWLtoCO > WL) { //Case B - not enough wash liquid SetNote(idWashNote, "Not enough wash water: All wash water sent to cake"); //double dTemp1 = (WS*rCL*rCS*(a-rWS)+(CS*rCL*(a-rCS)+a*CL*rCS)*rWS) // / NZ(CS*((1-m)*rCL*(a-rCS)+m*a*rCS)*rWS + WS*rCS*((1-m)*rCL*(a-rWS) + m*a*rWS)); double dTemp1 = 1 / (CS * ((1-m) * rCL * (a-rCS) + m*a*rCS) * rWS + WS * rCS * ((1-m) * rCL * (a-rWS) + m*a*rWS)); double dTemp2 = WS * rCL * rCS * (a-rWS) + (CS * rCL * (a-rCS) + a*(CL+WL)*rCS)*rWS; dWLtoCO = WL; dCLtoCO = (m*CS*CS*rCL*(a-rCS)*rWS + CS*((-(1-m)*WL*rCL*(a-rCS) + m*a*CL*rCS)*rWS+m*WS*rCL*(rCS*(a-2*rWS)+a*rWS)) + WS*rCS*(-(1-m)*WL*rCL*(a-rWS) + m*(WS*rCL*(a-rWS) + a*CL*rWS))) * dTemp1; dCStoCO = (1-m) * CS * dTemp1 * dTemp2; dWStoCO = (1-m) * WS * dTemp1 * dTemp2; } else { //dWLtoCO already set. dCLtoCO = m * (1-w) * CS * dTemp; dCStoCO = (1-m) * CS * dTemp; dWStoCO = (1-m) * WS * dTemp; } if (dCLtoCO > CL) { //Case C - not enough cake moisture [Can only happen if cake comes in with moisture content v. low.] dCLtoCO = CL; //Although this results in a lower than required moisture content, well, the cake is comming in drier than required. SetNote(idWashNote, "Cake too dry"); } //Simple Cons of Mass: double dWLtoWO = WL - dWLtoCO; double dCLtoWO = CL - dCLtoCO; double dWStoWO = WS - dWStoCO; double dCStoWO = CS - dCStoCO; double dWLtoCOFrac,dCLtoCOFrac,dWStoCOFrac,dCStoCOFrac,dWLtoWOFrac,dCLtoWOFrac,dWStoWOFrac,dCStoWOFrac; if (dCStoCO < 0) //Case A, Too wet: Everything sent with washings { SetNote(idWashNote, "Too much liquid in WashWater. Everything sent to washings"); dWLtoCOFrac=dCLtoCOFrac=dWStoCOFrac=dCStoCOFrac=0; dWLtoWOFrac=dCLtoWOFrac=dWStoWOFrac=dCStoWOFrac=1; } else if ((CL+WL)/NZ(CS+WS+CL+WL) < m) //Case D, Too Dry: Send everything to cake. { SetNote(idWashNote, "Not enough liquid in washing stage. Everything sent to cake"); dWLtoCOFrac=dCLtoCOFrac=dWStoCOFrac=dCStoCOFrac=1; dWLtoWOFrac=dCLtoWOFrac=dWStoWOFrac=dCStoWOFrac=0; } else { //Here's where we handle if we have any other zeros. //Also handle if a variable manages to slip by the other checks [It is possible with outlandish input parameters.] dWLtoCOFrac = WL > 0 ? dWLtoCO / WL : 0; dCLtoCOFrac = CL > 0 ? dCLtoCO / CL : 0; dWStoCOFrac = WS > 0 ? dWStoCO / WS : 0; dCStoCOFrac = CS > 0 ? dCStoCO / CS : 0; dWLtoWOFrac = WL > 0 ? dWLtoWO / WL : 0; dCLtoWOFrac = CL > 0 ? dCLtoWO / CL : 0; dWStoWOFrac = WS > 0 ? dWStoWO / WS : 0; dCStoWOFrac = CS > 0 ? dCStoWO / CS : 0; } dWashEfficiency = CL > 0 ? 1 - dCLtoCOFrac : dNAN; //MStream QWashingOutput = QCake; BAD idea, QWashingOutput becomes a reference to QCake!!!! MStreamI QWashingOutput; QWashingOutput = QCake; QWashingOutput.ZeroMass(); for (int i = 0; i < gs_MVDefn.Count(); i++) if (gs_MVDefn[i].IsSolid()) { QWashingOutput.M[i] = QUnwashedCake.M[i] * dCStoWOFrac + QWashWater.M[i] * dWStoWOFrac; QCake.M[i] = QUnwashedCake.M[i] * dCStoCOFrac + QWashWater.M[i] * dWStoCOFrac; } else if (gs_MVDefn[i].IsLiquid()) { QWashingOutput.M[i] = QUnwashedCake.M[i] * dCLtoWOFrac + QWashWater.M[i] * dWLtoWOFrac; QCake.M[i] = QUnwashedCake.M[i] * dCLtoCOFrac + QWashWater.M[i] * dWLtoCOFrac; } double dInputHf; MStream* pQWashingOutput; if (bWashingsConnected) //bWashingsConnected indicates whether OUTPUT washings are connected. { dInputHf = QUnwashedCake.totHf() + QWashWater.totHf(); pQWashingOutput = &QWashingOutput; } else { dInputHf = QUnwashedCake.totHf() + QWashWater.totHf() + QFiltrate.totHf(); for (int i = 0; i < gs_MVDefn.Count(); i++) QFiltrate.M[i] += QWashingOutput.M[i]; pQWashingOutput = &QFiltrate; //For thermal property managing } double dgbTi = pQWashingOutput->T; bool converged = false; for (int i = 0; i < 10 && !converged; i++) { double dbgTt = pQWashingOutput->T; double dOutputHf = QCake.totHf() + pQWashingOutput->totHf(); double deltaT = -(dOutputHf - dInputHf) / NZ(pQWashingOutput->totCp() + QCake.totCp()); pQWashingOutput->T += deltaT; QCake.T = pQWashingOutput->T; converged = abs(dInputHf - dOutputHf) < 1; //TODO: Check what sort of convergence we require. } double dbgT = pQWashingOutput->T; double dbgT2 = QWashingOutput.T; if (bWashingsConnected) { MStream& QWashings = FlwIOs[FlwIOs.First[idWashings]].Stream; QWashings = QWashingOutput; } } else //If we have no washings { if (bWashingsConnected) { MStream& QWashings = FlwIOs[FlwIOs.First[idWashings]].Stream; QWashings.ZeroMass(); } QCake = QUnwashedCake; } //Vent: if (FlwIOs.Count[idVent] > 0) { MStream& QVent = FlwIOs[FlwIOs.First[idVent]].Stream; QVent = QCake; QVent.ZeroMass(); MStreamI QWashWater; if (FlwIOs.Count[idWash] > 0) QWashWater = FlwIOs[FlwIOs.First[idWash]].Stream; else QWashWater.ZeroMass(); for (int i = 0; i < gs_MVDefn.Count(); i++) if (gs_MVDefn[i].IsGas()) QVent.M[i] = QCake.M[i] + QWashWater.M[i]; } //Update "Actual" values. dCakeSolids = QCake.MassFrac(MP_Sol); dFiltSolids = QFiltrate.MassFrac(MP_Sol); dCakeSolConc = QCake.Mass(MP_Sol) / NZ(QCake.Volume(MP_All, C_2_K(25.0))); dFiltSolConc = QFiltrate.Mass(MP_Sol) / NZ(QFiltrate.Volume(MP_All, C_2_K(25.0))); if (bWashWaterConnected) { double CFeed = QFeed.SpecieConc(MP_Liq, nWashCompSpecie, C_2_K(25.0)); double CCake = QCake.SpecieConc(MP_Liq, nWashCompSpecie, C_2_K(25.0)); double CWash = QWashWater.SpecieConc(MP_Liq, nWashCompSpecie, C_2_K(25.0)); dWashCompEff = (CFeed - CCake) / NZ(CFeed - CWash); } else { dWashCompEff = 0; dWashEfficiency = 0; } } 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"); } }
/*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); } }
double CCWashEffFnd::Function(double x) { X = x; // *************************** NBNB *************************** // These Temporary assignments are a compiler bug work around //dword PhS = som_Sol; Qt.QSetF(Qm, som_Sol, 1.0, POut); Qt.QAddF(Qw, som_Sol, 1.0); //dword PhL = som_Liq; Qt.QAddF(Qm, som_Liq, 1.0); Qt.QAddF(Qw, som_Liq, 1.0-x); Qt.SetTemp(FT); // *************************** NBNB *************************** const double RhoS = Range(1.0, Qt.Rho(som_Sol), 5000.0); const double RhoL = Qt.Rho(som_Liq); const double Lu = RhoL*Su*(1.0/(U + UCorr)-1.0/RhoS); if (SA) Y = GEZ(1.0-(Lu/GTZ(Lf + (1.0 - x)*Lw))); else { const double solper = Range(0.1, U + UCorr, 1.0); const double solmas = Qt.QMass(som_Sol); const double LiqMas = solmas/solper - solmas; const double TotLiq = Max(1e-6, Qt.QMass(som_Liq)); Y = Range(0.0, 1.0 - LiqMas/TotLiq, 1.0); } Qu.QSetF(Qt, som_Sol, 1.0, POut); Qu.QAddF(Qt, som_Liq, (1.0 - Y)); Qo.QSetF(Qt, som_Liq, Y, POut); Qo.QAddF(Qw, som_Liq, x); Qu.SanityCheck(); Qo.SanityCheck(); if (1) {// Correct Enthalpy... Qu.SetTemp(FT); Qo.SetTemp(FT); double P = POut; double H = Qu.totHf()+Qo.totHf(); double dT = 0.0, H0, T0; for (int Hiter=0; Hiter<10; Hiter++) { if (ConvergedVV(HTot, H, 1.0e-12, 1.0e-12)) break; if (dT!=0.0) dT = (HTot-H)*(FT-T0)/NZ(H-H0); else dT = 0.1; T0 = FT; H0 = H; FT += dT; H = Qu.totHf(som_ALL, FT, P)+Qo.totHf(som_ALL, FT, P); } Qu.SetTemp(FT); Qo.SetTemp(FT); } const double Co = Qo.SpecieConc(Qo.Temp(), iScanEffSpecie, som_Liq); const double Cu = Qu.SpecieConc(Qu.Temp(), iScanEffSpecie, som_Liq); if (Converging() || TestingEstimate()) // Converging { if (SA) // Solids expressed as g/L. { double SolConc25 = Qu.PhaseConc(C_2_K(25.0), som_Sol, som_ALL); UCorr = Range(-10.0, UCorr + U - SolConc25, 10.0); } else // Solids expressed as % w/w. { double solid = Qu.QMass(som_Sol);// + Qw.QMass(som_Sol); double Totmass = Max(1e-6, Qu.QMass(som_ALL)); double SolUF = solid/Totmass; UCorr = Range(-0.1, UCorr + U - SolUF, 0.1); } } double ScandrettEff = (Cf - Cu)/NZ(Cf - Co); return ScandrettEff; }
Sys13::Sys13(pTagObjClass pClass_, pchar TagIn, pTaggedObject pAttach, TagObjAttachment eAttach) : MdlNode(pClass_, TagIn, pAttach, eAttach), VH1301 ("VH1301", this, TOA_Embedded), VH1302 ("VH1302", this, TOA_Embedded), VGA1301 ("VGA1301", this, TOA_Embedded), VBA1301 ("VBA1301", this, TOA_Embedded), HBA1301 ("HBA1301", this, TOA_Embedded), TEGStore("TEGStore", this, TOA_Embedded), Q1("Q1", this, TOA_Embedded), Q2("Q2", this, TOA_Embedded), Q3("Q3", this, TOA_Embedded), Q4("Q4", this, TOA_Embedded), Q5("Q5", this, TOA_Embedded), Q6("Q6", this, TOA_Embedded), Q7("Q7", this, TOA_Embedded), Q8("Q8", this, TOA_Embedded), Q9("Q9", this, TOA_Embedded), Qa("Qa", this, TOA_Embedded), Qb("Qb", this, TOA_Embedded), Qc("Qc", this, TOA_Embedded) { AttachClassInfo(nc_Process, S13IOAreaList); AllDataHere = 1; VH1301.SetVolume(2.6); VH1302.SetVolume(2.6); VGA1301.SetVolume(1.0); VBA1301.SetVolume(26.0); HBA1301.SetVolume(2.0); TEGStore.SetVolume(2.0); VH1301.SetStateAction(IE_Integrate); VH1302.SetStateAction(IE_Integrate); VGA1301.SetStateAction(IE_Integrate); VBA1301.SetStateAction(IE_Integrate); HBA1301.SetStateAction(IE_Integrate); TEGStore.SetStateAction(IE_Integrate); Q1m=0.0; Q2m=0.0; Q3m=0.0; Q4m=0.0; Q5m=0.0; Q6m=0.0; Q7m=0.0; Q8m=0.0; Q9m=0.0; Qam=0.0; Qbm=0.0; Qcm=0.0; bLC13111=0; bLC13114=0; bLC13117=0; bLC13120=0; bPKA1301B=0; bPKA1301A=0; bPKA1301B=0; bPK1302A=0; bPK1302B=0; bUHA1301A=0; bUHA1301B=0; VH1301_Lvl=0.5; VH1301_P=AtmosPress()+300.0; VH1302_Lvl=0.5; VH1302_P=AtmosPress()+300.0; VGA1301_LvlSet=0.55; VGA1301_Lvl=0.5; VGA1301_P=AtmosPress()+300.0; HBA1301_LvlSet=0.5; HBA1301_Lvl=0.5; HBA1301_P=AtmosPress()+300.0; VBA1301_Lvl=0.5; VGA1301_T=C_2_K(100.0); HBA1301_T=C_2_K(100); VBA1301_T=C_2_K(20.0); VH1301_T=C_2_K(20.0); VH1302_T=C_2_K(20.0); //VB1001_LvlSet=0.65; // changed for external controller //VB1001_Lvl=0.7; // mhm comeback - this level has to come from the glycol contacter //FEA13050_Qm=0.0; QmBoil=1.0; QmRich=0.0; QmLean=0.0; QmCirc=1.0; QmXfer=1.0; QmFill=1.0; QmSetl=10.0; VLVin_Posn = 1.0; TEGStore.ZeroMass(); TEGStore.SetSpMass(TEG.li(), 1.0); TEGStore.SetTemp(C_2_K(20.0)); };
flag Sys13::ValidateData(ValidateDataBlk & VDB) { flag OK=MdlNode::ValidateData(VDB); VGA1301_T=(VGA1301_T < C_2_K(-20.0) || VGA1301_T > C_2_K(200.0)) ? C_2_K(20.0) : VGA1301_T; HBA1301_T=(HBA1301_T < C_2_K(-20.0) || HBA1301_T > C_2_K(200.0)) ? C_2_K(20.0) : HBA1301_T; VBA1301_T=(VBA1301_T < C_2_K(-20.0) || VBA1301_T > C_2_K(200.0)) ? C_2_K(20.0) : VBA1301_T; VH1301_T =(VH1301_T < C_2_K(-20.0) || VH1301_T > C_2_K(200.0)) ? C_2_K(20.0) : VH1301_T; VH1302_T =(VH1302_T < C_2_K(-20.0) || VH1302_T > C_2_K(200.0)) ? C_2_K(20.0) : VH1302_T; VGA1301.SetTemp(VGA1301_T); HBA1301.SetTemp(HBA1301_T); VBA1301.SetTemp(VBA1301_T); VH1301.SetTemp(VH1301_T); VH1302.SetTemp(VH1302_T); return OK; };