Changeset 2376 for branches/newmole
- Timestamp:
- 09/30/08 16:36:15 (3 months ago)
- Location:
- branches/newmole/source
- Files:
-
- 23 modified
-
atmdat_adfa.cpp (modified) (1 diff)
-
atmdat_ligbar.cpp (modified) (2 diffs)
-
atom_feii.cpp (modified) (5 diffs)
-
cddefines.h (modified) (2 diffs)
-
container_classes.h (modified) (1 diff)
-
cont_createmesh.cpp (modified) (1 diff)
-
cool_iron.cpp (modified) (1 diff)
-
date.h (modified) (1 diff)
-
ionbal.h (modified) (2 diffs)
-
ion_recomb.cpp (modified) (4 diffs)
-
ion_recomb_Badnell.cpp (modified) (10 diffs)
-
iter_startend.cpp (modified) (2 diffs)
-
parse_grain.cpp (modified) (1 diff)
-
parse_punch.cpp (modified) (1 diff)
-
parse_set.cpp (modified) (7 diffs)
-
punch_do.cpp (modified) (1 diff)
-
radius_increment.cpp (modified) (4 diffs)
-
rfield.h (modified) (2 diffs)
-
rt_diffuse.cpp (modified) (12 diffs)
-
rt_line_one.cpp (modified) (4 diffs)
-
stars.cpp (modified) (2 diffs)
-
temp_change.cpp (modified) (8 diffs)
-
zero.cpp (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/newmole/source/atmdat_adfa.cpp
r2112 r2376 863 863 ASSERT( ipLo < ipHi ); 864 864 865 # ifndef NDEBUG865 # if !defined NDEBUG || defined ASSERTDEBUG 866 866 long ipISO = ipH_LIKE; 867 867 long nelem = ipHYDROGEN; 868 # endif868 # endif 869 869 ASSERT( N_(ipLo) < N_(ipHi) ); 870 870 ASSERT( N_(ipHi) <= 5 ); -
branches/newmole/source/atmdat_ligbar.cpp
r1780 r2376 130 130 /* tarray(ipLnGF) = gf; tarray(ipLnBolt) excit temp kelvin */ 131 131 /* 132 *cs2s2p = gbar*197.47*EVDEGK*t2s2p->Lo->gf/t2s2p->EnergyK;133 */134 *cs2s2p = gbar*197.47*EVDEGK*t2s2p->Emis->gf/t2s2p->EnergyK;132 *cs2s2p = gbar*197.47*EVDEGK*t2s2p->Lo->gf/t2s2p->EnergyK; 133 */ 134 *cs2s2p = gbar*197.47*EVDEGK*t2s2p->Emis->gf/t2s2p->EnergyK; 135 135 /* small correction factors to CMcW83 2s-2p fits: 136 136 * fits 3.57% too small compared to R-matrix calc. for Mg X. … … 167 167 /* tarray(ipLnGF) = gf */ 168 168 /**cs2s3p = gbar*197.47*EVDEGK*t2s3p->Lo->gf/t2s3p->EnergyK;*/ 169 *cs2s3p = gbar*197.47*EVDEGK*t2s3p->Emis->gf/t2s3p->EnergyK;169 *cs2s3p = gbar*197.47*EVDEGK*t2s3p->Emis->gf/t2s3p->EnergyK; 170 170 /* cs2s3p = gbar * 197.47*eVdegK * GF2/(e2/1.60184e-12) 171 171 * */ -
branches/newmole/source/atom_feii.cpp
r2346 r2376 480 480 fclose(ioDATA); 481 481 482 /* now get radiation data. 483 * These are from the following data sources: 484 >>refer fe2 as Nahar, S., 1995, A&A 293, 967 485 >>refer fe2 as Quinet, P., LeDourneuf, M., & Zeipppen C.J., 1996, A&AS, 120, 361 486 >>refer fe2 as Furh, J.R., Martin, G.A., & Wiese, W.L., 1988; J Phys Chem Ref Data 17, Suppl 4 487 >>refer fe2 as Giridhar, S., & Arellano Ferro, A., 1995; Ref Mexicana Astron Astrofis 31, 23 488 >>refer fe2 as Kurucz, R.L., 1995, SAO CD ROM 23 489 */ 482 /* transition probabilities */ 490 483 491 484 if( trace.lgTrace ) 492 {493 485 fprintf( ioQQQ," FeIICreate opening fe2rad.dat:"); 494 }495 486 496 487 ioDATA = open_data( "fe2rad.dat", "r" ); … … 504 495 /* scan off three integers */ 505 496 sscanf( chLine ,"%ld%ld%ld",&lo, &ihi, &m1); 506 if( lo!=3 || ihi!=1 || m1!=28 ) 507 { 508 fprintf( ioQQQ, " fe2rad.dat has the wrong magic numbers, expected 3 1 28\n" ); 497 const int nYrFe2Rad=8, nMoFe2Rad=8, nDyFe2Rad=24; 498 if( lo!=nYrFe2Rad || ihi!=nMoFe2Rad || m1!=nDyFe2Rad ) 499 { 500 fprintf( ioQQQ, "DISASTER fe2rad.dat has the wrong magic numbers, expected " 501 "%2i %2i %2i and got %2li %2li %2li\n" , 502 nYrFe2Rad, nMoFe2Rad, nDyFe2Rad, 503 lo, ihi, m1); 509 504 cdEXIT(EXIT_FAILURE); 510 505 } … … 534 529 &lo, &ihi, &m1, &m2 , 535 530 &save[0], &save[1] , &m3); 536 /* the pointeres ihi and lo within this array were531 /* the indices ihi and lo within this array were 537 532 * read in from the line above */ 538 533 Fe2LevN[ihi-1][lo-1].Lo->g = (realnum)m1; … … 545 540 else 546 541 Fe2LevN[ihi-1][lo-1].EnergyWN = Fe2Energies[ihi-1]-Fe2Energies[lo-1]; 547 if( Fe2LevN[ihi-1][lo-1].Emis->Aul == 1e3f ) 548 { 549 /*realnum xmicron; 550 xmicron = 1e4f/ Fe2LevN[ihi-1][lo-1].EnergyWN;*/ 551 /* these are made-up intercombination lines, set gf to 1e-5 552 * then get better A */ 542 543 /* Aul == -1 indicates intercombination line with no real Aul */ 544 if( fp_equal( Fe2LevN[ihi-1][lo-1].Emis->Aul , -1.0f ) ) 545 { 546 /* these are made-up intercombination lines, set gf to 1e-5 */ 553 547 Fe2LevN[ihi-1][lo-1].Emis->gf = 1e-5f; 554 548 549 /* get corresponding A */ 555 550 Fe2LevN[ihi-1][lo-1].Emis->Aul = Fe2LevN[ihi-1][lo-1].Emis->gf*(realnum)TRANS_PROB_CONST* 556 551 POW2(Fe2LevN[ihi-1][lo-1].EnergyWN)*Fe2LevN[ihi-1][lo-1].Lo->g/Fe2LevN[ihi-1][lo-1].Hi->g; 557 558 /*fprintf(ioQQQ," %e from 1e3 to %e\n",xmicron , Fe2LevN[ihi-1][lo-1].Emis->Aul );*/ 559 } 560 /* this is the last column of fe2rad.dat, and is 1, 2, or 3. 552 } 553 554 /* the last column of fe2rad.dat, and is 1, 2, or 3. 561 555 * 1 signifies that transition is permitted, 562 556 * 2 is semi-forbidden … … 564 558 * transition is from 64 */ 565 559 ncs1[ihi-1][lo-1] = (int)m3; 566 /*if( m3==1 )567 fprintf(ioQQQ,"DEBUG fe2 new old energ\t%.5e\t%.5e\t%.3e\n",568 Fe2Energies[ihi-1]-Fe2Energies[lo-1],569 Fe2LevN[ihi-1][lo-1].EnergyWN ,570 (Fe2Energies[ihi-1]-Fe2Energies[lo-1]-Fe2LevN[ihi-1][lo-1].EnergyWN)/571 Fe2LevN[ihi-1][lo-1].EnergyWN572 );*/573 560 } 574 561 } -
branches/newmole/source/cddefines.h
r2346 r2376 39 39 #include <memory> 40 40 #include <stdexcept> 41 #include <algorithm> 41 42 #ifdef DMALLOC 42 43 #include <dmalloc.h> … … 595 596 #undef ASSERT 596 597 #ifndef STD_ASSERT 597 # ifdef NDEBUG 598 # ifdef ASSERTDEBUG 599 # define ASSERT(exp) if (!(exp)) MyAssert(__FILE__, __LINE__) 600 # elif NDEBUG 598 601 # define ASSERT(exp) ((void)0) 599 # elif defined ASSERTDEBUG600 # define ASSERT(exp) if (!(exp)) MyAssert(__FILE__, __LINE__)601 602 # else 602 603 /* the do { ... } while ( 0 ) prevents bugs in code like this: -
branches/newmole/source/container_classes.h
r2112 r2376 66 66 } 67 67 public: 68 typedef random_access_iterator_tag iterator_category; 69 typedef T value_type; 70 typedef T& reference; 71 typedef const T& const_reference; 72 typedef T* pointer; 73 typedef const T* const_pointer; 74 typedef size_t size_type; 75 typedef ptrdiff_t difference_type; 76 68 77 // constructors 69 78 basic_pntr( T* p0, T* p1, T* p2 ) -
branches/newmole/source/cont_createmesh.cpp
r2346 r2376 538 538 rfield.SummedOcc = (realnum*)MALLOC((size_t)(rfield.nupper*sizeof(realnum)) ); 539 539 rfield.ConOTS_local_photons = (realnum*)MALLOC((size_t)(rfield.nupper*sizeof(realnum)) ); 540 rfield.DiffuseEscape = (realnum*)MALLOC((size_t)(rfield.nupper*sizeof(realnum)) ); 540 541 rfield.TotDiff2Pht = (realnum*)MALLOC((size_t)(rfield.nupper*sizeof(realnum)) ); 541 542 rfield.ConOTS_local_OTS_rate = (realnum*)MALLOC((size_t)(rfield.nupper*sizeof(realnum)) ); -
branches/newmole/source/cool_iron.cpp
r1830 r2376 327 327 { 328 328 /* spacing needed to get proper trace convergence output */ 329 fprintf( ioQQQ, " FeIILevelPops5 returned cool=%.2e heat=%.2e derivative=%.2e\n ",329 fprintf( ioQQQ, " FeIILevelPops5 returned cool=%.2e heat=%.2e derivative=%.2e\n", 330 330 FeII.Fe2_large_cool,FeII.Fe2_large_heat ,FeII.ddT_Fe2_large_cool); 331 331 } -
branches/newmole/source/date.h
r2346 r2376 14 14 #define YEAR 108 15 15 /* month, January is 0, December is 11 */ 16 #define MONTH 716 #define MONTH 8 17 17 /* day is correct */ 18 18 #define DAY 9 -
branches/newmole/source/ionbal.h
r1830 r2376 220 220 **lgRR_Badnell_rate_coef_exist; 221 221 222 /** use the mean Badnell rates in place of existing hacks? */223 bool lg_use_DR_Badnell_rate_coef_mean_ion;224 225 222 /** do we use new Badnell rates? */ 226 223 bool lgDR_recom_Badnell_use, … … 267 264 bool lgSupDie[2]; 268 265 269 /** this is fudge factor for scaled Nussbaumer and Storey dielectronic270 recombination, set with dielectronic kludge command, usually 1 */271 realnum GuessDiel[4];272 273 266 /** following all for 3-body recombination */ 274 267 /** lgNoCota flag set with no three body recombination */ -
branches/newmole/source/ion_recomb.cpp
r2023 r2376 43 43 fac2, 44 44 factor, 45 DielRecomRateCoef_HiT[LIMELM] , 46 DielRecomRateCoef_LowT[LIMELM] , 47 ChargeTransfer[LIMELM] , 48 t4m1, 49 tefac; 45 DielRecomRateCoef_HiT[LIMELM], 46 DielRecomRateCoef_LowT[LIMELM], 47 ChargeTransfer[LIMELM]; 50 48 51 49 /* these are used for adding noise to rec coef */ … … 72 70 73 71 atmdat.nsbig = MAX2(dense.IonHigh[nelem]+1,atmdat.nsbig); 74 t4m1 = 1e4/phycon.te;75 72 fac2 = 1e-14*phycon.sqrte; 76 73 … … 198 195 DielRecomRateCoef_LowT[ion] = 0.; 199 196 if( ((n_bnd_elec_after_recomb-1) != 2) && 200 ((n_bnd_elec_after_recomb-1) != 10) && 201 ((n_bnd_elec_after_recomb-1) != 18) ) 202 { 203 tefac = ff[ion]*t4m1; 197 ((n_bnd_elec_after_recomb-1) != 10) && 198 ((n_bnd_elec_after_recomb-1) != 18) ) 199 { 204 200 /* do not do iron here since all dn = 0 DR either Badnell or a guess */ 205 if( ff[ion] != 0. && nelem!=ipIRON ) 206 { 201 if( ff[ion] != 0. && nelem != ipIRON ) 202 { 203 double t4m1 = 1e4/phycon.te; 204 double tefac = ff[ion]*t4m1; 207 205 /* >>chng 06 feb 14, O+3 ff[ion] is very negative, as a result exp goes to +inf 208 206 * at very low temperatures. This is a error in the Nussbaumer & Storey fits to DR. … … 221 219 else if( ionbal.lg_guess_coef ) 222 220 { 223 /* first guesses are those based on Nussbaumer & Storey and are from 224 * >>refer ion DR Ali, B., Blum, R. D., Bumgardner, T. E., Cranmer, S. R., 225 * >>refercon Ferland, G. J., Haefner, R. I., & Tiede, G. P. 1991, PASP, 103, 1182*/ 226 if( (ff[ion] == 0.) && (ion <= 3) ) 227 { 228 /* these are guessed dielectronic rec rates, taken from 229 * >>refer all dielectronic Ali, B., Blum, R. D., Bumgardner, T. E., Cranmer, S. R., Ferland, G. J., 230 * >>refercon Haefner, R. I., & Tiede, G. P. 1991, PASP, 103, 1182 */ 231 static double cludge[4]={3e-13,3e-12,1.5e-11,2.5e-11}; 232 233 /* >>chng 01 jun 30, make GuessDiel array */ 234 DielRecomRateCoef_LowT[ion] = ionbal.DielSupprs[1][ion]*cludge[ion]* 235 /* this is optional scale factor on set dielectronic rec command 236 * >>chng 06 feb 07, add Boltzmann factor to induce behavior 237 * like in Nussbaumer & Storey */ 238 ionbal.GuessDiel[ion] * sexp( 1e3 / phycon.te ); 239 } 240 /* this implements the low-T kludge dielectronic command */ 241 else 242 { 243 /* assume the recombination rate is the coefficient scanned off the steve option, times the charge 244 * before recombination raised to a power */ 245 double fitcoef[3][3] = 246 { 247 /* L- shell, Z=17-23 */ 248 {-52.5073,+5.19385,-0.126099} , 249 /* M-shell (Z=9-15) */ 250 {-10.9679,1.66397,-0.0605965} , 251 /* N shell */ 252 {-3.95599,1.61884,-0.194540} 253 }; 254 255 /* these implement the guesses in 256 * Kraemer, S.B., Ferland, G.J., & Gabel, J.R. 2004, ApJ, 604, 556 */ 257 if( nelem==ipIRON ) 258 { 259 int nshell; 260 if( (n_bnd_elec_after_recomb>=4) && (n_bnd_elec_after_recomb<=11) ) 261 nshell = 0; 262 else if( (n_bnd_elec_after_recomb>=12) && (n_bnd_elec_after_recomb<=19 )) 263 nshell = 1; 264 else 265 nshell = 2; 266 /* n_bnd_elec_after_recomb is the number of bound electrons */ 267 DielRecomRateCoef_LowT[ion] = fitcoef[nshell][0] + 268 fitcoef[nshell][1]*(ion+1) + 269 fitcoef[nshell][2]*POW2(ion+1.); 270 DielRecomRateCoef_LowT[ion] = 1e-10*pow(10.,DielRecomRateCoef_LowT[ion]); 271 272 } 273 else 274 { 275 /* this is guess for all other elements presented in 276 * >>refer all DR Kraemer, S.B., Ferland, G.J., & Gabel, J.R. 2004, ApJ, 604, 556 */ 277 DielRecomRateCoef_LowT[ion] = 3e-12*pow(10.,(double)(ion+1)*0.1); 278 } 279 /* >>chng 06 feb 02, add option to use mean of Badnell DR in place 280 * of these hacks */ 281 if( ionbal.lg_use_DR_Badnell_rate_coef_mean_ion ) 282 DielRecomRateCoef_LowT[ion] = ionbal.DR_Badnell_rate_coef_mean_ion[ion]; 283 } 221 /* use mean of Badnell DR rates */ 222 DielRecomRateCoef_LowT[ion] = ionbal.DR_Badnell_rate_coef_mean_ion[ion]; 284 223 /* include optional noise here 285 224 * >>chng 06 feb 07, move noise down to here so that use for both -
branches/newmole/source/ion_recomb_Badnell.cpp
r1918 r2376 273 273 char *chs; 274 274 275 #define BIGGEST_INDEX_TO_USE 103 275 const int BIGGEST_INDEX_TO_USE = 103; 276 276 277 277 /* Declaration of data file name array - done by Kausalya */ … … 974 974 975 975 /* save mean rec coefficients - used to derive rates for those ions with none */ 976 for( ion=0; ion<LIMELM; ++ion ) 977 { 978 ionbal.DR_Badnell_rate_coef_mean_ion[ion] = 0.; 976 for( ion=0; ion < LIMELM; ++ion ) 979 977 nsumrec[ion] = 0; 980 } 978 981 979 TeUsed = phycon.te; 982 980 /* NB - for following loop important to go over all elements and all 983 981 * ions, not just active ones, since mean DR is used as the guess for 984 * DR rates for unknown species. */ 985 for( nelem=ipHYDROGEN; nelem<LIMELM; ++nelem ) 986 { 987 for( ion=0; ion<nelem+1; ++ion ) 982 * DR rates for unknown species. */ 983 multi_arr<double,2> DR_Badnell_rate_stack( LIMELM, LIMELM ); 984 for( nelem=ipHYDROGEN; nelem < LIMELM; ++nelem ) 985 { 986 for( ion=0; ion < nelem+1; ++ion ) 988 987 { 989 988 long int n_bnd_elec_before_recom , … … 992 991 n_bnd_elec_before_recom = nelem-ion; 993 992 n_bnd_elec_after_recom = nelem-ion+1; 994 # define DR2SMALL 1e-15995 993 /* Badnell dielectronic recombination rate coefficients */ 996 994 if( (ionbal.DR_Badnell_rate_coef[nelem][ion] = … … 1003 1001 { 1004 1002 ionbal.lgDR_Badnell_rate_coef_exist[nelem][ion] = true; 1005 if( ionbal.DR_Badnell_rate_coef[nelem][ion] > DR2SMALL) 1006 { 1007 /* keep track of mean DR rate for this ion */ 1008 ++nsumrec[ion]; 1009 ionbal.DR_Badnell_rate_coef_mean_ion[ion] += 1010 log10(ionbal.DR_Badnell_rate_coef[nelem][ion]); 1011 } 1003 /* keep track of mean DR rate for this ion */ 1004 DR_Badnell_rate_stack[ion][nsumrec[ion]++] = 1005 ionbal.DR_Badnell_rate_coef[nelem][ion]; 1012 1006 } 1013 1007 else … … 1113 1107 ionbal.lgDR_Badnell_rate_coef_exist[nelem][ion] = true; 1114 1108 ionbal.DR_Badnell_rate_coef[nelem][ion] = fitSum / phycon.te32; 1115 if( ionbal.DR_Badnell_rate_coef[nelem][ion]>DR2SMALL ) 1116 { 1117 ++nsumrec[ion]; 1118 ionbal.DR_Badnell_rate_coef_mean_ion[ion] += 1119 log10(ionbal.DR_Badnell_rate_coef[nelem][ion]); 1120 } 1109 DR_Badnell_rate_stack[ion][nsumrec[ion]++] = 1110 ionbal.DR_Badnell_rate_coef[nelem][ion]; 1121 1111 1122 1112 /* >>chng 06 jun 21 by Mitchell Martin, added new DR rate coefficient … … 1138 1128 ionbal.lgDR_Badnell_rate_coef_exist[nelem][ion] = true; 1139 1129 ionbal.DR_Badnell_rate_coef[nelem][ion] = fitSum / phycon.te32; 1140 if( ionbal.DR_Badnell_rate_coef[nelem][ion] > DR2SMALL ) 1141 { 1142 ++nsumrec[ion]; 1143 ionbal.DR_Badnell_rate_coef_mean_ion[ion] += 1144 log10(ionbal.DR_Badnell_rate_coef[nelem][ion]); 1145 } 1130 DR_Badnell_rate_stack[ion][nsumrec[ion]++] = 1131 ionbal.DR_Badnell_rate_coef[nelem][ion]; 1146 1132 } 1147 1133 1148 1134 /* this is the temperature in eV evaluated to the 3/2 power */ 1149 double te_eV32 = pow( phycon.te_eV, 1.5 );1135 double te_eV32 = pow( phycon.te_eV, 1.5 ); 1150 1136 1151 1137 /* >>chng 06 jul 07 by Mitchell Martin, added DR rate coefficient … … 1165 1151 ionbal.lgDR_Badnell_rate_coef_exist[nelem][ion+5] = true; 1166 1152 ionbal.DR_Badnell_rate_coef[nelem][ion+5] = fitSum / te_eV32; 1167 if( ionbal.DR_Badnell_rate_coef[nelem][ion+5] > DR2SMALL ) 1168 { 1169 ++nsumrec[ion+5]; 1170 ionbal.DR_Badnell_rate_coef_mean_ion[ion+5] += 1171 log10(ionbal.DR_Badnell_rate_coef[nelem][ion+5]); 1172 } 1153 DR_Badnell_rate_stack[ion+5][nsumrec[ion+5]++] = 1154 ionbal.DR_Badnell_rate_coef[nelem][ion+5]; 1173 1155 } 1174 1156 } … … 1176 1158 1177 1159 /* now get mean of the DR rates - may be used for ions with no DR rates */ 1178 for( ion=0; ion<LIMELM; ++ion ) 1179 { 1160 for( ion=0; ion < LIMELM; ++ion ) 1161 { 1162 ionbal.DR_Badnell_rate_coef_mean_ion[ion] = 0.; 1180 1163 if( nsumrec[ion] > 0 ) 1181 ionbal.DR_Badnell_rate_coef_mean_ion[ion] = 1182 pow(10., ionbal.DR_Badnell_rate_coef_mean_ion[ion]/nsumrec[ion]); 1164 { 1165 // we have collected all the DR rates above, now we calculate the logarithmic average 1166 // of the top 3/4 of the entries. this procedure has been checked not to result in 1167 // discontinuities in the average rate as a function of temperature. 1168 // NB NB if this code is changed, check again that the resulting average rates 1169 // are continuous, otherwise discontiuities in the ionization balance and heating- 1170 // cooling function may result. 1171 1172 // first sort the stack so that we can easily determine the largest rates 1173 sort( DR_Badnell_rate_stack.ptr( ion, 0 ), DR_Badnell_rate_stack.ptr( ion, nsumrec[ion] ) ); 1174 1175 double maxval = DR_Badnell_rate_stack[ion][nsumrec[ion]-1]; 1176 if( maxval > 0. ) 1177 { 1178 // now calculate logarithmic average of top three quarters of entries 1179 // we discard the lower quarter because many of these are very low or 0 1180 // NB NB we MUST use a fixed number of values at any temperature to 1181 // calculate the average since discontinuities would result otherwise! 1182 double sum = 0.; 1183 for( int j = nsumrec[ion]/4; j < nsumrec[ion]; ++j ) 1184 { 1185 double rate_one = log(max(DR_Badnell_rate_stack[ion][j],1e-5*maxval)); 1186 sum += rate_one; 1187 } 1188 sum /= (double)(nsumrec[ion] - nsumrec[ion]/4); 1189 ionbal.DR_Badnell_rate_coef_mean_ion[ion] = exp(sum); 1190 } 1191 } 1183 1192 /*fprintf(ioQQQ,"DEBUG %li %.2e\n", ion , ionbal.DR_Badnell_rate_coef_mean_ion[ion] );*/ 1184 1193 } … … 1190 1199 for( ion=0; ion<5; ion++ ) 1191 1200 { 1192 1193 1201 /* only do this rate if not already done by a previous approximation 1194 1202 * so following used for ion <= 3 */ … … 1289 1297 cdEXIT( EXIT_SUCCESS ); 1290 1298 } 1291 1292 1299 return; 1293 1300 } -
branches/newmole/source/iter_startend.cpp
r2346 r2376
