bool BatchPrecip::PrecipBatchSS(double dTime, MVector & Prod, double CurLevel) { MIBayer & ProdB = *Prod.GetIF<MIBayer>(); MIPSD & ProdSz = *Prod.FindIF<MIPSD>(); MISSA & ProdSSA = *Prod.FindIF<MISSA>(); double TProd = Prod.getT(); double gpl1 = ProdB.SolidsConc(TProd); double ProdVolFlow = Prod.Volume(MP_All); double SolidsInitial = Prod.Mass(MP_Sol); double Sx; if (!sm_bCompletePopulation && sm_bUsePrevPSD) { MISSA & PrevSSA=*m_QProd.FindIF<MISSA>();// this is the SSA of the last Popul run to use in "NO POpul mode" if (IsNothing(PrevSSA)) { m_bPrevPSDUsed = 0; Sx=ProdSSA.SpecificSurfaceAreaMass(); //m^2/g//PROBLEM!!! Prev does not have SSA, use feed } else { m_bPrevPSDUsed = 1; Sx=PrevSSA.SpecificSurfaceAreaMass(); //m^2/g } } else { m_bPrevPSDUsed = 0; Sx = m_dInTankSSA ; // the SSA value manually entered by the user in m^2/g } Sx=Range(0.020, Sx, 0.085); //=== mass balance === double T0 = Prod.T; double Ain2 = ProdB.AluminaConc(C2K(25.0)); double Cin2 = ProdB.CausticConc(C2K(25.0)); double ASat2 = ProdB.AluminaConcSat(T0); double SSat2 = (Ain2-ASat2)/GTZ(Cin2); double NoPerSec2 = ProdSSA.PartNumPerSec(); double enthIn = Prod.totCp(MP_All) * K2C(T0); // Enthalpie IN double PrecipRate1 = get_PrecipRate(Prod, Sx); gpl1 = gpl1 - PrecipRate1 * dTime*156/102 ; bool CErr; const double Na2OFac = get_Na2OFactor(Prod); PerformAluminaSolubility(Prod, dNAN, dNAN, gpl1, 0, Na2OFac, SSat2, CErr); double T1 = Prod.T; double SolidsVariation = (Prod.Mass(MP_Sol) - SolidsInitial); //* 0.97 ;// 0.97 is to compensate for the HYPROD error HeatBalance(Prod, SolidsVariation, enthIn, dTime, CurLevel); // Éliminé pour vérifier la temp ? return true; }
bool BatchPrecip::BatchCycle(MVector & Slurry, MVector & Liquor, MVector & Prod) { double dt; MIBayer & SlurryB = *Slurry.GetIF<MIBayer>(); MIBayer & ProdB = *Prod.GetIF<MIBayer>(); MIPSD & SlurrySz = *Slurry.FindIF<MIPSD>(); MIPSD & ProdSz = *Prod.FindIF<MIPSD>(); MISSA & SlurrySSA = *Slurry.FindIF<MISSA>(); MISSA & ProdSSA = *Prod.FindIF<MISSA>(); Log.SetCondition(IsNothing(SlurrySz) || IsNothing(ProdSz), 2, MMsg_Error, "Bad Slurry Stream - No Size Distribution"); Log.SetCondition(IsNothing(SlurrySSA) || IsNothing(ProdSSA), 3, MMsg_Error, "Bad Slurry Stream - No SSA Data"); m_dCirculationTime = Max(1.0, m_dCirculationTime); m_dSeedingTime = Max(1.0, m_dSeedingTime); m_dDrawOffTime = Max(1.0, m_dDrawOffTime); m_dFillingTime = Max(1.0, m_dFillingTime); m_dFillingTempDrop = Max(0.0, m_dFillingTempDrop); if (!IsNothing(ProdSz) && !IsNothing(ProdSSA)) { if (!InitSizeData(Prod)) return false; InitAgloData(m_eAgloModel, m_dAgloParam[int(m_eAgloModel)]); double *ProdPSD=ProdSz.getFracVector(0); //array of PSD data //Determine adjusted filtrate and slurry feed for "single" tank double SlurryTtlFlow = Slurry.Volume(); if (SlurryTtlFlow<1.0e-6) return true; double LiquorTtlFlow = (m_bHasLiquor ? Liquor.Volume() : 0.0); if (m_bHasLiquor && LiquorTtlFlow<1.0e-6) return true; //todo error message double CombinedFlows = SlurryTtlFlow + LiquorTtlFlow; const double SlurryTtlMassFlow = Slurry.Mass(); const double LiquorTtlMassFlow = (m_bHasLiquor ? Liquor.Mass() : 0.0); bool ContSeeding = !m_bHasLiquor; double FractionFiltrate = 0.0; double SeedFlow = 0.0; double FillFlow = 0.0; if (ContSeeding) { SeedFlow = (m_dVolume*m_dLevel) / (m_dFillingTime+m_dSeedingTime);// !! in that CASE filling means the PL&Seed filling time Slurry.AdjustMassTo(MP_All, SlurryTtlMassFlow*SeedFlow/SlurryTtlFlow); } else { FractionFiltrate = LiquorTtlFlow / (LiquorTtlFlow+SlurryTtlFlow); FillFlow = (m_dVolume*m_dLevel) * FractionFiltrate / m_dFillingTime; SeedFlow = (m_dVolume*m_dLevel) * (1.0-FractionFiltrate) / m_dSeedingTime; Liquor.AdjustMassTo(MP_All, LiquorTtlMassFlow*FillFlow/LiquorTtlFlow); Slurry.AdjustMassTo(MP_All, SlurryTtlMassFlow*SeedFlow/SlurryTtlFlow); //Liquor.SetF(Liquor, MP_All, FillFlow/LiquorTtlFlow); } /*if (m_dNbTankAvailable<=0.0) { Nb = (m_dFilltime + m_dSeedingTime + Recirc_t +DrawOff_t) * ( CombinedFlows) / (m_dVolume*level); }*/ //todo: No Of Tanks??? double iLevel; if (ContSeeding) { iLevel = 0.2; const double TimeSoFar = iLevel*(m_dVolume*m_dLevel) / Slurry.Volume(); const double d = Slurry.Mass()* TimeSoFar ;//kg Prod.SetM(Slurry, MP_All, d); //const double d = iLevel*(m_dVolume*m_dLevel) / Slurry.Volume(); //Prod.SetF(Slurry, MP_All, d); // les valeurs dans PrecipResult ne sont plus des debits mais des volumes et des masses; } else { iLevel = FractionFiltrate; const double d = Liquor.Mass()*m_dFillingTime;//kg Prod.SetM(Liquor, MP_All, d); // les valeurs dans PrecipResult ne sont plus des debits mais des volumes et des masses; // todo !!!!! reduire la temperature du filtrat et Flash } double ProdSSurf; MassFrac2HyprodDist(SizeClass, ProdPSD, ProdHyPsd, NIntervals, ProdSSurf); //gets PSD for 1 gpl double target = m_dLevel; //add seed until specified level (filling tank) while (iLevel < target) { double dt = 36.0; //seconds if ( (iLevel + Slurry.Volume()*dt/(m_dVolume*m_dLevel)) > target ) dt = fabs( (target-iLevel) * (m_dVolume*m_dLevel) / Slurry.Volume()); if (dt>0.0000001) { iLevel += (Slurry.Volume()*dt/(m_dVolume*m_dLevel)); //MixBatch... HyprodDist2MassFrac(SizeClass, ProdHyPsd, ProdPSD, NIntervals); Prod.AddM(Slurry, MP_All, Slurry.Mass()*dt); MassFrac2HyprodDist(SizeClass, ProdPSD, ProdHyPsd, NIntervals, ProdSSurf); //gets PSD for 1 gpl //precip reaction... if (ProdB.SolidsConc(C2K(25.0))>5.0) // need a minimum solid conc. to precipitate if (sm_bCompletePopulation) { if (!PrecipBatch(dt, Prod, iLevel,m_bAgglomONwhileFilling))// with population balance return false; } else { if (!PrecipBatchSS(dt, Prod, iLevel))// with SSA return false; } //application.processMessages; //if flowsheet.UserCancel then // break; } else iLevel = target; }//end while //precip reaction (tank is full) double DummyProdNNtl; CalcSSurgNNtl(SizeClass, ProdHyPsd, NIntervals, ProdSSurf, DummyProdNNtl); double PrecipRate = get_PrecipRate(Prod, ProdSSurf); //!! THE LOOP IS CALCULT. ONLY if PRECIP HAPPENS double Btime=0.0; if (PrecipRate<0.0) while (Btime < m_dCirculationTime ) { PrecipRate = get_PrecipRate(Prod, ProdSSurf); if (sm_bCompletePopulation) dt = -0.2/PrecipRate; else dt = -0.5/PrecipRate; if ( (Btime+dt) > m_dCirculationTime) dt = m_dCirculationTime - Btime; Btime += dt; if (sm_bCompletePopulation) { if (!PrecipBatch(dt, Prod, iLevel, true))// with population balance return false; } else { if (!PrecipBatchSS(dt, Prod, iLevel))// with SSA return false; if (sm_bUsePrevPSD) { //SetPSDfromPrev(Prod); } } //application.processMessages; //if flowsheet.UserCancel then // break; } Prod.AdjustMassTo(MP_All, SlurryTtlMassFlow+LiquorTtlMassFlow); HyprodDist2MassFrac(SizeClass, ProdHyPsd, ProdPSD, NIntervals); } return true; } //BatchCycle
bool BatchPrecip::PrecipBatch(double dTime, MVector & Prod, double CurLevel, bool AgglomON ) { long m; MIBayer & ProdB = *Prod.GetIF<MIBayer>(); MIPSD & ProdSz = *Prod.FindIF<MIPSD>(); MISSA & ProdSSA = *Prod.FindIF<MISSA>(); double TProd = Prod.getT(); double gpl1 = ProdB.SolidsConc(TProd); double ProdVolFlow = Prod.Volume(MP_All); double SolidsInitial = Prod.Mass(MP_Sol); double ProdSSurf, ProdNNtl; //adjusting the PSD from 1 gpl to real conc.... for (m=0; m<NIntervals; m++) ProdHyPsd[m]= gpl1 * ProdHyPsd[m]; #if ForceOptimizeOff for (m=0; m<NIntervals; m++) if (ProdHyPsd[m]<0.0) SetStopRequired("Phone Denis, negative PSD!"); #endif CalcSSurgNNtl(SizeClass, ProdHyPsd, NIntervals, ProdSSurf, ProdNNtl); //initialise deltaPSD for (m=0; m<NIntervals; m++) dPSD[m]=0.0; //variation of Particles due to Agglomeration by Collision const double GRate2 = ProdB.GrowthRate(); if (AgglomON ) ApplyAgglom(GRate2, dTime, m_dKvFac, ProdNNtl); //variation due to Nucleation in the Smallest Classes ONLY const double NRate = get_NucleationRate(m_eNuclModel, Prod, ProdSSurf, m_eShearRate); dPSD[0] += NRate*dTime; // variation due to Growth Only const double GRate3 = ProdB.GrowthRate(); const double Const_boucle3 = GRate3*dTime/HPDSize; dPSD[0] += Const_boucle3*( ProdHyPsd[0]-ProdHyPsd[2] )/2.0; for (m=1; m<NIntervals-1; m++) dPSD[m] += Const_boucle3*(ProdHyPsd[m-1]-ProdHyPsd[m+1])/2.0; // Finally Substr. or Adding Particles to the Each Classes for (m=0; m<NIntervals; m++) { ProdHyPsd[m] += dPSD[m]; ProdHyPsd[m] = Max(0.0, ProdHyPsd[m]); } CalcSSurgNNtl(SizeClass, ProdHyPsd, NIntervals, ProdSSurf, ProdNNtl); //Update ProdSSurf and ProdNNtl double gpl1x=gpl1; gpl1=0.0; for (m=0; m<NIntervals; m++) { gpl1=gpl1 + ProdHyPsd[m]*pow(SizeClass[m],3)/6E12; } gpl1 = gpl1 * PI * 2.42;// *ProdVolFlow; //readjusting the PSD to 1 gpl... for (m=0; m<NIntervals; m++) ProdHyPsd[m] = ProdHyPsd[m] / gpl1; //=== mass balance === double T0 = Prod.T; double Ain2 = ProdB.AluminaConc(C2K(25.0)); double Cin2 = ProdB.CausticConc(C2K(25.0)); double ASat2 = ProdB.AluminaConcSat(T0); double SSat2 = (Ain2-ASat2)/GTZ(Cin2); double NoPerSec2 = ProdSSA.PartNumPerSec(); double enthIn = Prod.totCp(MP_All) * K2C(T0); // Enthalpie IN bool CErr; const double Na2OFac = get_Na2OFactor(Prod); PerformAluminaSolubility(Prod, dNAN, dNAN, gpl1, 0/*NoPerSec2*/, Na2OFac, SSat2, CErr); double T1 = Prod.T; double SolidsVariation = (Prod.Mass(MP_Sol) - SolidsInitial); //* 0.97 ;// 0.97 is to compensate for the HYPROD error // mfshydPrev:=mfshyd; // mfshyd:=flowtotal*gpl1 -mfsSoda -mfsOxal -mfsSio2 ;the way it was done in HYPROD.. it is WRONG though HeatBalance(Prod, SolidsVariation, enthIn, dTime, CurLevel); // Éliminé pour vérifier la temp ? return true; }