示例#1
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");
    }
}
示例#2
0
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!!!");
  }