void CPrecipitator::DoResults(MStream & Prod) { MIBayer & FeedB=Feed.IF<MIBayer>(false); MIBayer & ProdB=Prod.IF<MIBayer>(false); dACin = FeedB.AtoC(); dACout = ProdB.AtoC(); dQvin = Feed.Volume(); dQvout = Prod.Volume(); dQmin = Feed.Mass(); dQmout = Prod.Mass(); double dQvout25 = Prod.Volume(MP_All, C2K(25)); double Cout = ProdB.CausticConc(Prod.T); m_dACeq = ProdB.AluminaConcSat(Prod.T)/ProdB.CausticConc(C2K(25)); m_dSSat = ProdB.AtoC()/m_dACeq; dYield = Cout*(dACin-dACout); double SolIn = Feed.MassVector[spTHA]; double SolOut = Prod.MassVector[spTHA]; dTHAPrecip = SolOut - SolIn; SolIn = Feed.Mass(MP_Sol); SolOut = Prod.Mass(MP_Sol); dSolPrecip = SolOut - SolIn; dSolConc = Prod.Mass(MP_Sol)/dQvout25; m_dLiquorTin = Prod.T; }
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::ValidateDataFields() {//ensure parameters are within expected ranges m_dVolume = DV.ValidateRange("Volume", 1.0, m_dVolume, 1000000); m_dSurface = DV.ValidateRange("Surface", 1.0, m_dSurface, 300000); m_dKvFac = DV.ValidateRange("KvFac", 0.0, m_dKvFac, 30); m_dUCoef = DV.ValidateRange("UCoef", 0.0, m_dUCoef, 2000); m_dLevel = DV.ValidateRange("Level", 0.01, m_dLevel, 1); if ( m_eHeatBalance == eHBal_ImposedTemp) m_dTempImpos = DV.ValidateRange("TempImposed", C2K(1.0e-6), m_dTempImpos, C2K(150.0)); return true; }
CoolingTower::CoolingTower(pTagObjClass pClass_, pchar TagIn, pTaggedObject pAttach, TagObjAttachment eAttach) : MN_Surge(pClass_, TagIn, pAttach, eAttach) { AttachIOAreas(CoolingTowerIOAreaList, &PipeEntryGroup); Contents.SetClosed(true); Contents.SetPreset(&m_Preset, &m_PresetImg); iMethod = CTM_Merkel; iMerkelCalcType = MCT_TOut; iLossMethod = WLM_DriftBlowdown; dAirWetBulbT = C2K(30.0); dApproachT = 5.0; dKaVL = 1.5; dLGRatio = 0.98; dDriftLossFrac = 0.002; dEvapFactor = 0.00085; iCycles = 5; dRqdLossFrac = 0.02; dRqdLossQm = 10.0; dRqdDriftLossFrac= 0.05; dMaxEvapFrac = 1.0; dAirCp = 0.98; dAirDryBulbT = C2K(25.0); dQmIn = 0.0; dAirQmIn = 0.0; dTempKFeed = Std_T; dFinalP = Std_P; dFinalT = dAirWetBulbT; dTempDrop = 0.0; dHeatFlow = 0.0; dDuty = 0.0; dEvapFrac = 0.0; dQmWaterEvap = 0.0; dDriftLossQm = 0.0; dBlowdownLossQm = 0.0; dEvapLossQm = 0.0; dLossQm = 0.0; dTotalLossQm = 0.0; dAirEnthOut = 0.0; dAirTOut = dAirWetBulbT; dAirTRise = 0.0; dAirMixQm = 0.0; dAirMixCp = 0.0; dAirMixT = dAirWetBulbT; //EHX.Open(&CEHX_LossPerQmClass); m_VLE.Open(NULL, true); }
/* * Name : d2199_get_soc */ static int d2199_get_soc(struct sec_fuelgauge_info *fuelgauge) { int soc; if((!fuelgauge) || (!fuelgauge->info.volt_adc_init_done)) { pr_err("%s. Invalid parameter. \n", __func__); return -EINVAL; } if(fuelgauge->info.soc) fuelgauge->info.prev_soc = fuelgauge->info.soc; soc = adc_to_soc_with_temp_compensat(fuelgauge->info.average_volt_adc, C2K(fuelgauge->info.average_temperature)); if(soc >= FULL_CAPACITY) { soc = FULL_CAPACITY; if(fuelgauge->info.virtual_battery_full == 1) { fuelgauge->info.virtual_battery_full = 0; fuelgauge->info.soc = FULL_CAPACITY; } } /* Don't allow soc goes up when battery is dicharged. and also don't allow soc goes down when battey is charged. */ if(fuelgauge->is_charging != TRUE && (soc > fuelgauge->info.prev_soc) && fuelgauge->info.prev_soc) { soc = fuelgauge->info.prev_soc; } else if(fuelgauge->is_charging && (soc < fuelgauge->info.prev_soc) && fuelgauge->info.prev_soc) { soc = fuelgauge->info.prev_soc; } fuelgauge->info.soc = soc; adc_write_reg(fuelgauge, D2199_GP_ID_2_REG, (0xFF & soc)); adc_write_reg(fuelgauge, D2199_GP_ID_3_REG, (0x0F & (soc>>8))); adc_write_reg(fuelgauge, D2199_GP_ID_4_REG, (0xFF & fuelgauge->info.average_volt_adc)); adc_write_reg(fuelgauge, D2199_GP_ID_5_REG, (0xF & (fuelgauge->info.average_volt_adc>>8))); return soc; }
void CEC_FinalConc::Parse(CRCTTokenFile &TF) { Clear(); ParseExtent(TF); m_Spc.m_Name=TF.NextToken(); TF.CheckToken("="); m_ddRqdConc.SetVal(GEZ(TF.DoubleToken()), &Eq); m_iFReactTerm=-1; m_iFProdTerm=-1; if (TF.TokenIs("At")) { m_dRqdTemp=GEZ(C2K(TF.DoubleToken())); if (TF.TokenIs("K")) m_dRqdTemp=K2C(m_dRqdTemp); } //ParseDynamic(TF); };
void CPSDUnit::EvalProducts() { try { MStreamI Feed; FlwIOs.AddMixtureIn_Id(Feed, idFeed); MStream & Prod = FlwIOs[FlwIOs.First[idProd]].Stream; //Reference to the overflow out stream //dbg.log("EvalProds"); Prod = Feed; bool streamOK=((Feed.Mass()>1.0e-9) && (Feed.Mass(MP_Sol)>1.0e-9)); m_dMassSolids = Prod.Mass(MP_Sol); m_dVolSlurry = Prod.Volume(MP_All, C2K(25)); MIBayer & ProdB=Prod.IF<MIBayer>(false); // Do the checks up front Log.SetCondition(IsNothing(ProdB), 1, MMsg_Warning, "Bad Feed Stream - Not Bayer Model"); MIPSD & ProdPSD = Prod.IF<MIPSD>(false); Log.SetCondition(IsNothing(ProdPSD), 1, MMsg_Warning, "Bad Feed Stream - No PSD Available"); if (IsNothing(ProdB) || IsNothing(ProdPSD) || (m_dVolSlurry < 1.0e-4)) streamOK = false; //if (streamOK) dbg.log("Stream OK"); //else dbg.log("Bad Stream"); if (streamOK) { // Online and have Bayer and SSA properties... m_dCnv = m_dMassSolids/m_dVolSlurry; DoPSDUnit(Prod); displayPSD(b); } else { // Just tidy up and put some sensible stuff in the results... // Log.Message(MMsg_Warning, "Bad Stream: PSD Unit"); } } 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"); } }
static int d2199_read_voltage(struct sec_fuelgauge_info *fuelgauge) { int new_vol_adc, base_weight, new_vol_orign = 0; int offset_with_new = 0; int ret = 0; //static int calOffset_4P2, calOffset_3P4 = 0; int num_multi = 0; int orign_offset = 0; u8 ta_status = 0; if(fuelgauge->info.volt_adc_init_done == FALSE ) { ta_status = adc_read_reg(fuelgauge, D2199_STATUS_C_REG); pr_debug("%s. STATUS_C register = 0x%X\n", __func__, ta_status); if(ta_status & D2199_GPI_3_TA_MASK) fuelgauge->is_charging = 1; else fuelgauge->is_charging = 0; } pr_debug("##### is_charging mode = %d\n", fuelgauge->is_charging); // Read voltage ADC ret = fuelgauge->info.d2199_read_adc(fuelgauge, D2199_ADC_VOLTAGE); pr_debug("[%s]READ_EOC = %d\n", __func__, fuelgauge->info.adc_res[D2199_ADC_VOLTAGE].is_adc_eoc); pr_debug("[%s]READ_ADC = %d\n", __func__, fuelgauge->info.adc_res[D2199_ADC_VOLTAGE].read_adc); if(ret < 0) return ret; if(fuelgauge->info.adc_res[D2199_ADC_VOLTAGE].is_adc_eoc) { int offset = 0; new_vol_orign = new_vol_adc = fuelgauge->info.adc_res[D2199_ADC_VOLTAGE].read_adc; if(fuelgauge->info.volt_adc_init_done) { // Initialization is done if(fuelgauge->is_charging) { // In case of charge. orign_offset = new_vol_adc - fuelgauge->info.average_volt_adc; base_weight = d2199_get_weight_from_lookup( C2K(fuelgauge->info.average_temperature), fuelgauge->info.average_volt_adc, fuelgauge->is_charging, orign_offset); offset_with_new = orign_offset * base_weight; fuelgauge->info.sum_total_adc += offset_with_new; num_multi = fuelgauge->info.sum_total_adc / NORM_CHG_NUM; if(num_multi) { fuelgauge->info.average_volt_adc += num_multi; fuelgauge->info.sum_total_adc = fuelgauge->info.sum_total_adc % NORM_CHG_NUM; } else { new_vol_adc = fuelgauge->info.average_volt_adc; } fuelgauge->info.current_volt_adc = new_vol_adc; } else { // In case of discharging. orign_offset = fuelgauge->info.average_volt_adc - new_vol_adc; base_weight = d2199_get_weight_from_lookup( C2K(fuelgauge->info.average_temperature), fuelgauge->info.average_volt_adc, fuelgauge->is_charging, orign_offset); offset_with_new = orign_offset * base_weight; if(fuelgauge->info.reset_total_adc) { fuelgauge->info.sum_total_adc = fuelgauge->info.sum_total_adc / 10; fuelgauge->info.reset_total_adc = 0; pr_debug("%s. sum_toal_adc was divided by 10\n", __func__); } fuelgauge->info.sum_total_adc += offset_with_new; pr_debug("Discharging. Recalculated base_weight = %d\n", base_weight); num_multi = fuelgauge->info.sum_total_adc / NORM_NUM; if(num_multi) { fuelgauge->info.average_volt_adc -= num_multi; fuelgauge->info.sum_total_adc = fuelgauge->info.sum_total_adc % NORM_NUM; } else { new_vol_adc = fuelgauge->info.average_volt_adc; } if(is_called_by_ticker == 0) { fuelgauge->info.current_volt_adc = new_vol_adc; } else { fuelgauge->info.current_volt_adc = new_vol_adc; is_called_by_ticker=0; } } } else { // Before initialization u8 i = 0; u8 res_msb, res_lsb, res_msb_adc, res_lsb_adc = 0; u32 capacity = 0, vbat_adc = 0; int convert_vbat_adc, X1, X0; int Y1, Y0 = FIRST_VOLTAGE_DROP_ADC; int X = C2K(fuelgauge->info.average_temperature); // If there is SOC data in the register // the SOC(capacity of battery) will be used as initial SOC res_lsb = adc_read_reg(fuelgauge, D2199_GP_ID_2_REG); res_msb = adc_read_reg(fuelgauge, D2199_GP_ID_3_REG); capacity = (((res_msb & 0x0F) << 8) | (res_lsb & 0xFF)); res_lsb_adc = adc_read_reg(fuelgauge, D2199_GP_ID_4_REG); res_msb_adc = adc_read_reg(fuelgauge, D2199_GP_ID_5_REG); vbat_adc = (((res_msb_adc & 0x0F) << 8) | (res_lsb_adc & 0xFF)); pr_debug("%s. capacity = %d, vbat_adc = %d \n", __func__, capacity, vbat_adc); if(capacity) { convert_vbat_adc = vbat_adc; pr_info("!#!#!# Boot BY Normal Power Off !#!#!# \n"); } else { // Initial ADC will be decided in here. pr_info("!#!#!# Boot BY Battery insert !#!#!# \n"); fuelgauge->info.pd2199->average_vbat_init_adc = (fuelgauge->info.pd2199->vbat_init_adc[0] + fuelgauge->info.pd2199->vbat_init_adc[1] + fuelgauge->info.pd2199->vbat_init_adc[2]) / 3; vbat_adc = fuelgauge->info.pd2199->average_vbat_init_adc; pr_debug("%s (L_%d). vbat_init_adc = %d, new_vol_orign = %d\n", __func__, __LINE__, fuelgauge->info.pd2199->average_vbat_init_adc, new_vol_orign); if(fuelgauge->is_charging) { if(vbat_adc < CHARGE_ADC_KRNL_F) { // In this case, vbat_adc is bigger than OCV // So, subtract a interpolated value // from initial average value(vbat_adc) u16 temp_adc = 0; if(vbat_adc < CHARGE_ADC_KRNL_H) vbat_adc = CHARGE_ADC_KRNL_H; X0 = CHARGE_ADC_KRNL_H; X1 = CHARGE_ADC_KRNL_L; Y0 = CHARGE_OFFSET_KRNL_H; Y1 = CHARGE_OFFSET_KRNL_L; temp_adc = do_interpolation(X0, X1, Y0, Y1, vbat_adc); convert_vbat_adc = vbat_adc - temp_adc; } else { convert_vbat_adc = new_vol_orign + CHARGE_OFFSET_KRNL2; } } else { vbat_adc = new_vol_orign; pr_debug("[L%d] %s discharging new_vol_adc = %d\n", __LINE__, __func__, vbat_adc); Y0 = FIRST_VOLTAGE_DROP_ADC; if(C2K(fuelgauge->info.average_temperature) <= BAT_LOW_LOW_TEMPERATURE) { convert_vbat_adc = vbat_adc + (Y0 + FIRST_VOLTAGE_DROP_LL_ADC); } else if(C2K(fuelgauge->info.average_temperature) >= BAT_ROOM_TEMPERATURE) { convert_vbat_adc = vbat_adc + Y0; } else { if(C2K(fuelgauge->info.average_temperature) <= BAT_LOW_MID_TEMPERATURE) { Y1 = Y0 + FIRST_VOLTAGE_DROP_ADC; Y0 = Y0 + FIRST_VOLTAGE_DROP_LL_ADC; X0 = BAT_LOW_LOW_TEMPERATURE; X1 = BAT_LOW_MID_TEMPERATURE; } else if(C2K(fuelgauge->info.average_temperature) <= BAT_LOW_TEMPERATURE) { Y1 = Y0 + FIRST_VOLTAGE_DROP_L_ADC; Y0 = Y0 + FIRST_VOLTAGE_DROP_ADC; X0 = BAT_LOW_MID_TEMPERATURE; X1 = BAT_LOW_TEMPERATURE; } else { Y1 = Y0 + FIRST_VOLTAGE_DROP_RL_ADC; Y0 = Y0 + FIRST_VOLTAGE_DROP_L_ADC; X0 = BAT_LOW_TEMPERATURE; X1 = BAT_ROOM_LOW_TEMPERATURE; } convert_vbat_adc = vbat_adc + Y0 + ((X - X0) * (Y1 - Y0)) / (X1 - X0); } } } new_vol_adc = convert_vbat_adc; pr_info("%s. # Calculated initial new_vol_adc = %d\n", __func__, new_vol_adc); if(new_vol_adc > MAX_FULL_CHARGED_ADC) { new_vol_adc = MAX_FULL_CHARGED_ADC; pr_debug("%s. Set new_vol_adc to max. ADC value\n", __func__); } for(i = AVG_SIZE; i ; i--) { fuelgauge->info.voltage_adc[i-1] = new_vol_adc; fuelgauge->info.sum_voltage_adc += new_vol_adc; } fuelgauge->info.current_volt_adc = new_vol_adc; fuelgauge->info.volt_adc_init_done = TRUE; fuelgauge->info.average_volt_adc = new_vol_adc; } fuelgauge->info.origin_volt_adc = new_vol_orign; fuelgauge->info.current_voltage = adc_to_vbat(fuelgauge->info.current_volt_adc, fuelgauge->is_charging); fuelgauge->info.average_voltage = adc_to_vbat(fuelgauge->info.average_volt_adc, fuelgauge->is_charging); }else { pr_err("%s. Voltage ADC read failure \n", __func__); ret = -EIO; } pr_debug("[%s]current volt_adc = %d, average_volt_adc = %d\n", __func__, fuelgauge->info.current_volt_adc, fuelgauge->info.average_volt_adc); pr_debug("[%s]current voltage = %d, average_voltage = %d\n", __func__, fuelgauge->info.current_voltage, fuelgauge->info.average_voltage); return ret; }
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; }
//{****************************************************************************} // THIS function perfrom the same role as MASSBALANCE for the continuous precipitator double BatchPrecip::PerformAluminaSolubility(MVector & Vec, double TRqd, double ARqd, double THARqd, double NoPerSec, double Na2OFac, double SSat, bool & ConvergeErr) { // 2 AlO2 + 4 H2O <==> Al2O3.3H2O + 2 OH //or Na2O + Al2O3 + 4 H2O <==> Al2O3.3H2O + 2 NaOH // THADelta is the Fraction of Alumina which precipitates as the hydrate // ie THADelta is deposition rate of new THA crystal const double ESoda=2335.0; //constant 2535K bool AdjustT=!Valid(TRqd); bool SetTHA=Valid(THARqd); double T=AdjustT ? Vec.T : TRqd; MVDouble AluminaMass (Vec, spAlumina); // Al2O3 MVDouble WaterMass (Vec, spWater); // H2O MVDouble THAMass (Vec, spTHA); // Al2O3.3H2O MVDouble CausticMass (Vec, spCausticSoda); // NaOH MVDouble Na2OMass (Vec, spOccSoda); // Na2O const double Fact = spAlumina.MW/spTHA.MW; // 0.654; MIBayer & BVec=*Vec.GetIF<MIBayer>(); for (int Iter=100; Iter; Iter--) { if (AdjustT) T=Vec.T; double CC= BVec.CausticConc(C2K(25.0)); double A=BVec.AluminaConc(C2K(25.0)); double THA=BVec.SolidsConc(T); double THADelta; if (SetTHA) { if (fabs(THA-THARqd)<1.0e-12*THARqd) break; THADelta = THAMass*(THARqd/GTZ(THA)-1.0); } else { if (fabs(A-ARqd)<1.0e-12*ARqd) break; THADelta = AluminaMass*(1.0-ARqd/GTZ(A))/Fact; } double alumSSat = CC *SSat; const double LclVar = Na2OFac*7.1868e-8*exp(ESoda/T)*Pow(alumSSat, 1.0955); double XSoda = THADelta*LclVar; double dAluminaMass = - Fact*THADelta; double dTHAMass = + THADelta; double dWaterMass = - (1.0-Fact)*THADelta + spWater.MW/spOccSoda.MW*XSoda; double dCausticMass = - 2.0*spCausticSoda.MW/spOccSoda.MW*XSoda; double dNa2OMass = + XSoda; double Scl; for(;;) { Scl=1; if (AluminaMass + Scl*dAluminaMass <0) Scl=Min(Scl, 1-(AluminaMass + Scl*dAluminaMass)/NZ(dAluminaMass)); if (THAMass + Scl*dTHAMass <0) Scl=Min(Scl, 1-(THAMass + Scl*dTHAMass)/NZ(dTHAMass)); if (WaterMass + Scl*dWaterMass <0) Scl=Min(Scl, 1-(WaterMass + Scl*dWaterMass)/NZ(dWaterMass)); if (CausticMass + Scl*dCausticMass <0) Scl=Min(Scl, 1-(CausticMass + Scl*dCausticMass)/NZ(dCausticMass)); if (Na2OMass + Scl*dNa2OMass <0) Scl=Min(Scl, 1-(Na2OMass + Scl*dNa2OMass)/NZ(dNa2OMass)); if (Scl<(1-1e-12)) { dAluminaMass *= Scl; dTHAMass *= Scl; dWaterMass *= Scl; dCausticMass *= Scl; dNa2OMass *= Scl; } else break; } if (fabs(dAluminaMass)<1e-22) {//problem!!! Iter=0; break; } //adjust masses... AluminaMass = AluminaMass + dAluminaMass; THAMass = THAMass + dTHAMass; WaterMass = WaterMass + dWaterMass; CausticMass = CausticMass + dCausticMass; Na2OMass = Na2OMass + dNa2OMass; Vec.MarkStateChanged(); //this forces recalculation of temperature / enthalpy based on new stream makeup } ConvergeErr = (Iter==0); Log.SetCondition(ConvergeErr, 9, MMsg_Warning, "Cannot converge PrecipTHA Alumina Conc"); if (ConvergeErr) { int xx=0; //place breakpoint here to trap this } MISSA & VecSSA=*Vec.GetIF<MISSA>(); if (NoPerSec>0.0 && !IsNothing(VecSSA)) { VecSSA.SetSAMFromFlow(BVec.THAMassFlow(), NoPerSec); } return BVec.AluminaConc(T); }
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!!!"); }
//--------------------------------------------------------------------------- // All the precipitation rate calcs are done in this routine // Takes the product composition and calculates rates from this // Rates (in kg/s) are put into global x //--------------------------------------------------------------------------- void CPrecipitator::EvalPrecipRates(MStream & Prod, double T) { T = Prod.T; // May want to do the rate calculation at other temperature MIBayer & ProdB=Prod.IF<MIBayer>(false); MISSA & ProdSSA = Prod.IF<MISSA>(false); dResidenceTime = dTankVol/GTZ(Prod.Volume(MP_SL)); for (int i=0; i<nPRECIPCOMPS; i++) xo[i]=x[i]; // Save old values for convergence test, damping double SAL = ProdSSA.SpecificSurfaceAreaVol(); double SSA = ProdSSA.SpecificSurfaceAreaMass(); double dSSat; if (ProdB.CausticConc(T) > 0) dSSat = (ProdB.AluminaConc(T)-ProdB.AluminaConcSat(T))/ProdB.CausticConc(T); else dSSat = 0.0; if (dSSat < 0) dSSat=0.0; // Supersaturation... switch (iGrowthRateMethod) { case GRM_Fixed: x[iALUMINA] = dGrowthRate; x[1] = x[iBOUND_ORGANICS] = 0.0; break; case GRM_White: { MIBayer & FeedB = Feed.IF<MIBayer>(false); double Ain = FeedB.AluminaConc(T); double Aout = ProdB.AluminaConc(T); dGrowthRateFactor = gF_White*K_White*Exps(-ER_White/T); dGrowthRate = dGrowthRateFactor*Sqr(dSSat); x[iALUMINA] = dGrowthRate*SAL*dTankVol/1000.; double sodaRate = m_dK_Soda*Sqr(dSSat)*Exps(m_dE_Soda/T)*(Aout - Ain)*dTankVol/1000.; if (sodaRate <0.0) sodaRate = 0.0; x[iBOUND_SODA] = sodaRate*(1.0-m_dBndOrgSoda); x[iBOUND_ORGANICS] = sodaRate*m_dBndOrgSoda; } break; case GRM_TTTest: { // Alumina Precipitation.... as per QPRECIPD.cpp MIBayer & FeedB = Feed.IF<MIBayer>(false); double C = ProdB.CausticConc(T); double C25 = ProdB.CausticConc(C2K(25)); double CtoS = ProdB.CtoS(); double S_out = C25/CtoS; // double FC = ProdB.FreeCaustic(T); double FC = ProdB.FreeCaustic(C2K(25)); double ACeq = ProdB.AluminaConcSat(T)/C25; double TOOC=ProdB.TOC(C2K(25))*MW_Na2CO3/MW_C; double a=Exps(-m_dActivationEnergy/T); double b=Exps(-TOOC*m_dk_TOC); double c=Pow(GTZ(S_out), m_dn_s); double d=Pow(GTZ(FC), m_dn_fc); double e=Pow(GTZ(ACeq), m_dn_eq); double K = m_dK0*a*b*c*d*e; double ACout = ProdB.AtoC(); double VLiq = Prod.Volume()*3600.; double MSolids = Prod.MassVector[spTHA]*3600.0/1000.0; double Sol = MSolids*1000.0/GTZ(VLiq); double deltaAC = K * dResidenceTime/3600 * Pow(GTZ(ACout-ACeq),m_dn_) * Pow(GTZ(Sol),m_dn_sol) * Pow(GTZ(m_dSSA),m_dn_ssa); double ACoutEst = dACin - deltaAC; double VolOut = Prod.Volume(MP_Liq, C2K(25.0)); double xx = deltaAC*C25*VolOut*2*MW_Alumina/MW_Al2O3; if (xx<0.0) xx=0.0; x[iALUMINA] = xx; // Bound Soda calculations... slurped from QAL file double k1x = m_dK1 * (0.000598*C25 - 0.00036*K2C(T) + 0.019568*TOOC/C) * (1.0 - 0.758*CtoS); double BoundSoda = k1x * Sqr(ACoutEst-ACeq); if (x[iALUMINA]>=0.0) { double BndSoda = BoundSoda*(x[iALUMINA]*MW_Al2O3/(2.0*MW_THA))*(1.0-m_dBndOrgSoda)*((2.0*MW_NaOH)/MW_Na2O); double BndOrgSoda = BoundSoda*(x[iALUMINA]*MW_Al2O3/(2.0*MW_THA))*(m_dBndOrgSoda)*(MW_OrganicSoda/MW_Na2O); /* If represented as Na2O double BndSoda = BoundSoda*GibbsRate*dBndOrgSoda; double BndOrgSoda = BoundSoda*GibbsRate*(1.0-dBndOrgSoda); */ x[iBOUND_SODA] = BndSoda; x[iBOUND_ORGANICS] = BndOrgSoda; } } break; } return; }
CPrecipitator::CPrecipitator(MUnitDefBase *pUnitDef, TaggedObject * pNd) : MBaseMethod(pUnitDef, pNd), m_RB(this, false, "RB"), Evap(this, "Evap") { //default values... // x=new double[4]; // xo = new double[4]; m_bEvapConnected = 0; /*//CAR data: m_dActivationEnergy = 7734.00; m_dK0 = 2.29e+13; m_dK1 = 1.75; m_dk_TOC = 0.0023000; m_dn_s = -1.00; m_dn_fc = -2.00; m_dn_eq = 0; m_dn_ = 1.70; m_dn_sol = 1.00; m_dn_ssa = 0.60;*/ //Generic data: m_dActivationEnergy = 7600.00; m_dK0 = 1.0e+11; m_dK1 = 1.0; m_dk_TOC = 0.0; m_dn_s = -1.00; m_dn_fc = -0.5; m_dn_eq = 0.0; m_dn_ = 2.0; m_dn_sol = 1.00; m_dn_ssa = 0.60; bOnLine = 1; dTempDropRqd = 0.5; dThermalLossRqd = 1500.0; iThermalLossMethod = THL_TempDrop; iCoolMethod=0; m_bEvapConnected = 0; ER_White = 7200.0; K_White = 1.96e10; gF_White = 1.0; m_dK_Soda = .00127; // K for soda inclusion m_dE_Soda = 2535.; // E for soda inclusion dTankVol = 1000.0; dSolPrecip = 0.0; dGrowthRate = 0.0; dGrowthRateFactor = 0.0; dYield = 0.0; dTin = C2K(25.0); dTout = C2K(25.0); dDiamin = 0.0; dSALin = 0.0; dQvin = 0.0; dQvout = 0.0; dACin = 0.0; dACout = 0.0; dResidenceTime = 0.0; dThermalLoss = 0.0; dReactionHeat = 0.0; m_dCoolOutTotHz = 0.0; iCoolType = 0; }
CToleranceBlock EvapFnd::s_Tol(TBF_Both, "CoolTower:EvapFnd", 0.0, 1.0e-9); double EvapFnd::Function(double x) { m_VLE.SetSatPVapFrac(Cd, x, 0); //Cd.Model()->ClrStatesOK(); Cd.SetTemp(RqdT); Cd.SetPress(RqdP); double h=Cd.totHf(); return h; } //-------------------------------------------------------------------------- const double MerkelTMn = C2K(-106.5); const double MerkelTMx = C2K(70.0); double AirEnth(double T) { ASSERT_RDB(K2C(T)>-106.666666666666 && K2C(T)<70.0000001, "Temperature range problem!", __FILE__, __LINE__); const double Tc = Range(-106.666666666667, K2C(T), 70.0); if (Tc<-45.5555555555557) return 1.0086*Tc + 18.092; //y = 1.0086x + 18.092 if (Tc<-23.3333333333334) return 0.0026*Tc*Tc + 1.2353*Tc + 23.016; //y = 0.0026x2 + 1.2353x + 23.016 if (Tc<-12.2222222222223) return 0.0082*Tc*Tc + 1.4875*Tc + 25.788; //y = 0.0082x2 + 1.4875x + 25.788 if (Tc<0.0000000001) return 0.0199*Tc*Tc + 1.7523*Tc + 27.309; //y = 0.0199x2 + 1.7523x + 27.309 if (Tc<12.2222222222223)