ASCOT5
Loading...
Searching...
No Matches
bbnbi5.c
Go to the documentation of this file.
1
11#include <getopt.h>
12#include <math.h>
13#ifdef MPI
14 #include <mpi.h>
15#endif
16#include <omp.h>
17#include <stdlib.h>
18#include <string.h>
19#include <time.h>
20#include "ascot5.h"
21#include "consts.h"
22#include "gitver.h"
23#include "math.h"
24#include "physlib.h"
25#include "print.h"
26#include "simulate.h"
27#include "endcond.h"
28#include "random.h"
29#include "particle.h"
30#include "suzuki.h"
31#include "B_field.h"
32#include "plasma.h"
33#include "neutral.h"
34#include "wall.h"
35#include "asigma.h"
36#include "nbi.h"
37#include "diag.h"
38#include "bbnbi5.h"
39
41void bbnbi_inject_markers(particle_state* p, int nprt, int ngenerated, real t0,
42 real t1, nbi_injector* inj, sim_data* sim);
43
57 sim_data* sim, int nprt, real t1, real t2, particle_state** p) {
58
59 /* Initialize input data */
60 simulate_init(sim);
61 random_init(&sim->random_data, time(NULL));
62
63 /* Calculate total NBI power so that we can distribute markers along
64 * the injectors according to their power */
65 real total_power = 0;
66 for(int i=0; i < sim->nbi_data.ninj; i++) {
67 total_power += sim->nbi_data.inj[i].power;
68 }
69
70 /* Initialize particle struct */
71 *p = (particle_state*) malloc(nprt * sizeof(particle_state));
72
73 /* Generate markers at the injectors */
74 int nprt_generated = 0;
75 for(int i = 0; i < sim->nbi_data.ninj; i++) {
76
77 /* Number of markers generated is proportional to NBI power */
78 int nprt_inj = ( sim->nbi_data.inj[i].power / total_power ) * nprt;
79 if(i == sim->nbi_data.ninj-1) {
80 /* All "remaining" markers goes to the last injector to avoid any
81 * rounding issues */
82 nprt_inj = nprt - nprt_generated;
83 }
84
85 /* Generates markers at the injector location and traces them until
86 * they enter the region with magnetic field data */
87 bbnbi_inject_markers(&((*p)[nprt_generated]), nprt_inj, nprt_generated,
88 t1, t2, &(sim->nbi_data.inj[i]), sim);
89
90 nprt_generated += nprt_inj;
92 "Generated %d markers for injector %d.\n", nprt_inj, i+1);
93 }
94
95 /* Place markers in a queue */
97 pq.n = 0;
98 for(int i = 0; i < nprt; i++) {
99 pq.n++;
100 }
101 pq.p = (particle_state**) malloc(pq.n * sizeof(particle_state*));
102 pq.finished = 0;
103
104 pq.next = 0;
105 for(int i = 0; i < nprt; i++) {
106 pq.p[pq.next++] = &((*p)[i]);
107
108 }
109 pq.next = 0;
110
111 /* Trace neutrals until they are ionized or lost to the wall */
112 #pragma omp parallel
113 bbnbi_trace_markers(&pq, sim);
114}
115
133void bbnbi_inject_markers(particle_state* p, int nprt, int ngenerated, real t0,
134 real t1, nbi_injector* inj, sim_data* sim) {
135
136 /* Set marker weights assuming a large number is created so that the energy
137 * fractions of generated markers are close to the injector values */
138 real f = 1.0 * inj->efrac[0] + (1.0/2) * inj->efrac[1]
139 + (1.0/3) * inj->efrac[2];
140 real weight = (inj->power / inj->energy ) / ( f * nprt );
141
142 /* Inject markers and trace their ballistic trajectories (without any
143 * other physics) until they enter the plasma for the first time. */
144 #pragma omp parallel for
145 for(int i = 0; i < nprt; i++) {
146 real time = t0 + random_uniform(&sim->random_data) * (t1-t0);
147
148 /* Assign initial phase-space coordinates for this marker */
149 real xyz[3], vxyz[3], rpz[3], vhat[3];
150 nbi_inject(xyz, vxyz, inj, &sim->random_data);
151 math_xyz2rpz(xyz, rpz);
152 math_unit(vxyz, vhat);
153
154 /* Advance until the marker enters the magnetic field */
155 real psi;
156 real ds = 1e-3;
157 a5err err = B_field_eval_psi(&psi, rpz[0], rpz[1], rpz[2], time,
158 &sim->B_data);
159 while(err) {
160 xyz[0] += ds * vhat[0];
161 xyz[1] += ds * vhat[1];
162 xyz[2] += ds * vhat[2];
163 math_xyz2rpz(xyz, rpz);
164 err = B_field_eval_psi(&psi, rpz[0], rpz[1], rpz[2], time,
165 &sim->B_data);
166 }
167
168 real vrpz[3];
169 math_vec_xyz2rpz(vxyz, vrpz, rpz[1]);
170 real gamma = physlib_gamma_vnorm(math_norm(vrpz));
171
172 /* Fill the particle state with particle coordinates */
173 p[i].rprt = rpz[0];
174 p[i].phiprt = rpz[1];
175 p[i].zprt = rpz[2];
176 p[i].p_r = vrpz[0] * gamma * inj->mass;
177 p[i].p_phi = vrpz[1] * gamma * inj->mass;
178 p[i].p_z = vrpz[2] * gamma * inj->mass;
179 p[i].mass = inj->mass;
180 p[i].charge = 0.0;
181 p[i].anum = inj->anum;
182 p[i].znum = inj->znum;
183 p[i].weight = weight;
184 p[i].time = time;
185 p[i].mileage = 0.0;
186 p[i].cputime = 0.0;
187 p[i].id = ngenerated + i + 1;
188 p[i].endcond = 0;
189 p[i].walltile = 0;
190 p[i].err = 0;
191 }
192}
193
204 int cycle[NSIMD] __memalign__;
206 int shinethrough[NSIMD] __memalign__;
207 real remaining[NSIMD] __memalign__;
208 real threshold[NSIMD] __memalign__;
209 particle_simd_fo p, p0, pdiag;
210
211 int n_species = plasma_get_n_species(&sim->plasma_data);
212 const int* pls_anum = plasma_get_species_anum(&sim->plasma_data);
213 const int* pls_znum = plasma_get_species_znum(&sim->plasma_data);
214
215 /* Init dummy markers */
219 for(int i=0; i< NSIMD; i++) {
220 p.id[i] = -1;
221 p.running[i] = 0;
222 hin[i] = 1e-10;
223 threshold[i] = random_uniform(&sim->random_data);
224 remaining[i] = 1.0;
225 shinethrough[i] = 0;
226 }
227
228 /* Initialize running particles */
229 int n_running = particle_cycle_fo(pq, &p, &sim->B_data, cycle);
230 while(n_running > 0) {
231
232 #pragma omp simd
233 for(int i=0; i< NSIMD; i++) {
234 /* Store marker states */
235 particle_copy_fo(&p, i, &p0, i);
236
237 if(p.running[i]) {
238 a5err err = 0;
239
240 /* These are needed later */
241 real pnorm = math_normc(p.p_r[i], p.p_phi[i], p.p_z[i]);
242 real gamma = physlib_gamma_pnorm(p.mass[i], pnorm);
243 real ekin = physlib_Ekin_pnorm(p.mass[i], pnorm);
244
245 /* Advance ballistic trajectory by converting momentum to
246 * cartesian coordinates */
247 real prpz[3] = {p.p_r[i], p.p_phi[i], p.p_z[i]};
248 real pxyz[3];
249 math_vec_rpz2xyz(prpz, pxyz, p.phi[i]);
250
251 real posrpz[3] = {p.r[i], p.phi[i], p.z[i]};
252 real posxyz[3], fposxyz[3];
253 math_rpz2xyz(posrpz, posxyz);
254 fposxyz[0] = posxyz[0] + pxyz[0] * hin[i] / (gamma * p.mass[i]);
255 fposxyz[1] = posxyz[1] + pxyz[1] * hin[i] / (gamma * p.mass[i]);
256 fposxyz[2] = posxyz[2] + pxyz[2] * hin[i] / (gamma * p.mass[i]);
257
258 /* Back to cylindrical coordinates (note phi is cumulative) */
259 p.r[i] = sqrt(fposxyz[0]*fposxyz[0] + fposxyz[1]*fposxyz[1]);
260 p.phi[i] += atan2(
261 posxyz[0] * fposxyz[1] - posxyz[1] * fposxyz[0],
262 posxyz[0] * fposxyz[0] + posxyz[1] * fposxyz[1] );
263 p.z[i] = fposxyz[2];
264
265 real cosp = cos(p.phi[i]);
266 real sinp = sin(p.phi[i]);
267 p.p_r[i] = pxyz[0] * cosp + pxyz[1] * sinp;
268 p.p_phi[i] = -pxyz[0] * sinp + pxyz[1] * cosp;
269 p.p_z[i] = pxyz[2];
270
271 real ds = hin[i];
272 p.mileage[i] += hin[i];
273
274 /* Update background values at the new position */
275 real psi, rho[2], pls_dens[MAX_SPECIES], pls_temp[MAX_SPECIES];
276 err = B_field_eval_psi(
277 &psi, p.r[i], p.phi[i], p.z[i], p.time[i], &sim->B_data);
278 if(!err) {
279 err = B_field_eval_rho(rho, psi, &sim->B_data);
280 }
281 if(!err && p.rho[i] <= 1.0 && rho[0] > 1.0) {
282 shinethrough[i] = 1;
283 }
284 p.rho[i] = rho[0];
285 /* Update theta value */
286 real axisrz[2];
287 if(!err) {
288 B_field_get_axis_rz(axisrz, &sim->B_data, p.phi[i]);
289 }
290 p.theta[i] = atan2(p.z[i]-axisrz[1], p.r[i]-axisrz[0]);
291
292 if(!err) {
294 pls_dens, pls_temp, rho[0], p.r[i], p.phi[i], p.z[i],
295 p.time[i], &sim->plasma_data);
296 }
297
298 /* Calculate ionization rate */
299 real rate = 0.0;
300 if(!err) {
301 real sigmav;
302 if( asigma_eval_bms(
303 &sigmav, p.znum[i], p.anum[i], ekin, p.mass[i],
304 n_species-1, pls_znum, pls_anum, pls_temp[0],
305 &(pls_dens[1]), &sim->asigma_data) ) {
306 err = 1;
307 }
308 rate = pls_dens[0] * sigmav;
309 }
310 remaining[i] *= exp(-rate * ds);
311
312 /* Check for end conditions */
313 if(!err) {
314 real w_coll = 0;
315 int tile = 0;
316 if(shinethrough[i]) {
317 tile = wall_hit_wall(
318 p0.r[i], p0.phi[i], p0.z[i],
319 p.r[i], p.phi[i], p.z[i], &sim->wall_data, &w_coll);
320 }
321 if(tile > 0) {
322 real w = w_coll;
323 p.time[i] = p0.time[i] + w*(p.time[i] - p0.time[i]);
324 p.r[i] = p0.r[i] + w*(p.r[i] - p0.r[i]);
325 p.phi[i] = p0.phi[i] + w*(p.phi[i] - p0.phi[i]);
326 p.z[i] = p0.z[i] + w*(p.z[i] - p0.z[i]);
327
328 p.walltile[i] = tile;
329 p.endcond[i] |= endcond_wall;
330 p.running[i] = 0;
331 }
332 if(p.mileage[i] > NBI_MAX_DISTANCE) {
333 p.endcond[i] |= endcond_tlim;
334 p.running[i] = 0;
335 }
336 if(remaining[i] < threshold[i]) {
337 p.charge[i] = 1*CONST_E;
338 p.endcond[i] |= endcond_ioniz;
339 p.running[i] = 0;
340 }
341 } else {
342 p.err[i] = err;
343 p.running[i] = 0;
344 }
345 }
346 }
347
348 /* Update markers that just finished */
349 #pragma omp simd
350 for(int i=0; i< NSIMD; i++) {
351 /* Use this as a flag for which markers to update in diagnostics */
352 pdiag.running[i] = 0;
353 if(!p.running[i] && p.id[i] >= 0) {
354 p.time[i] += p.mileage[i];
355
356 /* Reset these for the next marker */
357 threshold[i] = random_uniform(&sim->random_data);
358 remaining[i] = 1.0;
359 shinethrough[i] = 0;
360
361 /* Update the magnetic field at the marker position */
362 if(!p.err[i]) {
363 real B_dB[15];
364 B_field_eval_B_dB(B_dB, p.r[i], p.phi[i], p.z[i], p.time[i],
365 &sim->B_data);
366 p.B_r[i] = B_dB[0];
367 p.B_r_dr[i] = B_dB[1];
368 p.B_r_dphi[i] = B_dB[2];
369 p.B_r_dz[i] = B_dB[3];
370
371 p.B_phi[i] = B_dB[4];
372 p.B_phi_dr[i] = B_dB[5];
373 p.B_phi_dphi[i] = B_dB[6];
374 p.B_phi_dz[i] = B_dB[7];
375
376 p.B_z[i] = B_dB[8];
377 p.B_z_dr[i] = B_dB[9];
378 p.B_z_dphi[i] = B_dB[10];
379 p.B_z_dz[i] = B_dB[11];
380 }
381 }
382 particle_copy_fo(&p, i, &pdiag, i);
383 if(!p.running[i] && p.id[i] >= 0) {
384 pdiag.running[i] = 1;
385 }
386
387 /* Normalize weight with time and add hin so that we don't divide
388 * with zero when updating distributions */
389 pdiag.time[i] += hin[i];
390 pdiag.weight[i] /= hin[i];
391 }
392
393 /* Update distributions for markers that finished */
394 diag_update_fo(&sim->diag_data, &sim->B_data, &pdiag, &p);
395
396 /* Update running particles */
397 n_running = particle_cycle_fo(pq, &p, &sim->B_data, cycle);
398 }
399}
a5err B_field_eval_rho(real rho[2], real psi, B_field_data *Bdata)
Evaluate normalized poloidal flux rho and its psi derivative.
Definition B_field.c:228
a5err B_field_eval_psi(real *psi, real r, real phi, real z, real t, B_field_data *Bdata)
Evaluate poloidal flux psi.
Definition B_field.c:102
a5err B_field_eval_B_dB(real B_dB[15], real r, real phi, real z, real t, B_field_data *Bdata)
Evaluate magnetic field and its derivatives.
Definition B_field.c:449
a5err B_field_get_axis_rz(real rz[2], B_field_data *Bdata, real phi)
Return magnetic axis Rz-coordinates.
Definition B_field.c:501
Header file for B_field.c.
Main header file for ASCOT5.
#define NBI_MAX_DISTANCE
Maximum distance BBNBI traces markers in seconds.
Definition ascot5.h:104
double real
Definition ascot5.h:85
#define NSIMD
Number of particles simulated simultaneously in a particle group operations.
Definition ascot5.h:91
#define MAX_SPECIES
Maximum number of plasma species.
Definition ascot5.h:95
#define __memalign__
Definition ascot5.h:76
a5err asigma_eval_bms(real *ratecoeff, int z_1, int a_1, real E, real mass, int nion, const int *znum, const int *anum, real T_e, real *n_i, asigma_data *asigma_data)
Evaluate beam stopping rate coefficient.
Definition asigma.c:237
Header file for asigma.c.
void bbnbi_inject_markers(particle_state *p, int nprt, int ngenerated, real t0, real t1, nbi_injector *inj, sim_data *sim)
Inject neutrals from an injector.
Definition bbnbi5.c:133
void bbnbi_simulate(sim_data *sim, int nprt, real t1, real t2, particle_state **p)
Simulate NBI injection.
Definition bbnbi5.c:56
void bbnbi_trace_markers(particle_queue *pq, sim_data *sim)
Trace a neutral marker until it has ionized or hit wall.
Definition bbnbi5.c:203
Functions to execute bbnbi externally.
Header file containing physical and mathematical constants.
#define CONST_E
Elementary charge [C]
Definition consts.h:32
void diag_update_fo(diag_data *data, B_field_data *Bdata, particle_simd_fo *p_f, particle_simd_fo *p_i)
Collects diagnostics when marker represents a particle.
Definition diag.c:144
Header file for diag.c.
Header file for endcond.c.
@ endcond_tlim
Definition endcond.h:19
@ endcond_wall
Definition endcond.h:22
@ endcond_ioniz
Definition endcond.h:30
unsigned long int a5err
Simulation error flag.
Definition error.h:17
Header file for math.c.
#define math_vec_xyz2rpz(vxyz, vrpz, phi)
Transform vector from cartesian to cylindrical basis: vxyz -> vrpz, phi is the toroidal angle in radi...
Definition math.h:90
#define math_unit(a, b)
Calculate unit vector b from a 3D vector a.
Definition math.h:70
#define math_xyz2rpz(xyz, rpz)
Convert cartesian coordinates xyz to cylindrical coordinates rpz.
Definition math.h:74
#define math_vec_rpz2xyz(vrpz, vxyz, phi)
Transform vector from cylindrical to cartesian basis: vrpz -> vxyz, phi is the toroidal angle in radi...
Definition math.h:83
#define math_rpz2xyz(rpz, xyz)
Convert cylindrical coordinates rpz to cartesian coordinates xyz.
Definition math.h:78
#define math_normc(a1, a2, a3)
Calculate norm of 3D vector from its components a1, a2, a3.
Definition math.h:67
#define math_norm(a)
Calculate norm of 3D vector a.
Definition math.h:64
void nbi_inject(real *xyz, real *vxyz, nbi_injector *inj, random_data *rng)
Sample injected marker's coordinates.
Definition nbi.c:118
Header file for nbi.c.
Header file for neutral.c.
void particle_allocate_fo(particle_simd_fo *p_fo, int nmrk)
Allocates struct representing particle markers.
Definition particle.c:69
int particle_cycle_fo(particle_queue *q, particle_simd_fo *p, B_field_data *Bdata, int *cycle)
Replace finished FO markers with new ones or dummies.
Definition particle.c:268
void particle_copy_fo(particle_simd_fo *p1, int i, particle_simd_fo *p2, int j)
Copy FO struct.
Definition particle.c:1302
Header file for particle.c.
Methods to evaluate elementary physical quantities.
#define physlib_gamma_pnorm(m, p)
Evaluate Lorentz factor from momentum norm.
Definition physlib.h:46
#define physlib_Ekin_pnorm(m, p)
Evaluate kinetic energy [J] from momentum norm.
Definition physlib.h:115
#define physlib_gamma_vnorm(v)
Evaluate Lorentz factor from velocity norm.
Definition physlib.h:21
const int * plasma_get_species_znum(plasma_data *pls_data)
Get charge number of ion species.
Definition plasma.c:325
int plasma_get_n_species(plasma_data *pls_data)
Get the number of plasma species.
Definition plasma.c:237
a5err plasma_eval_densandtemp(real *dens, real *temp, real rho, real r, real phi, real z, real t, plasma_data *pls_data)
Evaluate plasma density and temperature for all species.
Definition plasma.c:186
const int * plasma_get_species_anum(plasma_data *pls_data)
Get atomic mass number of ion species.
Definition plasma.c:353
Header file for plasma.c.
Macros for printing console output.
@ VERBOSE_NORMAL
Definition print.h:18
#define print_out0(v, rank, root,...)
Print to standard output only for root process.
Definition print.h:36
Header file for random.c.
#define random_uniform(data)
Definition random.h:96
#define random_init(data, seed)
Definition random.h:94
void simulate_init(sim_data *sim)
Initialize simulation data struct.
Definition simulate.c:306
Header file for simulate.c.
int ninj
Definition nbi.h:41
nbi_injector * inj
Definition nbi.h:42
Structure for describing an NBI injector.
Definition nbi.h:15
real mass
Definition nbi.h:34
real efrac[3]
Definition nbi.h:26
int anum
Definition nbi.h:32
real energy
Definition nbi.h:25
real power
Definition nbi.h:24
int znum
Definition nbi.h:33
Marker queue.
Definition particle.h:154
particle_state ** p
Definition particle.h:156
volatile int next
Definition particle.h:158
volatile int finished
Definition particle.h:159
Struct representing NSIMD particle markers.
Definition particle.h:210
real * B_phi_dphi
Definition particle.h:233
integer * id
Definition particle.h:246
integer * endcond
Definition particle.h:247
integer * walltile
Definition particle.h:248
integer * running
Definition particle.h:252
General representation of a marker.
Definition particle.h:40
integer id
Definition particle.h:63
integer walltile
Definition particle.h:65
integer endcond
Definition particle.h:64
Simulation data struct.
Definition simulate.h:57
plasma_data plasma_data
Definition simulate.h:61
int mpi_rank
Definition simulate.h:132
random_data random_data
Definition simulate.h:71
B_field_data B_data
Definition simulate.h:59
wall_data wall_data
Definition simulate.h:63
int mpi_root
Definition simulate.h:131
asigma_data asigma_data
Definition simulate.h:66
diag_data diag_data
Definition simulate.h:68
nbi_data nbi_data
Definition simulate.h:67
Header file for suzuki.c.
int wall_hit_wall(real r1, real phi1, real z1, real r2, real phi2, real z2, wall_data *w, real *w_coll)
Check if a given directed line segment intersects the wall.
Definition wall.c:80
Header file for wall.c.