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
65 sim_offload_data* sim, int nprt, real t1, real t2, real* B_offload_array,
66 real* plasma_offload_array, real* neutral_offload_array,
67 real* wall_offload_array, int* wall_int_offload_array,
68 real* asigma_offload_array, real* nbi_offload_array, particle_state** p,
69 real* diag_offload_array) {
70
71 /* Initialize input data */
73 sim_init(&sim_data, sim);
74 random_init(&sim_data.random_data, time(NULL));
75 B_field_init(&sim_data.B_data, &sim->B_offload_data, B_offload_array);
77 plasma_offload_array);
79 neutral_offload_array);
80 wall_init(&sim_data.wall_data, &sim->wall_offload_data, wall_offload_array,
81 wall_int_offload_array);
83 asigma_offload_array);
84 nbi_init(&sim_data.nbi_data, &sim->nbi_offload_data, nbi_offload_array);
85 diag_init(&sim_data.diag_data, &sim->diag_offload_data, diag_offload_array);
86
87 /* Calculate total NBI power so that we can distribute markers along
88 * the injectors according to their power */
89 real total_power = 0;
90 for(int i=0; i < sim_data.nbi_data.ninj; i++) {
91 total_power += sim_data.nbi_data.inj[i].power;
92 }
93
94 /* Initialize particle struct */
95 *p = (particle_state*) malloc(nprt * sizeof(particle_state));
96
97 /* Generate markers at the injectors */
98 int nprt_generated = 0;
99 for(int i = 0; i < sim_data.nbi_data.ninj; i++) {
100
101 /* Number of markers generated is proportional to NBI power */
102 int nprt_inj = ( sim_data.nbi_data.inj[i].power / total_power ) * nprt;
103 if(i == sim_data.nbi_data.ninj-1) {
104 /* All "remaining" markers goes to the last injector to avoid any
105 * rounding issues */
106 nprt_inj = nprt - nprt_generated;
107 }
108
109 /* Generates markers at the injector location and traces them until
110 * they enter the region with magnetic field data */
111 bbnbi_inject_markers(&((*p)[nprt_generated]), nprt_inj, nprt_generated,
112 t1, t2, &(sim_data.nbi_data.inj[i]), &sim_data);
113
114 nprt_generated += nprt_inj;
116 "Generated %d markers for injector %d.\n", nprt_inj, i+1);
117 }
118
119 /* Place markers in a queue */
121 pq.n = 0;
122 for(int i = 0; i < nprt; i++) {
123 pq.n++;
124 }
125 pq.p = (particle_state**) malloc(pq.n * sizeof(particle_state*));
126 pq.finished = 0;
127
128 pq.next = 0;
129 for(int i = 0; i < nprt; i++) {
130 pq.p[pq.next++] = &((*p)[i]);
131
132 }
133 pq.next = 0;
134
135 /* Trace neutrals until they are ionized or lost to the wall */
136 #pragma omp parallel
138}
139
157void bbnbi_inject_markers(particle_state* p, int nprt, int ngenerated, real t0,
158 real t1, nbi_injector* inj, sim_data* sim) {
159
160 /* Set marker weights assuming a large number is created so that the energy
161 * fractions of generated markers are close to the injector values */
162 real f = 1.0 * inj->efrac[0] + (1.0/2) * inj->efrac[1]
163 + (1.0/3) * inj->efrac[2];
164 real weight = (inj->power / inj->energy ) / ( f * nprt );
165
166 /* Inject markers and trace their ballistic trajectories (without any
167 * other physics) until they enter the plasma for the first time. */
168 #pragma omp parallel for
169 for(int i = 0; i < nprt; i++) {
170 real time = t0 + random_uniform(&sim->random_data) * (t1-t0);
171
172 /* Assign initial phase-space coordinates for this marker */
173 real xyz[3], vxyz[3], rpz[3], vhat[3];
174 nbi_inject(xyz, vxyz, inj, &sim->random_data);
175 math_xyz2rpz(xyz, rpz);
176 math_unit(vxyz, vhat);
177
178 /* Advance until the marker enters the magnetic field */
179 real psi;
180 real ds = 1e-3;
181 a5err err = B_field_eval_psi(&psi, rpz[0], rpz[1], rpz[2], time,
182 &sim->B_data);
183 while(err) {
184 xyz[0] += ds * vhat[0];
185 xyz[1] += ds * vhat[1];
186 xyz[2] += ds * vhat[2];
187 math_xyz2rpz(xyz, rpz);
188 err = B_field_eval_psi(&psi, rpz[0], rpz[1], rpz[2], time,
189 &sim->B_data);
190 }
191
192 real vrpz[3];
193 math_vec_xyz2rpz(vxyz, vrpz, rpz[1]);
194 real gamma = physlib_gamma_vnorm(math_norm(vrpz));
195
196 /* Fill the particle state with particle coordinates */
197 p[i].rprt = rpz[0];
198 p[i].phiprt = rpz[1];
199 p[i].zprt = rpz[2];
200 p[i].p_r = vrpz[0] * gamma * inj->mass;
201 p[i].p_phi = vrpz[1] * gamma * inj->mass;
202 p[i].p_z = vrpz[2] * gamma * inj->mass;
203 p[i].mass = inj->mass;
204 p[i].charge = 0.0;
205 p[i].anum = inj->anum;
206 p[i].znum = inj->znum;
207 p[i].weight = weight;
208 p[i].time = time;
209 p[i].mileage = 0.0;
210 p[i].cputime = 0.0;
211 p[i].id = ngenerated + i + 1;
212 p[i].endcond = 0;
213 p[i].walltile = 0;
214 p[i].err = 0;
215 }
216}
217
228 int cycle[NSIMD] __memalign__;
230 int shinethrough[NSIMD] __memalign__;
231 real remaining[NSIMD] __memalign__;
232 real threshold[NSIMD] __memalign__;
233 particle_simd_fo p, p0, pdiag;
234
235 int n_species = plasma_get_n_species(&sim->plasma_data);
236 const int* pls_anum = plasma_get_species_anum(&sim->plasma_data);
237 const int* pls_znum = plasma_get_species_znum(&sim->plasma_data);
238
239 /* Init dummy markers */
243 for(int i=0; i< NSIMD; i++) {
244 p.id[i] = -1;
245 p.running[i] = 0;
246 hin[i] = 1e-10;
247 threshold[i] = random_uniform(&sim->random_data);
248 remaining[i] = 1.0;
249 shinethrough[i] = 0;
250 }
251
252 /* Initialize running particles */
253 int n_running = particle_cycle_fo(pq, &p, &sim->B_data, cycle);
254 while(n_running > 0) {
255
256 #pragma omp simd
257 for(int i=0; i< NSIMD; i++) {
258 /* Store marker states */
259 particle_copy_fo(&p, i, &p0, i);
260
261 if(p.running[i]) {
262 a5err err = 0;
263
264 /* These are needed later */
265 real pnorm = math_normc(p.p_r[i], p.p_phi[i], p.p_z[i]);
266 real gamma = physlib_gamma_pnorm(p.mass[i], pnorm);
267 real ekin = physlib_Ekin_pnorm(p.mass[i], pnorm);
268
269 /* Advance ballistic trajectory by converting momentum to
270 * cartesian coordinates */
271 real prpz[3] = {p.p_r[i], p.p_phi[i], p.p_z[i]};
272 real pxyz[3];
273 math_vec_rpz2xyz(prpz, pxyz, p.phi[i]);
274
275 real posrpz[3] = {p.r[i], p.phi[i], p.z[i]};
276 real posxyz[3], fposxyz[3];
277 math_rpz2xyz(posrpz, posxyz);
278 fposxyz[0] = posxyz[0] + pxyz[0] * hin[i] / (gamma * p.mass[i]);
279 fposxyz[1] = posxyz[1] + pxyz[1] * hin[i] / (gamma * p.mass[i]);
280 fposxyz[2] = posxyz[2] + pxyz[2] * hin[i] / (gamma * p.mass[i]);
281
282 /* Back to cylindrical coordinates (note phi is cumulative) */
283 p.r[i] = sqrt(fposxyz[0]*fposxyz[0] + fposxyz[1]*fposxyz[1]);
284 p.phi[i] += atan2(
285 posxyz[0] * fposxyz[1] - posxyz[1] * fposxyz[0],
286 posxyz[0] * fposxyz[0] + posxyz[1] * fposxyz[1] );
287 p.z[i] = fposxyz[2];
288
289 real cosp = cos(p.phi[i]);
290 real sinp = sin(p.phi[i]);
291 p.p_r[i] = pxyz[0] * cosp + pxyz[1] * sinp;
292 p.p_phi[i] = -pxyz[0] * sinp + pxyz[1] * cosp;
293 p.p_z[i] = pxyz[2];
294
295 real ds = hin[i];
296 p.mileage[i] += hin[i];
297
298 /* Update background values at the new position */
299 real psi, rho[2], pls_dens[MAX_SPECIES], pls_temp[MAX_SPECIES];
300 err = B_field_eval_psi(
301 &psi, p.r[i], p.phi[i], p.z[i], p.time[i], &sim->B_data);
302 if(!err) {
303 err = B_field_eval_rho(rho, psi, &sim->B_data);
304 }
305 if(!err && p.rho[i] <= 1.0 && rho[0] > 1.0) {
306 shinethrough[i] = 1;
307 }
308 p.rho[i] = rho[0];
309 /* Update theta value */
310 real axisrz[2];
311 if(!err) {
312 B_field_get_axis_rz(axisrz, &sim->B_data, p.phi[i]);
313 }
314 p.theta[i] = atan2(p.z[i]-axisrz[1], p.r[i]-axisrz[0]);
315
316 if(!err) {
318 pls_dens, pls_temp, rho[0], p.r[i], p.phi[i], p.z[i],
319 p.time[i], &sim->plasma_data);
320 }
321
322 /* Calculate ionization rate */
323 real rate = 0.0;
324 if(!err) {
325 real sigmav;
326 if( asigma_eval_bms(
327 &sigmav, p.znum[i], p.anum[i], ekin, p.mass[i],
328 n_species-1, pls_znum, pls_anum, pls_temp[0],
329 &(pls_dens[1]), &sim->asigma_data) ) {
330 err = 1;
331 }
332 rate = pls_dens[0] * sigmav;
333 }
334 remaining[i] *= exp(-rate * ds);
335
336 /* Check for end conditions */
337 if(!err) {
338 real w_coll = 0;
339 int tile = 0;
340 if(shinethrough[i]) {
341 tile = wall_hit_wall(
342 p0.r[i], p0.phi[i], p0.z[i],
343 p.r[i], p.phi[i], p.z[i], &sim->wall_data, &w_coll);
344 }
345 if(tile > 0) {
346 real w = w_coll;
347 p.time[i] = p0.time[i] + w*(p.time[i] - p0.time[i]);
348 p.r[i] = p0.r[i] + w*(p.r[i] - p0.r[i]);
349 p.phi[i] = p0.phi[i] + w*(p.phi[i] - p0.phi[i]);
350 p.z[i] = p0.z[i] + w*(p.z[i] - p0.z[i]);
351
352 p.walltile[i] = tile;
353 p.endcond[i] |= endcond_wall;
354 p.running[i] = 0;
355 }
356 if(p.mileage[i] > NBI_MAX_DISTANCE) {
357 p.endcond[i] |= endcond_tlim;
358 p.running[i] = 0;
359 }
360 if(remaining[i] < threshold[i]) {
361 p.charge[i] = 1*CONST_E;
362 p.endcond[i] |= endcond_ioniz;
363 p.running[i] = 0;
364 }
365 } else {
366 p.err[i] = err;
367 p.running[i] = 0;
368 }
369 }
370 }
371
372 /* Update markers that just finished */
373 #pragma omp simd
374 for(int i=0; i< NSIMD; i++) {
375 /* Use this as a flag for which markers to update in diagnostics */
376 pdiag.running[i] = 0;
377 if(!p.running[i] && p.id[i] >= 0) {
378 p.time[i] += p.mileage[i];
379
380 /* Reset these for the next marker */
381 threshold[i] = random_uniform(&sim->random_data);
382 remaining[i] = 1.0;
383 shinethrough[i] = 0;
384
385 /* Update the magnetic field at the marker position */
386 if(!p.err[i]) {
387 real B_dB[15];
388 B_field_eval_B_dB(B_dB, p.r[i], p.phi[i], p.z[i], p.time[i],
389 &sim->B_data);
390 p.B_r[i] = B_dB[0];
391 p.B_r_dr[i] = B_dB[1];
392 p.B_r_dphi[i] = B_dB[2];
393 p.B_r_dz[i] = B_dB[3];
394
395 p.B_phi[i] = B_dB[4];
396 p.B_phi_dr[i] = B_dB[5];
397 p.B_phi_dphi[i] = B_dB[6];
398 p.B_phi_dz[i] = B_dB[7];
399
400 p.B_z[i] = B_dB[8];
401 p.B_z_dr[i] = B_dB[9];
402 p.B_z_dphi[i] = B_dB[10];
403 p.B_z_dz[i] = B_dB[11];
404 }
405 }
406 particle_copy_fo(&p, i, &pdiag, i);
407 if(!p.running[i] && p.id[i] >= 0) {
408 pdiag.running[i] = 1;
409 }
410
411 /* Normalize weight with time and add hin so that we don't divide
412 * with zero when updating distributions */
413 pdiag.time[i] += hin[i];
414 pdiag.weight[i] /= hin[i];
415 }
416
417 /* Update distributions for markers that finished */
418 diag_update_fo(&sim->diag_data, &sim->B_data, &pdiag, &p);
419
420 /* Update running particles */
421 n_running = particle_cycle_fo(pq, &p, &sim->B_data, cycle);
422 }
423}
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:327
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:201
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:547
int B_field_init(B_field_data *Bdata, B_field_offload_data *offload_data, real *offload_array)
Initialize magnetic field data struct on target.
Definition B_field.c:143
a5err B_field_get_axis_rz(real rz[2], B_field_data *Bdata, real phi)
Return magnetic axis Rz-coordinates.
Definition B_field.c:599
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
int asigma_init(asigma_data *asigma_data, asigma_offload_data *offload_data, real *offload_array)
Initializes atomic reaction data struct on target.
Definition asigma.c:126
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:308
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:157
void bbnbi_simulate(sim_offload_data *sim, int nprt, real t1, real t2, real *B_offload_array, real *plasma_offload_array, real *neutral_offload_array, real *wall_offload_array, int *wall_int_offload_array, real *asigma_offload_array, real *nbi_offload_array, particle_state **p, real *diag_offload_array)
Simulate NBI injection.
Definition bbnbi5.c:64
void bbnbi_trace_markers(particle_queue *pq, sim_data *sim)
Trace a neutral marker until it has ionized or hit wall.
Definition bbnbi5.c:227
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:223
void diag_init(diag_data *data, diag_offload_data *offload_data, real *offload_array)
Initializes diagnostics from offload data.
Definition diag.c:152
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_init(nbi_data *nbi, nbi_offload_data *offload_data, real *offload_array)
Initialize NBI data struct on target.
Definition nbi.c:59
void nbi_inject(real *xyz, real *vxyz, nbi_injector *inj, random_data *rng)
Sample injected marker's coordinates.
Definition nbi.c:114
Header file for nbi.c.
int neutral_init(neutral_data *ndata, neutral_offload_data *offload_data, real *offload_array)
Initialize neutral data struct on target.
Definition neutral.c:106
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:419
int plasma_get_n_species(plasma_data *pls_data)
Get the number of plasma species.
Definition plasma.c:331
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:280
int plasma_init(plasma_data *pls_data, plasma_offload_data *offload_data, real *offload_array)
Initialize plasma data struct on target.
Definition plasma.c:135
const int * plasma_get_species_anum(plasma_data *pls_data)
Get atomic mass number of ion species.
Definition plasma.c:447
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 sim_init(sim_data *sim, sim_offload_data *offload_data)
Initialize simulation data struct on target.
Definition simulate.c:365
Header file for simulate.c.
int ninj
Definition nbi.h:62
nbi_injector inj[NBI_MAX_INJ]
Definition nbi.h:63
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:154
plasma_data plasma_data
Definition simulate.h:158
random_data random_data
Definition simulate.h:168
neutral_data neutral_data
Definition simulate.h:159
B_field_data B_data
Definition simulate.h:156
wall_data wall_data
Definition simulate.h:160
asigma_data asigma_data
Definition simulate.h:163
diag_data diag_data
Definition simulate.h:165
nbi_data nbi_data
Definition simulate.h:164
Simulation offload struct.
Definition simulate.h:55
B_field_offload_data B_offload_data
Definition simulate.h:57
plasma_offload_data plasma_offload_data
Definition simulate.h:59
neutral_offload_data neutral_offload_data
Definition simulate.h:60
asigma_offload_data asigma_offload_data
Definition simulate.h:64
wall_offload_data wall_offload_data
Definition simulate.h:61
nbi_offload_data nbi_offload_data
Definition simulate.h:65
diag_offload_data diag_offload_data
Definition simulate.h:66
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:163
int wall_init(wall_data *w, wall_offload_data *offload_data, real *offload_array, int *int_offload_array)
Initialize wall data struct on target.
Definition wall.c:119
Header file for wall.c.