|
| 1 | +#include <iostream> |
| 2 | +#include <vector> |
| 3 | +#include <cmath> |
| 4 | +#include "plasma_source.hpp" |
| 5 | +#include <stdlib.h> |
| 6 | +#include "openmc/random_lcg.h" |
| 7 | + |
| 8 | +#define RANDOM openmc::prn() |
| 9 | + |
| 10 | +namespace plasma_source { |
| 11 | + |
| 12 | +// default constructor |
| 13 | +PlasmaSource::PlasmaSource() {} |
| 14 | + |
| 15 | +// large constructor |
| 16 | +PlasmaSource::PlasmaSource(const double ion_density_ped, const double ion_density_sep, |
| 17 | + const double ion_density_origin, const double ion_temp_ped, |
| 18 | + const double ion_temp_sep, const double ion_temp_origin, |
| 19 | + const double pedistal_rad, const double ion_density_peak, |
| 20 | + const double ion_temp_peak, const double minor_radius, |
| 21 | + const double major_radius, const double elongation, |
| 22 | + const double triangularity, const double shafranov, |
| 23 | + const std::string plasma_type, const int plasma_id, |
| 24 | + const int number_of_bins, |
| 25 | + const double min_toroidal_angle, |
| 26 | + const double max_toroidal_angle ) { |
| 27 | + |
| 28 | + // set params |
| 29 | + ionDensityPedistal = ion_density_ped; |
| 30 | + ionDensitySeperatrix = ion_density_sep; |
| 31 | + ionDensityOrigin = ion_density_origin; |
| 32 | + ionTemperaturePedistal = ion_temp_ped; |
| 33 | + ionTemperatureSeperatrix = ion_temp_sep; |
| 34 | + ionTemperatureOrigin = ion_temp_origin; |
| 35 | + pedistalRadius = pedistal_rad; |
| 36 | + ionDensityPeaking = ion_density_peak; |
| 37 | + ionTemperaturePeaking = ion_temp_peak; |
| 38 | + minorRadius = minor_radius; |
| 39 | + majorRadius = major_radius; |
| 40 | + this->elongation = elongation; |
| 41 | + this->triangularity = triangularity; |
| 42 | + this->shafranov = shafranov; |
| 43 | + plasmaType = plasma_type; |
| 44 | + plasmaId = plasma_id; |
| 45 | + numberOfBins = number_of_bins; |
| 46 | + minToroidalAngle = min_toroidal_angle/180.*M_PI; |
| 47 | + maxToroidalAngle = max_toroidal_angle/180.*M_PI; |
| 48 | + |
| 49 | + setup_plasma_source(); |
| 50 | +} |
| 51 | + |
| 52 | +// destructor |
| 53 | +PlasmaSource::~PlasmaSource(){} |
| 54 | + |
| 55 | +// main master sample function |
| 56 | +void PlasmaSource::SampleSource(std::array<double,8> random_numbers, |
| 57 | + double &x, |
| 58 | + double &y, |
| 59 | + double &z, |
| 60 | + double &u, |
| 61 | + double &v, |
| 62 | + double &w, |
| 63 | + double &E) { |
| 64 | + double radius = 0.; |
| 65 | + int bin = 0; |
| 66 | + sample_source_radial(random_numbers[0],random_numbers[1],radius,bin); |
| 67 | + double r = 0.; |
| 68 | + convert_rad_to_rz(radius,random_numbers[2],r,z); |
| 69 | + convert_r_to_xy(r,random_numbers[3],x,y); |
| 70 | + sample_energy(bin,random_numbers[4],random_numbers[5],E); |
| 71 | + isotropic_direction(random_numbers[6],random_numbers[7], |
| 72 | + u,v,w); |
| 73 | + |
| 74 | +} |
| 75 | + |
| 76 | +/* |
| 77 | + * sample the pdf src_profile, to generate the sampled minor radius |
| 78 | + */ |
| 79 | +void PlasmaSource::sample_source_radial(double rn_store1, double rn_store2, |
| 80 | + double &sampled_radius, int &sampled_bin) { |
| 81 | + |
| 82 | + for ( int i = 0 ; i < numberOfBins ; i++ ) { |
| 83 | + if ( rn_store1 <= source_profile[i] ) { |
| 84 | + if ( i > 0 ) { |
| 85 | + sampled_radius = (float(i-1)*(binWidth)) + (binWidth*(rn_store2)); |
| 86 | + sampled_bin = i; |
| 87 | + return; |
| 88 | + } else { |
| 89 | + sampled_radius = binWidth*(rn_store2); |
| 90 | + sampled_bin = i; |
| 91 | + return; |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + std::cerr << "error" << std::endl; |
| 97 | + std::cerr << "Sample position greater than plasma radius" << std::endl; |
| 98 | + exit(1); |
| 99 | + return; |
| 100 | +} |
| 101 | + |
| 102 | +/* |
| 103 | + * sample the energy of the neutrons, updates energy neutron in mev |
| 104 | + */ |
| 105 | +void PlasmaSource::sample_energy(const int bin_number, double random_number1, double random_number2, |
| 106 | + double &energy_neutron) { |
| 107 | + // generate the normally distributed number |
| 108 | + const double twopi = 6.28318530718; |
| 109 | + double sample1 = std::sqrt(-2.0*std::log(random_number1)); |
| 110 | + double sample2 = cos(twopi*(random_number2)); |
| 111 | + energy_neutron = (5.59/2.35)*(ion_kt[bin_number])*sample1*sample2; |
| 112 | + energy_neutron += 14.08; |
| 113 | + // test energy limit |
| 114 | + // if (energy_neutron < 15.5){energy_neutron = 15.5} else {} |
| 115 | + return; |
| 116 | +} |
| 117 | + |
| 118 | +/* |
| 119 | + * convert the sampled radius to an rz coordinate by using plasma parameters |
| 120 | + */ |
| 121 | +void PlasmaSource::convert_rad_to_rz( const double minor_sampled, |
| 122 | + const double rn_store, |
| 123 | + double &radius, double &height) |
| 124 | +{ |
| 125 | + const double twopi = 6.28318530718; |
| 126 | + |
| 127 | + double alpha = twopi*(rn_store); |
| 128 | + |
| 129 | + double shift = shafranov*(1.0-std::pow(minor_sampled/(minorRadius),2)); |
| 130 | + |
| 131 | + radius = majorRadius + minor_sampled*cos(alpha+(triangularity*sin(alpha))) + shift; |
| 132 | + height = elongation*minor_sampled*sin(alpha); |
| 133 | + |
| 134 | + |
| 135 | + return; |
| 136 | +} |
| 137 | + |
| 138 | + |
| 139 | +/* |
| 140 | + * convert rz_to_xyz |
| 141 | + */ |
| 142 | +void PlasmaSource::convert_r_to_xy(const double r, const double rn_store, |
| 143 | + double &x, double &y) |
| 144 | + |
| 145 | +{ |
| 146 | + double toroidal_extent = maxToroidalAngle - minToroidalAngle; |
| 147 | + double toroidal_angle = toroidal_extent*rn_store + minToroidalAngle; |
| 148 | + x = r*sin(toroidal_angle); |
| 149 | + y = r*cos(toroidal_angle); |
| 150 | + return; |
| 151 | +} |
| 152 | + |
| 153 | +/* |
| 154 | + * sets up the cumulatitive probability profile |
| 155 | + * on the basis of the ion temp and ion density |
| 156 | + * this portion is deterministic |
| 157 | + */ |
| 158 | +void PlasmaSource::setup_plasma_source() |
| 159 | +{ |
| 160 | + double ion_d; // ion density |
| 161 | + double ion_t; // ion temp |
| 162 | + double sig_dt; // dt xs |
| 163 | + |
| 164 | + std::vector<double> src_strength; // the source strength, n/m3 |
| 165 | + double r; |
| 166 | + |
| 167 | + binWidth = minorRadius/float(numberOfBins); |
| 168 | + double total = 0.0; // total source strength |
| 169 | + |
| 170 | + for (int i = 0 ; i < numberOfBins ; i++) { |
| 171 | + r = binWidth * float(i); |
| 172 | + ion_d = ion_density(r); |
| 173 | + ion_t = ion_temperature(r); |
| 174 | + src_strength.push_back(std::pow(ion_d,2)*dt_xs(ion_t)); |
| 175 | + ion_kt.push_back(sqrt(ion_t/1000.0)); // convert to sqrt(MeV) |
| 176 | + total += src_strength[i]; |
| 177 | + } |
| 178 | + |
| 179 | + // normalise the source profile |
| 180 | + double sum = 0 ; |
| 181 | + for ( int i = 0 ; i < numberOfBins ; i++) { |
| 182 | + sum += src_strength[i]; |
| 183 | + source_profile.push_back(sum/total); |
| 184 | + } |
| 185 | + return; |
| 186 | +} |
| 187 | + |
| 188 | +/* |
| 189 | + * function that returns the ion density given the |
| 190 | + * given the critical plasma parameters |
| 191 | + */ |
| 192 | +double PlasmaSource::ion_density(const double sample_radius) |
| 193 | +{ |
| 194 | + double ion_dens = 0.0; |
| 195 | + |
| 196 | + if( plasmaId == 0 ) { |
| 197 | + ion_dens = ionDensityOrigin* |
| 198 | + (1.0-std::pow(sample_radius/minorRadius,2)); |
| 199 | + } else { |
| 200 | + if(sample_radius <= pedistalRadius) { |
| 201 | + ion_dens += ionDensityPedistal; |
| 202 | + double product; |
| 203 | + product = 1.0-std::pow(sample_radius/pedistalRadius,2); |
| 204 | + product = std::pow(product,ionDensityPeaking); |
| 205 | + ion_dens += (ionDensityOrigin-ionDensityPedistal)* |
| 206 | + (product); |
| 207 | + } else { |
| 208 | + ion_dens += ionDensitySeperatrix; |
| 209 | + double product; |
| 210 | + product = ionDensityPedistal - ionDensitySeperatrix; |
| 211 | + ion_dens += product*(minorRadius-sample_radius)/(minorRadius-pedistalRadius); |
| 212 | + } |
| 213 | + } |
| 214 | + |
| 215 | + return ion_dens; |
| 216 | +} |
| 217 | + |
| 218 | +/* |
| 219 | + * function that returns the ion density given the |
| 220 | + * given the critical plasma parameters |
| 221 | + */ |
| 222 | +double PlasmaSource::ion_temperature(const double sample_radius) |
| 223 | +{ |
| 224 | + double ion_temp = 0.0; |
| 225 | + |
| 226 | + if( plasmaId == 0 ) { |
| 227 | + ion_temp = ionTemperatureOrigin* |
| 228 | + (1.0-std::pow(sample_radius/minorRadius, |
| 229 | + ionTemperaturePeaking)); |
| 230 | + } else { |
| 231 | + if(sample_radius <= pedistalRadius) { |
| 232 | + ion_temp += ionTemperaturePedistal; |
| 233 | + double product; |
| 234 | + product = 1.0-std::pow(sample_radius/pedistalRadius,2); |
| 235 | + product = std::pow(product,ionTemperaturePeaking); |
| 236 | + ion_temp += (ionTemperatureOrigin- |
| 237 | + ionTemperaturePedistal)*(product); |
| 238 | + } else { |
| 239 | + ion_temp += ionTemperatureSeperatrix; |
| 240 | + double product; |
| 241 | + product = ionTemperaturePedistal - ionTemperatureSeperatrix; |
| 242 | + ion_temp += product*(minorRadius-sample_radius)/(minorRadius-pedistalRadius); |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + return ion_temp; |
| 247 | +} |
| 248 | + |
| 249 | +/* |
| 250 | + * returns the dt cross section for a given ion temp |
| 251 | + */ |
| 252 | +double PlasmaSource::dt_xs(double ion_temp) |
| 253 | +{ |
| 254 | + double dt; |
| 255 | + double c[7]={2.5663271e-18,19.983026,2.5077133e-2, |
| 256 | + 2.5773408e-3,6.1880463e-5,6.6024089e-2, |
| 257 | + 8.1215505e-3}; |
| 258 | + |
| 259 | + double u = 1.0-ion_temp*(c[2]+ion_temp*(c[3]-c[4]*ion_temp)) |
| 260 | + /(1.0+ion_temp*(c[5]+c[6]*ion_temp)); |
| 261 | + |
| 262 | + dt = c[0]/(std::pow(u,5./6.)*std::pow(ion_temp,2./3.0)); |
| 263 | + dt *= exp(-1.*c[1]*std::pow(u/ion_temp,1./3.)); |
| 264 | + |
| 265 | + return dt; |
| 266 | +} |
| 267 | + |
| 268 | +/* |
| 269 | + * returns the dt cross section for a given ion temp |
| 270 | + */ |
| 271 | +void PlasmaSource::isotropic_direction(const double random1, |
| 272 | + const double random2, |
| 273 | + double &u, double &v, |
| 274 | + double &w) { |
| 275 | + double t = 2*M_PI*random1; |
| 276 | + double p = acos(1. - 2.*random2); |
| 277 | + |
| 278 | + u = sin(p)*cos(t); |
| 279 | + v = sin(p)*sin(t); |
| 280 | + w = cos(p); |
| 281 | + |
| 282 | + return; |
| 283 | +} |
| 284 | + |
| 285 | +} // end of namespace |
0 commit comments