00001
00007 #include "StMaker.h"
00008 #include "StTpcdEdxCorrection.h"
00009 #include "StTpcDb/StTpcDb.h"
00010 #include "StBichsel/Bichsel.h"
00011 #include "StMessMgr.h"
00012 #include "tables/St_tss_tsspar_Table.h"
00013 #include "St_db_Maker/St_db_Maker.h"
00014 #include "StDetectorDbMaker/St_tss_tssparC.h"
00015 #include "StDetectorDbMaker/St_tpcAvCurrentC.h"
00016 #include "St_db_Maker/St_db_Maker.h"
00017 ClassImp(dEdxY2_t);
00018 ClassImp(StTpcdEdxCorrection)
00019
00020 StTpcdEdxCorrection::StTpcdEdxCorrection(Int_t option, Int_t debug) :
00021 m_Mask(option), m_tpcGas(0),
00022 m_TpcSecRowB(0),
00023 m_TpcSecRowC(0),
00024 m_Debug(debug)
00025 {
00026 assert(gStTpcDb);
00027 memset (&m_Corrections, 0, kTpcAllCorrections*sizeof(dEdxCorrection_t));
00028 m_Corrections[kAdcCorrection ] = dEdxCorrection_t("TpcAdcCorrectionB" ,"ADC/Clustering nonlinearity correction");
00029 m_Corrections[kEdge ] = dEdxCorrection_t("TpcEdge" ,"Dependence of the Gain on distance from Chamber edge");
00030 m_Corrections[kTpcdCharge ] = dEdxCorrection_t("TpcdCharge" ,"ADC/Clustering undershoot correction");
00031 m_Corrections[kTpcrCharge ] = dEdxCorrection_t("TpcrCharge" ,"ADC/Clustering rounding correction");
00032 m_Corrections[kTpcCurrentCorrection] = dEdxCorrection_t("TpcCurrentCorrection","Correction due to sagg of Voltage due to anode current");
00033 m_Corrections[kTpcRowQ ] = dEdxCorrection_t("TpcRowQ" ,"Gas gain correction for row versus accumulated charge, absolute normalization");
00034 m_Corrections[kTpcSecRowB ] = dEdxCorrection_t("TpcSecRowB" ,"Gas gain correction for sector/row");
00035 m_Corrections[kTpcSecRowC ] = dEdxCorrection_t("TpcSecRowC" ,"Additional Gas gain correction for sector/row");
00036 m_Corrections[kDrift ] = dEdxCorrection_t("TpcDriftDistOxygen" ,"Correction for Electron Attachment due to O2");
00037 m_Corrections[kMultiplicity ] = dEdxCorrection_t("TpcMultiplicity" ,"Global track multiplicity dependence");
00038 m_Corrections[kzCorrection ] = dEdxCorrection_t("TpcZCorrectionB" ,"Variation on drift distance");
00039 m_Corrections[kdXCorrection ] = dEdxCorrection_t("TpcdXCorrectionB" ,"dX correction");
00040 m_Corrections[ktpcPressure ] = dEdxCorrection_t("tpcPressureB" ,"Dependence of the Gain on Gas Density due to Pressure");
00041 m_Corrections[ktpcMethaneIn ] = dEdxCorrection_t("tpcMethaneIn" ,"Dependence of the Gain on Methane content");
00042 m_Corrections[ktpcGasTemperature ] = dEdxCorrection_t("tpcGasTemperature" ,"Dependence of the Gain on Gas Density due to Temperature");
00043 m_Corrections[ktpcWaterOut ] = dEdxCorrection_t("tpcWaterOut" ,"Dependence of the Gain on Water content");
00044 m_Corrections[kTpcdCharge ] = dEdxCorrection_t("TpcdCharge" ,"Dependence of the Gain on total charge accumulated so far");
00045 m_Corrections[kTpcZDC ] = dEdxCorrection_t("TpcZDC" ,"Dependence of the Gain on Zdc CoincidenceRate");
00046
00047 m_Corrections[kSpaceCharge ] = dEdxCorrection_t("TpcSpaceCharge" ,"Dependence of the Gain on space charge near the wire");
00048 m_Corrections[kPhiDirection ] = dEdxCorrection_t("TpcPhiDirection" ,"Dependence of the Gain on interception angle");
00049 m_Corrections[kTpcdEdxCor ] = dEdxCorrection_t("TpcdEdxCor" ,"dEdx correction wrt Bichsel parameterization");
00050 m_Corrections[kTpcLengthCorrection ] = dEdxCorrection_t("TpcLengthCorrectionB","Variation on Track length and relative error in Ionization");
00051
00052 if (!m_Mask) m_Mask = -1;
00053
00054 ReSetCorrections();
00055 }
00056
00057 void StTpcdEdxCorrection::ReSetCorrections() {
00058 TDataSet *tpc_calib = StMaker::GetChain()->GetDataBase("Calibrations/tpc"); assert(tpc_calib);
00059 if (Debug() > 1) tpc_calib->ls(3);
00060 St_tpcGas *k_tpcGas = (St_tpcGas *) tpc_calib->Find("tpcGas");
00061 if (!k_tpcGas || ! k_tpcGas->GetNRows()) {
00062 gMessMgr->Error() << "=== tpcGas is missing ===" << endm;
00063 assert(k_tpcGas);
00064 }
00065 SettpcGas(k_tpcGas);
00066 TDatime t[2];
00067 if (St_db_Maker::GetValidity(k_tpcGas,t) > 0) {
00068 Int_t Nrows = k_tpcGas->GetNRows();
00069 LOG_WARN << "StTpcdEdxCorrection::ReSetCorrections found table " << k_tpcGas->GetName()
00070 << " with NRows = " << Nrows << " in db" << endm;
00071 LOG_WARN << "Validity:" << t[0].GetDate() << "/" << t[0].GetTime()
00072 << " ----- " << t[1].GetDate() << "/" << t[1].GetTime() << endm;
00073 if (Nrows > 10) Nrows = 10;
00074 if (k_tpcGas->GetRowSize() < 256) k_tpcGas->Print(0,Nrows);
00075 }
00076 St_TpcSecRowCor *TpcSecRow = 0;
00077 St_tpcCorrection *table = 0;
00078 tpcCorrection_st *cor = 0;
00079 Int_t N = 0;
00080 Int_t k = 0;
00081 Int_t i = 0;
00082 Int_t npar = 0;
00083 for (k = kUncorrected+1; k < kTpcAllCorrections; k++) {
00084 SafeDelete(m_Corrections[k].Chair);
00085 if (! m_Corrections[k].Name ) {CLRBIT(m_Mask,k); continue;}
00086 if (! TESTBIT(m_Mask,k)) continue;
00087
00088 gMessMgr->Warning() << "StTpcdEdxCorrection: " << m_Corrections[k].Name << "/" << m_Corrections[k].Title << endm;
00089 switch (k) {
00090 case kTpcSecRowB:
00091 TpcSecRow = (St_TpcSecRowCor *) tpc_calib->Find("TpcSecRowB");
00092 if (TpcSecRow) SetTpcSecRowB(TpcSecRow);
00093 else {CLRBIT(m_Mask,k); gMessMgr->Warning() << " \tis missing" << endm;}
00094 break;
00095 case kTpcSecRowC:
00096 TpcSecRow = (St_TpcSecRowCor *) tpc_calib->Find("TpcSecRowC");
00097 if (TpcSecRow) SetTpcSecRowC(TpcSecRow);
00098 else {CLRBIT(m_Mask,k); gMessMgr->Warning() << " \tis missing" << endm;}
00099 break;
00100 default:
00101 table = (St_tpcCorrection *) tpc_calib->Find(m_Corrections[k].Name);
00102 if (! table) {
00103 gMessMgr->Warning() << " \tis missing" << endm;
00104 CLRBIT(m_Mask,k);
00105 continue;
00106 }
00107 cor = table->GetTable();
00108 N = table->GetNRows();
00109 if (! cor || ! N) {
00110 gMessMgr->Warning() << " \tis empty" << endm;
00111 CLRBIT(m_Mask,k);
00112 continue;
00113 }
00114 npar = 0;
00115 for (i = 0; i < N; i++, cor++) {
00116 if (cor->nrows == 0 && cor->idx == 0) continue;
00117 npar += TMath::Abs(cor->npar);
00118 if (TMath::Abs(cor->OffSet) > 1.e-7 ||
00119 TMath::Abs(cor->min) > 1.e-7 ||
00120 TMath::Abs(cor->max) > 1.e-7) npar++;
00121 }
00122 if (! npar ) {
00123 gMessMgr->Warning() << " \thas no significant corrections => switch it off" << endm;
00124 CLRBIT(m_Mask,k);
00125 continue;
00126 }
00127 SetCorrection(k,table);
00128 if (table) {
00129 if (St_db_Maker::GetValidity(table,t) > 0) {
00130 Int_t Nrows = table->GetNRows();
00131 LOG_WARN << "StTpcdEdxCorrection::ReSetCorrections found table " << table->GetName()
00132 << " with NRows = " << Nrows << " in db" << endm;
00133 LOG_WARN << "Validity:" << t[0].GetDate() << "/" << t[0].GetTime()
00134 << " ----- " << t[1].GetDate() << "/" << t[1].GetTime() << endm;
00135 if (Nrows > 10) Nrows = 10;
00136 if (table->GetRowSize() < 256) table->Print(0,Nrows);
00137 }
00138 }
00139 }
00140 }
00141 }
00142
00143 StTpcdEdxCorrection::~StTpcdEdxCorrection() {
00144 for (Int_t k = 0; k < kTpcAllCorrections; k++) SafeDelete(m_Corrections[k].Chair);
00145 }
00146
00147 Int_t StTpcdEdxCorrection::dEdxCorrection(dEdxY2_t &CdEdx, Bool_t doIT) {
00148
00149 mdEdx = &CdEdx;
00150 Double_t dEU = CdEdx.dE;
00151 Double_t dE = dEU;
00152 Int_t sector = CdEdx.sector;
00153 Int_t row = CdEdx.row;
00154 Double_t dx = CdEdx.dx;
00155 if (dE <= 0 || dx <= 0) return 3;
00156
00157 Double_t ZdriftDistance = CdEdx.ZdriftDistance;
00158 ESector kTpcOutIn = kTpcOuter;
00159 if (row <= 13) kTpcOutIn = kTpcInner;
00160 St_tss_tssparC *tsspar = St_tss_tssparC::instance();
00161 Float_t gasGain = 1;
00162 Float_t gainNominal = 0;
00163 if (row > 13) {
00164 gainNominal = tsspar->gain_out()*tsspar->wire_coupling_out();
00165 gasGain = tsspar->gain_out(sector,row)*tsspar->wire_coupling_out();
00166 } else {
00167 gainNominal = tsspar->gain_in()*tsspar->wire_coupling_in();
00168 gasGain = tsspar->gain_in(sector,row) *tsspar->wire_coupling_in();
00169 }
00170 if (gasGain <= 0.0) return 4;
00171 mAdc2GeV = tsspar->ave_ion_pot() * tsspar->scale()/gainNominal;
00172 Double_t Adc2GeVReal = tsspar->ave_ion_pot() * tsspar->scale()/gasGain;
00173 tpcGas_st *gas = m_tpcGas->GetTable();
00174 Double_t ZdriftDistanceO2 = ZdriftDistance*(*m_tpcGas)[0].ppmOxygenIn;
00175 Double_t ZdriftDistanceO2W = ZdriftDistanceO2*(*m_tpcGas)[0].ppmWaterOut;
00176 CdEdx.ZdriftDistanceO2 = ZdriftDistanceO2;
00177 CdEdx.ZdriftDistanceO2W = ZdriftDistanceO2W;
00178 Double_t gc, ADC, xL2, dXCorr;
00179 Double_t adcCF = CdEdx.adc;
00180 Int_t l;
00181 tpcCorrection_st *cor = 0;
00182 tpcCorrection_st *corl = 0;
00183 TpcSecRowCor_st *gain = 0;
00184 Double_t VarX = 0;
00185 Double_t iCut = 0;
00186 Double_t slope = 0;
00187 Int_t nrows = 0;
00188 for (Int_t k = kUncorrected; k <= kTpcLast; k++) {
00189 if (k != kAdcCorrection && CdEdx.lSimulated) goto ENDL;
00190 if (! TESTBIT(m_Mask, k)) goto ENDL;
00191 cor = 0;
00192 if ( m_Corrections[k].Chair) {
00193 cor = ((St_tpcCorrection *) m_Corrections[k].Chair->Table())->GetTable();
00194 if (! cor) goto ENDL;
00195 nrows = cor->nrows;
00196 l = kTpcOuter;
00197 if (nrows > 1 && nrows < 45) l = kTpcOutIn;
00198 else if (nrows == 45) l = row - 1;
00199 corl = cor + l;
00200 }
00201 iCut = 0;
00202 switch (k) {
00203 case kAdcCorrection:
00204 if (CdEdx.lSimulated) {
00205 dE *= 2.116;
00206 } else {
00207 ADC = dE/mAdc2GeV;
00208 if (TMath::Abs(ADC - adcCF) > 1) {
00209
00210 }
00211 dE = Adc2GeVReal*m_Corrections[k].Chair->CalcCorrection(kTpcOutIn,ADC,TMath::Abs(CdEdx.zG));
00212 if (dE <= 0) return 3;
00213 }
00214 goto ENDL;
00215 case kTpcdCharge:
00216 slope = m_Corrections[k].Chair->CalcCorrection(kTpcOutIn,row+0.5);
00217 dE *= TMath::Exp(-slope*CdEdx.dCharge);
00218 dE *= TMath::Exp(-m_Corrections[k].Chair->CalcCorrection(2+kTpcOutIn,CdEdx.dCharge));
00219 goto ENDL;
00220 case kTpcZDC: VarX = (CdEdx.Zdc > 0) ? TMath::Log10(CdEdx.Zdc) : 0; break;
00221 case kTpcCurrentCorrection:
00222 VarX = CdEdx.Crow;
00223 break;
00224 case kTpcrCharge:
00225 VarX = CdEdx.rCharge;
00226 break;
00227 case kTpcRowQ:
00228 VarX = CdEdx.Qcm; break;
00229 case kTpcSecRowB:
00230 case kTpcSecRowC:
00231 if (k == kTpcSecRowB) gain = m_TpcSecRowB->GetTable() + sector - 1;
00232 else gain = m_TpcSecRowC->GetTable() + sector - 1;
00233 gc = gain->GainScale[row-1];
00234 if (gc <= 0.0) return 1;
00235 dE *= gc;
00236 CdEdx.Weight = 1;
00237 if (gain->GainRms[row-1] > 0.1) CdEdx.Weight = 1./(gain->GainRms[row-1]*gain->GainRms[row-1]);
00238 goto ENDL;
00239 case kTpcPadTBins:
00240 VarX = CdEdx.Npads*CdEdx.Ntbins;
00241 break;
00242 case ktpcPressure:
00243 VarX = TMath::Log(gas->barometricPressure);
00244 break;
00245 case kDrift:
00246 VarX = ZdriftDistanceO2;
00247 break;
00248 case kMultiplicity:
00249 VarX = CdEdx.QRatio;
00250 break;
00251 case kzCorrection:
00252 VarX = ZdriftDistance;
00253 iCut = 1;
00254 break;
00255 case kdXCorrection:
00256 xL2 = TMath::Log2(dx);
00257 dXCorr = m_Corrections[k].Chair->CalcCorrection(kTpcOutIn,xL2);
00258 if (nrows > 2) dXCorr += m_Corrections[k].Chair->CalcCorrection(2,xL2);
00259 if (nrows > 6) dXCorr += m_Corrections[k].Chair->CalcCorrection(5+kTpcOutIn,xL2);
00260 dE *= TMath::Exp(-dXCorr);
00261 goto ENDL;
00262 case kTpcdEdxCor:
00263 break;
00264 case ktpcMethaneIn:
00265 VarX = gas->percentMethaneIn*1000./gas->barometricPressure;
00266 break;
00267 case ktpcGasTemperature:
00268 VarX = gas->outputGasTemperature;
00269 break;
00270 case ktpcWaterOut:
00271 VarX = gas->ppmWaterOut;
00272 break;
00273 case kSpaceCharge:
00274 if (cor[2*kTpcOutIn ].min <= CdEdx.QRatio && CdEdx.QRatio <= cor[2*kTpcOutIn ].max &&
00275 cor[2*kTpcOutIn+1].min <= CdEdx.DeltaZ && CdEdx.DeltaZ <= cor[2*kTpcOutIn+1].max)
00276 dE *= TMath::Exp(-m_Corrections[k].Chair->CalcCorrection(2*kTpcOutIn ,CdEdx.QRatio)
00277 -m_Corrections[k].Chair->CalcCorrection(2*kTpcOutIn+1,CdEdx.DeltaZ));
00278 goto ENDL;
00279 case kEdge:
00280 VarX = CdEdx.PhiR;
00281 if (corl->type == 200) {
00282 VarX = TMath::Abs(CdEdx.edge);
00283 if (corl->min > 0 && corl->min > VarX ) return 2;
00284 if (corl->max > 0 && VarX > corl->max) return 2;
00285 }
00286 break;
00287 case kPhiDirection:
00288 VarX = 999.;
00289 if (TMath::Abs(CdEdx.xyzD[0]) > 1.e-7) VarX = TMath::Abs(CdEdx.xyzD[1]/CdEdx.xyzD[0]);
00290 break;
00291 default:
00292 goto ENDL;
00293 }
00294 if (TMath::Abs(corl->npar) >= 100 || iCut) {
00295 Int_t iok = 2;
00296 if (corl->min >= corl->max) {
00297 iok = 0;
00298 } else {
00299 for (; l < nrows; l += 2) {
00300 corl = cor + l;
00301 if (corl->min <= VarX && VarX <= corl->max) {
00302 iok = 0;
00303 break;
00304 }
00305 }
00306 }
00307 if (iok) return iok;
00308 }
00309 if (corl->npar%100) dE *= TMath::Exp(-m_Corrections[k].Chair->CalcCorrection(l,VarX));
00310 ENDL:
00311 CdEdx.C[k].dE = dE;
00312 CdEdx.C[k].dEdx = CdEdx.C[k].dE/CdEdx.dx;
00313 CdEdx.C[k].dEdxL = TMath::Log(CdEdx.C[k].dEdx);
00314 }
00315
00316 memcpy (&CdEdx.dE, &CdEdx.C[kTpcLast].dE, sizeof(dE_t));
00317 return 0;
00318 }
00319
00320 Int_t StTpcdEdxCorrection::dEdxTrackCorrection(EOptions opt, Int_t type, dst_dedx_st &dedx) {
00321 Double_t LogTrackLength = TMath::Log((Double_t) (dedx.ndedx/100));
00322 Int_t nrows = 0;
00323 Double_t I70L;
00324 Int_t k = opt;
00325 if (! m_Corrections[k].Chair) return 0;
00326 switch (k) {
00327 case kTpcLengthCorrection:
00328 nrows = (((St_tpcCorrection *) m_Corrections[k].Chair->Table())->GetTable())->nrows;
00329 switch (type) {
00330 case 0:
00331 case 1:
00332 if (nrows > 1+4*type) {
00333 dedx.dedx[0] *= TMath::Exp(-m_Corrections[k].Chair->CalcCorrection( 4*type,LogTrackLength));
00334 dedx.dedx[1] = m_Corrections[k].Chair->CalcCorrection(1+4*type,LogTrackLength);
00335 }
00336 if (nrows > 6+2*type) {
00337 dedx.dedx[0] *= TMath::Exp(-m_Corrections[k].Chair->CalcCorrection(6+2*type,LogTrackLength));
00338 }
00339 break;
00340 default:
00341 break;
00342 }
00343 break;
00344 case kTpcdEdxCor:
00345 I70L = TMath::Log(1.e6*dedx.dedx[0]);
00346 if (I70L > 0) dedx.dedx[0] *= TMath::Exp(-m_Corrections[k].Chair->SumSeries(0,I70L));
00347 break;
00348 default:
00349 break;
00350 }
00351 return 0;
00352 }
00353
00354 void StTpcdEdxCorrection::SetCorrection(Int_t k, St_tpcCorrection *m) {
00355 if (m && k >=0 && k < kTpcAllCorrections && m_Corrections[k].Name) {
00356 m_Corrections[k].Chair = new St_tpcCorrectionC(m);
00357 tpcCorrection_st *cor = m->GetTable();
00358 m_Corrections[k].nrows = cor->nrows;
00359 gMessMgr->Warning() << "StTpcdEdxCorrection::SetCorrection " << m_Corrections[k].Name
00360 << " \thas been set with nrows = " << m_Corrections[k].nrows << endm;
00361 {
00362 TDatime t[2];
00363 St_db_Maker::GetValidity(m,t);
00364 gMessMgr->Warning() << " \tValidity:" << t[0].GetDate() << "/" << t[0].GetTime()
00365 << " ----- " << t[1].GetDate() << "/" << t[1].GetTime() << endm;
00366 }
00367 if (Debug()) m->Print(0,m_Corrections[k].nrows);
00368 }
00369 }
00370
00371 void StTpcdEdxCorrection::SetTpcSecRowB (St_TpcSecRowCor *m) {
00372 if (m) {
00373 m_TpcSecRowB = m;
00374 gMessMgr->Warning() << "StTpcdEdxCorrection::SetTpcSecRowB " << m_Corrections[kTpcSecRowB].Name << "/"
00375 << m_Corrections[kTpcSecRowB].Title << endm;
00376 gMessMgr->Warning() << " \tcorrection has been set" << endm;
00377 {
00378 TDatime t[2];
00379 St_db_Maker::GetValidity(m,t);
00380 gMessMgr->Warning() << " Validity:" << t[0].GetDate() << "/" << t[0].GetTime()
00381 << " ----- " << t[1].GetDate() << "/" << t[1].GetTime() << endm;
00382 }
00383 }
00384 }
00385
00386 void StTpcdEdxCorrection::SetTpcSecRowC (St_TpcSecRowCor *m) {
00387 if (m) {
00388 m_TpcSecRowC = m;
00389 gMessMgr->Warning() << "StTpcdEdxCorrection::SetTpcSecRowC " << m_Corrections[kTpcSecRowC].Name << "/"
00390 << m_Corrections[kTpcSecRowC].Title << endm;
00391 gMessMgr->Warning() << " \tcorrection has been set" << endm;
00392 {
00393 TDatime t[2];
00394 St_db_Maker::GetValidity(m,t);
00395 gMessMgr->Warning() << " Validity:" << t[0].GetDate() << "/" << t[0].GetTime()
00396 << " ----- " << t[1].GetDate() << "/" << t[1].GetTime() << endm;
00397 }
00398 }
00399 }
00400
00401
00402
00403 void StTpcdEdxCorrection::SettpcGas (St_tpcGas *m) {
00404 if (m) {
00405 m_tpcGas = m;
00406 gMessMgr->Warning() << "StTpcdEdxCorrection::SettpcGas St_tpcGas has been set" << endm;
00407 {
00408 TDatime t[2];
00409 St_db_Maker::GetValidity(m,t);
00410 gMessMgr->Warning() << " Validity:" << t[0].GetDate() << "/" << t[0].GetTime()
00411 << " ----- " << t[1].GetDate() << "/" << t[1].GetTime() << endm;
00412 }
00413
00414 if (Debug()) m_tpcGas->Print(0,m_tpcGas->GetNRows());
00415 }
00416 }
00417
00418 void StTpcdEdxCorrection::Print(Option_t *opt) const {
00419 if (! mdEdx) return;
00420 cout << "StTpcdEdxCorrection:: Sector/row/pad " << mdEdx->sector << "/" << mdEdx->row << "/" << mdEdx->pad << endl;
00421 cout << "Npads/Ntbins " << mdEdx->Npads << "/" << mdEdx->Ntbins
00422 << "\tdrift distance / O2 / O2W " << mdEdx->ZdriftDistance << "/" << mdEdx->ZdriftDistanceO2 << "/" << mdEdx->ZdriftDistanceO2W << endl;
00423 cout << "Local xyz " << mdEdx->xyz[0] << "\t" << mdEdx->xyz[1] << "\t" << mdEdx->xyz[2] << endl;
00424 cout << "Local xyzD " << mdEdx->xyzD[0] << "\t" << mdEdx->xyzD[1] << "\t" << mdEdx->xyzD[2] << endl;
00425 cout << "dx " << mdEdx->dx << " dE " << mdEdx->dE << " dE/dx " << mdEdx->dEdx << " log(dE/dx) " << mdEdx->dEdxL << endl;
00426 for (Int_t k = kUncorrected; k < kTpcAllCorrections; k++) {
00427 cout << m_Corrections[k].Name << "\t" << m_Corrections[k].Title
00428 << "\tdE " << mdEdx->C[k].dE
00429 << "\tdE/dx " << mdEdx->C[k].dEdx
00430 << "\tlog(dE/dx) " << mdEdx->C[k].dEdxL << endl;
00431 }
00432 }