// // Class : NeutronTransport v1.0 (~750 lines of c++ code including comments) // Date : June 2009 // // Simulates a Neutron Transport System for a spherical bulk (medium) containing U235 and U238 nuclei. // // Thermal neutrons are relased from the center of a shperical bulk of radius m_radius and mass m_mass // containing only two nuclei U235 and U238. Using Monte Carlo methods, program simulates // the trajectories (i.e. the positions) of the secondary neutrons propagating in the bulk. // The secondaries can be prompt neutrons (from fission reaction) or scattered neutrons. // // At the end, end() method outputs the average neutron multiplication factor (<keff>) of the // system defined as the ratio: // // keff = (# neutrons generated in one generation) / (# neutrons generated in preceding generation) // // keff < 1 ==> results in sub-critial mass (means no chain reaction occurs) // keff = 1 ==> results in critial mass (means a chain reaction can start) // keff > 1 ==> results in super-critial mass (means a chain reaction definitely starts) // // You can find more information at: http://www1.gantep.edu.tr/~bingul/simulation/fission/ // and a main (driver) program at: http://www1.gantep.edu.tr/~bingul/simulation/fission/fission.cc // // Author : Dr. Ahmet Bingül <bingul(at)gantep.edu.tr> // #include <iostream> #include <iomanip> #include <cmath> #include <cstdio> #include <string> #include <cstdlib> #include <ctime> #include <vector> #include <fstream> using namespace std; class NeutronTransport { private: // *** private variables and methods starts with m_ *** double m_PI, m_A0, m_barn, m_A235, m_A238, m_N235, m_N238, m_density, m_purity, m_eAvr, m_eCharge, m_micro235[5], m_macro235[5], m_micro238[5], m_macro238[5], m_macroAll[5], m_P[5], m_lambda, m_heat, m_En, m_radius, m_mass, m_s235[5][20], m_s238[5][20], m_Energy[20], m_eThermal, m_pAbs, m_pCap, m_pFis, m_p235, m_pSct, m_pEla, m_pInE; bool m_showDetails; int m_evtMax, m_evtNum, m_genNum, m_genMax, m_nMax, **m_nNeutron, m_hit; int m_randomSeed; double m_randomFlat(void); int m_randomNubar(double, bool); double m_randomPathLength(void); double m_randomPromptEnergy(void); double m_randomScatteredEnergy(double, double, bool); void m_chain(int &, double*, double*, double*, double*, double*, double*, double*); void m_printDetails(int, string, double, double, double, double, double); int m_nint(double); void m_getData(void); void m_getProbabilities(double); bool m_isOutside(double, double, double); time_t m_timeStart, m_timeEnd; public: NeutronTransport(int =1, int =100, double =0.03, double =10.0, bool =false, int =0); void start(); void execute(); void end(); }; //------------------------------------------------------------------------------------------------------------ // Constructor for the NeutronTransport class // // max_evt (default is 1) : maximum number of events (borbarded neutrons) required // max_gen (default is 20) : maximum number of generation that a calculation performed // pur (default is 3%) : purtiy of the U235 in the bulk // rad (default is 10) : radius in cm of the spherical bulk // detail (default is false): true or false to allow to print details of an event // seed (default is 0) : seed of the random number generator // //------------------------------------------------------------------------------------------------------------ NeutronTransport::NeutronTransport(int max_evt, int max_gen, double pur, double rad, bool detail, int seed) { //*** Parameters used in the program *** // Description Unit // ---------------------------------------- ---------- m_PI = 3.14159265358979323846; // Number pi - m_A0 = 6.02214179e+23; // Avagadros number atoms/mole m_barn = 1.0e-24; // A conversion factor between barn and cm2 cm2 m_A235 = 235.0439299; // Atomic weigth of U235 u m_A238 = 238.0507826; // Atomic weigth of U235 u m_density = 18.75; // mass density of Uranium g/cm3 if(pur<0.9) m_density = 19.05; // mass density for purity < 90% g/cm3 m_eCharge = 1.602e-19; // electronic charge C m_eAvr = 2.0e+8 * m_eCharge; // Average energy released per fission J m_purity = pur; // purity of the U235 - m_N235 = m_purity*m_density*m_A0/m_A235; // Atom density of U235 1/cm3 m_N238 = (1.0-m_purity)*m_density*m_A0/m_A238; // Atom density of U238 1/cm3 m_heat = 0.0; // Average kinetic energy per fission J m_radius = rad; // Radius of the U235-U238 mixture (bulk) cm m_mass = m_density * 4.0*m_PI*pow(m_radius,3)/3.0; // mass of the sphrical bulk g m_eThermal= 0.0253; // Kinetic energy of thermal neutrons eV //*** Neutron counting stuff *** m_hit = 0; // see m_printDetails() routine m_nMax = 50000; // Maximum number of neutrons in one generation m_evtNum = 0; // Event Number counter (upto m_evtMax) m_genNum = 0; // Generation number counter (upto m_genMax) m_evtMax = max_evt; // Maximum number of events (default is 1) m_genMax = max_gen; // Maximum number of generations allowed (default is 20) m_showDetails = detail; // To show details of the each event if detail = true (default is false) //*** total number of neutrons generated *** m_nNeutron = new int* [m_evtMax]; for(int i=0; i<m_evtMax; i++) *(m_nNeutron+i) = new int [m_genMax]; //*** initilize the random number generator *** if( !seed ) m_randomSeed = (int) time(NULL); else m_randomSeed = seed; //*** to calculate program execution time count number of seconds *** time(&m_timeStart); } //------------------------------------------------------------------------------------------------------------ // Main method for the simulation. // Contains two main loops // event loop : loops over each event (i.e. maximum number of events) // generation loop : loops over the geneations corresponding to an event //------------------------------------------------------------------------------------------------------------ void NeutronTransport::execute() { // To save position of neutrons double *x, *y, *z, *e, *ux, *uy, *uz; x = new double [m_nMax]; y = new double [m_nMax]; z = new double [m_nMax]; e = new double [m_nMax]; ux = new double [m_nMax]; uy = new double [m_nMax]; uz = new double [m_nMax]; // event loop ********************************************************** for(int m_evtNum = 0; m_evtNum<m_evtMax; m_evtNum++) { // Each event starts with n = 1 neutron int n = 1; // Total # of neutrons generated for this event int ntot = 0; // initial positions of the neutrons for the first generation for(int j=0; j<m_nMax; j++) { double phi = 2.0*m_PI*m_randomFlat(); double costh = 2.0*m_randomFlat()-1.0; double sinth = sqrt(1.0-costh*costh); double rad = m_radius*m_randomFlat(); x[j] = rad*sinth*cos(phi); y[j] = rad*sinth*sin(phi); z[j] = rad*costh; ux[j] = -x[j]; uy[j] = -y[j]; uz[j] = -z[j]; //x[j] = y[j] = z[j] = ux[j] = uy[j] = uz[j] = 0.0; e[j] = m_eThermal; } // generation loop for(m_genNum = 0; m_genNum < m_genMax; m_genNum++) { m_chain(n, x, y, z, e, ux, uy, uz); m_nNeutron[m_evtNum][m_genNum] = n; ntot += n; if(n<=0) break; } // end of generation loop cout << "Event: " << setw(7) << m_evtNum+1 << " | Generation depth: " << setw(5) << m_genNum << " | Total # of genenerated neutrons: " << ntot << endl; } // end of event loop ************************************************* delete [] x; delete [] y; delete [] z; delete [] e; delete [] ux; delete [] uy; delete [] uz; } //------------------------------------------------------------------------------------------------------------ // The climax method // Performs the chain reaction for one generation. // // n number of neutrons in the generation // x,y,z position of each neutron to check if it it outside of the bulk //------------------------------------------------------------------------------------------------------------ void NeutronTransport::m_chain (int& n, double* x, double* y, double* z, double* e, double *ux, double *uy, double *uz){ if(n<=0) return; double d, phi, theta, xx, yy, zz, ee, uux, uuy, uuz; int i, j, k, l, ng; vector<double> X, Y, Z, E, Ux, Uy, Uz; k = ng = j = 0; // loop over the all neutrons inside the medium for(i=0; i<n; i++) { bool isAbsorbed = false; bool isScattered = false; bool isFission = false; bool isCaptured = false; bool isElastic = false; bool isInElastic = false; bool isPossible = true; m_getProbabilities( *(e+i) ); // Using probalities decide if absorbtion occur **************************** if( m_randomFlat() < m_pAbs ){ isAbsorbed = true; if( m_randomFlat() < m_pFis ) isFission = true; // fission else isCaptured = true; // capture } // if scattering occur else{ isScattered = true; if( m_randomFlat() < m_pEla ) isElastic = true; // elastic scatt. else isInElastic = true; // in-elastic scatt. } //************************************************************************ // print mother particle info m_printDetails(0, " Mother ",*(x+i), *(y+i), *(z+i), *(e+i), m_heat); if(isFission) { // is the nucleus U235 ? bool isU235 = false; if(m_randomFlat() < m_p235) isU235 = true; // Get number of prompt neutrons produced for the fission int np = m_randomNubar(*(e+i), isU235); for(l=1; l<=np; l++) { // Get a random free path length d = m_randomPathLength(); // Send the prompt neutron to a isotropiaclly random direction in space phi = 2.0*m_PI*m_randomFlat(); theta= acos(2.0*m_randomFlat()-1.0); uux = sin(theta)*cos(phi); uuy = sin(theta)*sin(phi); uuz = cos(theta); xx = *(x+i) + d*uux; yy = *(y+i) + d*uuy; zz = *(z+i) + d*uuz; // Assign a random kinetic energy to the prompt neutron ee = m_randomPromptEnergy(); // Check if the neutron is outside of the bulk if( m_isOutside(xx, yy, zz) ) m_printDetails(k+1, " Out of bulk ",xx, yy, zz, ee, m_heat); else{ m_heat += m_eAvr; m_printDetails(k+1, " Prompt ",xx, yy, zz, ee, m_heat); X.push_back(xx); Y.push_back(yy); Z.push_back(zz); E.push_back(ee); Ux.push_back(uux); Uy.push_back(uuy); Uz.push_back(uuz); ng++; k++; } } // end of prompt neutron loop } // end of isFission else if(isScattered) { // Get a random free path length for this energy d = m_randomPathLength(); // Send the scattered neutron to a isotropiaclly random direction in space phi = 2.0*m_PI*m_randomFlat(); theta= acos(2.0*m_randomFlat()-1.0); uux = sin(theta)*cos(phi); uuy = sin(theta)*sin(phi); uuz = cos(theta); xx = *(x+i) + d*uux; yy = *(y+i) + d*uuy; zz = *(z+i) + d*uuz; // Scattering angle // i.e. opening angle between new and old direction (it is found from dot product) double cosThetaS = *(ux+i)*uux + *(uy+i)*uuy + *(uz+i)*uuz; // Determine the random kinetic energy to this scattered neutron ee = m_randomScatteredEnergy(*(e+i), cosThetaS, isElastic); if(ee<0.0) isPossible = false; // Check if the neutron is outside of the bulk if( m_isOutside(xx, yy, zz) ) m_printDetails(k+1, " Out of bulk ",xx, yy, zz, ee, m_heat); else if(isPossible){ m_printDetails(k+1, " Scaterred ",xx, yy, zz, ee, m_heat); X.push_back(xx); Y.push_back(yy); Z.push_back(zz); E.push_back(ee); Ux.push_back(uux); Uy.push_back(uuy); Uz.push_back(uuz); ng++; k++; } else m_printDetails(k+1, " Captured ",xx, yy, zz, 0.0, m_heat); } else if(isCaptured) { m_printDetails(k+1, " Captured ",xx, yy, zz, 0.0, m_heat); } } // end of loop over the all neutrons inside the medium n = ng; if(ng<=0) return; for(i=0; i<(int) X.size(); i++){ *(x+i) = X.at(i); *(y+i) = Y.at(i); *(z+i) = Z.at(i); *(e+i) = E.at(i); *(ux+i) = Ux.at(i); *(uy+i) = Uy.at(i); *(uz+i) = Uz.at(i); } for(i=0; i<(int) X.size(); i++){ X.pop_back(); Y.pop_back(); Z.pop_back(); E.pop_back(); Ux.pop_back(); Uy.pop_back(); Uz.pop_back(); } } //------------------------------------------------------------------------------------------------------------ // Prints some initial information at the begining of the program. //------------------------------------------------------------------------------------------------------------ void NeutronTransport::start(){ cout << endl; cout << "NeutronTransport 1.0 - June 2009" << endl; cout << "A Basic Monte Carlo Neutron Transport Simulation " << endl << endl; cout << "--- Neutron Transport will start with the following initial values -------" << endl; cout << "Number of events : " << fixed << setw(8) << setprecision(3) << m_evtMax << endl; cout << "Radius of the bulk (cm) : " << fixed << setw(8) << setprecision(3) << m_radius << endl; cout << "Mass of the bulk (kg) : " << fixed << setw(8) << setprecision(3) << m_mass/1000. << endl; cout << "Purity of U(235) (%) : " << fixed << setw(8) << setprecision(3) << m_purity*100. << endl; cout << "Atom density of U(235) : " << scientific << setw(8) << setprecision(2) << m_N235 << endl; cout << "Atom density of U(238) : " << scientific << setw(8) << setprecision(2) << m_N238 << endl; cout << "Seed of random # gen. : " << setw(8) << m_randomSeed << endl; cout << "--------------------------------------------------------------------------" << endl; //*** Get cross-section data from the data file *** m_getData(); cout << endl << "Press [return] to continue..." << endl; getchar(); } //------------------------------------------------------------------------------------------------------------ // Finalises the NutronTransport after printing some statistics at the end of the program. //------------------------------------------------------------------------------------------------------------ void NeutronTransport::end() { int i, j, ng[m_genMax]; double keff[m_genMax], kerr[m_genMax]; // Evaluate and print keff for each generation for(j=0; j<m_genMax; j++){ keff[j] = kerr[j] = 0.0; for(ng[j] = 0, i=0; i<m_evtMax; i++) ng[j] += m_nNeutron[i][j]; } cout << endl << "--- Neutron Transport stoped. --------------------------------------------" << endl; cout << "Total Number of Events: " << m_evtMax << endl; cout << "Total Generation : " << m_genMax << endl << endl; cout << "Generation # nold nnew keff kerr " << endl; cout << "============ ========== ========== ======= =======" << endl; for(j=1; j<m_genMax; j++) { double ratio = double(ng[j]) / ng[j-1]; if(ratio<100.) { keff[j] = ratio; kerr[j] = sqrt( 1.0/ng[j] + 1.0/ng[j-1]) * ratio; } cout << setw(12) << j << setw(14) << ng[j-1] << setw(14) << ng[j] << fixed << setprecision(5) << setw(15) << keff[j] << fixed << setprecision(5) << setw(14) << kerr[j] << endl; } cout << "--------------------------------------------------------------------------" << endl << endl; // Evaluate <keff> (average of keff) and corresponding error normalised to one generation string conclusion; double sum, keffavr, kefferr, timediff; int ne; for(sum=0.0, ne = 0, i=0; i<m_genMax; i++){ if(keff[i]) {sum += keff[i]; ne++;} } keffavr = sum/ne; for(sum=0.0, i=0; i<m_genMax; i++) if(keff[i]) sum += (keff[i]-keffavr)*(keff[i]-keffavr); kefferr = sqrt(sum/(ne-1.))/sqrt(ne); if ( keffavr + kefferr > 1.0 && keffavr - kefferr < 1.0 ) conclusion = "The bulk seems to be critical."; if ( keffavr + kefferr > 1.0 && keffavr - kefferr > 1.0 ) conclusion = "The bulk seems to be super-critical."; if ( keffavr + kefferr < 1.0 ) conclusion = "The bulk seems to be sub-critical."; time(&m_timeEnd); timediff = m_timeEnd - m_timeStart; cout << "<keff> = " << fixed << setprecision(5) << keffavr << " +- " << kefferr << endl; cout << conclusion << endl; cout << "Time = " << fixed << setprecision(5) << setw(10) << timediff << " s." << endl; cout << "Purity = " << fixed << setprecision(5) << setw(10) << m_purity*100 << " %." << endl; cout << "Mass = " << fixed << setprecision(5) << setw(10) << m_mass/1000. << " kg." << endl; cout << "Radius = " << fixed << setprecision(5) << setw(10) << m_radius << " cm." << endl; cout << "Heat en.= " << scientific << setprecision(2) << setw(10) << m_heat << " J." << endl << endl; } //------------------------------------------------------------------------------------------------------------ // Prints deteiled information of id, positions, energy, ... //------------------------------------------------------------------------------------------------------------ void NeutronTransport::m_printDetails (int code, string id, double xx, double yy, double zz, double e1, double e2) { const int realWidth = 11, precision = 4, intWidth = 10; if(m_showDetails) { if( !m_hit ) { cout << "--- Detailed Event List including Generations (up to " << m_genMax; cout << ") -------------------------------------------------------------------" << endl; cout << endl; cout << " Event# Generation Track Neutron id"; cout << " x y z Energy(eV) Heat Energy(J)" << endl; cout << " ====== ========== ===== =========="; cout << " ====== ====== ====== ========== ==============" << endl; m_hit++; } cout << setw(7) << m_evtNum+1 << setw(14) << m_genNum+1; cout << setw(intWidth) << code << id << fixed << setprecision(precision) << setw(realWidth) << xx << setw(realWidth) << yy << setw(realWidth) << zz << setw(realWidth) << scientific << setprecision(2) << e1 << setw(realWidth) << scientific << setprecision(2) << e2 << endl; } } //------------------------------------------------------------------------------------------------------------ // Calculates the reaction probabilities using neutron cross-section data evaluated // for U235 and U238 for the given kinetic energy. //------------------------------------------------------------------------------------------------------------ void NeutronTransport::m_getProbabilities(double energy) { //for(int i=0; i<5; i++) m_micro235[i] = m_micro238[i] = 0.0; //=== Microscopic cross-sections === if( energy <= m_eThermal ){ m_micro235[0]=99.0; m_micro238[0]=0.0; // 0 -> Radiation capture m_micro235[1]=582.0; m_micro238[1]=0.0; // 1 -> Fission m_micro235[2]=13.78; m_micro238[2]=8.871; // 2 -> Elastic scattering m_micro235[3]=0.2; m_micro238[3]=0.0; // 3 -> In-elastic scattering m_micro235[4]=695.0; m_micro238[4]=11.551; // 4 -> Total cross-sections } else{ // values are obtained by linear extrapolation for(int i=0; i<19; i++){ if(energy >= m_Energy[i] && energy <= m_Energy[i+1]){ double de = (energy-m_Energy[i])/(m_Energy[i+1]-m_Energy[i]); for(int j=0; j<5; j++){ m_micro235[j] = (m_s235[j][i+1]-m_s235[j][i])*de + m_s235[j][i]; m_micro238[j] = (m_s238[j][i+1]-m_s238[j][i])*de + m_s238[j][i]; } } } } //=== Macroscopic cross-sections === for(int i=0; i<5; i++){ m_macro235[i] = m_N235 * m_micro235[i] * m_barn; m_macro238[i] = m_N238 * m_micro238[i] * m_barn; m_macroAll[i] = m_macro235[i] + m_macro238[i]; } //=== mean free path of a neutron in the bulk === m_lambda = 1.0/m_macroAll[4]; //=== Reaction probabilities === m_pAbs = (m_macroAll[0]+m_macroAll[1])/m_macroAll[4]; // absorbtion probability wrt total cs m_pCap = m_macroAll[0]/(m_macroAll[0]+m_macroAll[1]); // capture probability wrt absorbtion cs m_pFis = m_macroAll[1]/(m_macroAll[0]+m_macroAll[1]); // fission probability wrt absorbtion cs m_p235 = m_macro235[1]/(m_macro235[1]+m_macro238[1]); // U235 probability wrt fission cs m_pSct = (m_macroAll[2]+m_macroAll[3])/m_macroAll[4]; // scattering probability wrt total cs m_pEla = m_macroAll[2]/(m_macroAll[2]+m_macroAll[3]); // elastic s. probability wrt scattering cs m_pInE = m_macroAll[2]/(m_macroAll[2]+m_macroAll[3]); // in-elastic s. probability wrt scattering cs } //------------------------------------------------------------------------------------------------------------ // Reads data from the 'fission-cross-section.data' to get energies (eV) and corresponding // microscopic cross-sections (in barn) for U235 and U238. // Datum are stroed in the arrays of m_Energy[j], m_s235[i][j] and m_s238[i][j] (i=0,4; j=0,19) // // ** You can download data file from: // http://www1.gantep.edu.tr/~bingul/simulation/fission/fission-cross-section.data // // ** Data is taken from the following web sites: // // - http://www.ncnr.nist.gov/resources/n-lengths/list.html // Nuclear Center for Nuclear Resarch // // - http://www.nndc.bnl.gov/ // National Nuclear Data Center (Brookhaven National Lab.) // ENDF Evaluated Nuclear (reaction) Datas File is used. //------------------------------------------------------------------------------------------------------------ void NeutronTransport::m_getData(void) { ifstream csDataFile ("fission-cross-section.data"); cout << "Getting cross-section data... " << endl; if( csDataFile.is_open() ) { // get 20-Kinetic energy value (in eV) for the cross-section calculation for(int j=0; j<20; j++) csDataFile >> m_Energy[j]; // get U235 cross section (in barn) data for(int j=0; j<20; j++){ for(int i=0; i<5; i++) csDataFile >> m_s235[i][j]; } // get U238 cross section (in barn) data for(int j=0; j<20; j++){ for(int i=0; i<5; i++) csDataFile >> m_s238[i][j]; } csDataFile.close(); cout << "done." << endl; } else{ cout << "Cannot open 'fission-cross-section.data'." << endl; cout << "You can download it from:" << endl; cout << "http://www1.gantep.edu.tr/~bingul/simulation/fission/fission-cross-section.data" << endl; exit(1); } if( m_showDetails == false ) return; cout << "--- U235 cross-section data ----------------------------------------------" << endl; cout<< " En. (ev) Capture Fission Elestic In-Elas Total " << endl; cout<< " ======== ======= ======= ======= ======= =======" << endl; for(int j=0; j<20; j++){ cout << scientific << setw(10) << setprecision(2) << m_Energy[j]; for(int i=0; i<5; i++) cout << fixed << setw(10) << setprecision(5) << m_s235[i][j]; cout << endl; } cout << "--- U238 cross-section data ----------------------------------------------" << endl; cout<< " En. (ev) Capture Fission Elestic In-Elas Total " << endl; cout<< " ======== ======= ======= ======= ======= =======" << endl; for(int j=0; j<20; j++){ cout << scientific << setw(10) << setprecision(2) << m_Energy[j]; for(int i=0; i<5; i++) cout << fixed << setw(10) << setprecision(5) << m_s238[i][j]; cout << endl; } cout << "--------------------------------------------------------------------------" << endl << endl; } //------------------------------------------------------------------------------------------------------------ // Returns true if the neutrons is outside of the bulk //------------------------------------------------------------------------------------------------------------ bool NeutronTransport::m_isOutside(double xx, double yy, double zz) { if( sqrt(xx*xx + yy*yy + zz*zz) > m_radius ) return true; return false; } //------------------------------------------------------------------------------------------------------------ // Returns nearest integer of a the double x //------------------------------------------------------------------------------------------------------------ int NeutronTransport::m_nint(double x) { if( (x - int(x)) >= 0.5) return int(x+1.0); else return int(x); } //*********************************************************************************************************** // Following methods, that performs MC simulation, use random numbers. // // m_randomFlat() : Returns a uniform random deviate between 0.0 and 1.0. // m_randomNeutronNumber() : Returns a random neutron number after fission // m_randomPathLength() : Returns a random free path for a neutron (avr = m_lambda) // m_randomPromptEnergy() : Returns a random energy for a prompt neutron. (avr ~ 2.5 MeV) // m_randomScatteredEnergy() : Returns a random energy for a scattered neutron. // //------------------------------------------------------------------------------------------------------------ // Returns a uniform random deviate between 0.0 and 1.0. // Based on: Park and Miller's "Minimal Standard" random number generator (Comm. ACM, 31, 1192, 1988) //------------------------------------------------------------------------------------------------------------ double NeutronTransport::m_randomFlat() { const int im = 2147483647, ia = 16807; const int iq = 127773, ir = 2836; const double m = 128.0/im; int k; double r; k = m_randomSeed / iq; m_randomSeed = ia*(m_randomSeed-k*iq) - ir*k; if(m_randomSeed < 0) m_randomSeed += im; r = m * (m_randomSeed/128); return r; } //------------------------------------------------------------------------------------------------------------ // Returns a normally distributed integer number of neutron generated per fission. // The width of the distribution is sigma and mean is energy dependent. // The result Nubar dpends also the nuclei. //------------------------------------------------------------------------------------------------------------ int NeutronTransport::m_randomNubar(double energy, bool isU235) { double mean, sigma = 1.0; double x, y, z; while(1){ x = 2.0 * m_randomFlat() - 1.0; y = 2.0 * m_randomFlat() - 1.0; z = x*x + y*y; if( z <= 1.0 ) break; } // convert energy in eV to MeV energy *= 1.0e-6; // U235 nucleus if(isU235){ if(energy<5.0) mean = 0.12*energy + 2.4; else mean = 0.16*energy + 2.2; } // U238 nucleus else{ if(energy<3.0) mean = 0.0666*energy + 2.4; else mean = 0.1654*energy + 2.1538; } return m_nint(mean + sigma*x*sqrt(-2.0*log(z)/z)); } //------------------------------------------------------------------------------------------------------------ // Returns a random path length obeying exponential decay law. // This is actually a poission distribution exp(-x/lambda). //------------------------------------------------------------------------------------------------------------ double NeutronTransport::m_randomPathLength() { return -m_lambda * log(m_randomFlat()); } //------------------------------------------------------------------------------------------------------------ // Returns a random kinetic energy in eV for a prompt neutron. // The values are taken from a Maxwellian (or Watt) distribution. // This distribution is still commonly used to describe the prompt fission neutron spectra. //------------------------------------------------------------------------------------------------------------ double NeutronTransport::m_randomPromptEnergy() { double emin = 0.05; // minimum value of the kinetic energy in MeV double emax = 20.00; // maximum value of the kinetic energy in MeV double Tm = 1.29; // Maxwellian temperature, somewhere between [1.290, 1.426] double eopt = Tm/2.0; // optimum value of the energy double scal = 2.0/(sqrt(m_PI)*pow(Tm,1.5)); // a scale factor double fmax = scal*sqrt(eopt)*exp(-eopt/Tm); // optimum value of the distribution // A rejection algorithm while(1) { double ptest = m_randomFlat()*fmax; double e = m_randomFlat()*(emax-emin) + emin; double fmb = scal*sqrt(e)*exp(-e/Tm); if (fmb > ptest) return e*1.0e+6; } } //------------------------------------------------------------------------------------------------------------ // Returns a kinetic energy in eV of the scattered neutron for the given // Ei : kinetic energy of the incident neutron in eV // cosThetaScat : the cosine of scattering angle // elastic : the scattering type (true for elastic and false for in-elastic scattering). // Note that aproximated values are used for the calculation. //------------------------------------------------------------------------------------------------------------ double NeutronTransport::m_randomScatteredEnergy(double Ei, double cosThetaScat, bool elastic) { double A = 237.0, Ef, Q; if(elastic){ Ef = Ei*(A+cosThetaScat)*(A+cosThetaScat) / ((A+1.)*(A+1.)); //Ef = Ei*(0.99 - m_randomFlat()/100.); } else{ // Reaction Q value of the in-elastic reaction if(m_randomFlat()<0.1) Q = 148.0e+3; else Q = 44.9e+3; if(Q>Ei) Ef = -1.0; // reaction is not possible else Ef = pow(sqrt(Ei)*cosThetaScat-A*sqrt(Ei-Q),2.) / ((A+1.)*(A+1.)); //else Ef = Ei*(0.99 - m_randomFlat()/100.) - Q; if(Ef<5.0e+04) Ef = -1.0; // no cross-section info } return Ef; }