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 if(xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2] > 1e3) {
167 break;
168 }
169 }
170
171 real vrpz[3];
172 math_vec_xyz2rpz(vxyz, vrpz, rpz[1]);
173 real gamma = physlib_gamma_vnorm(math_norm(vrpz));
174
175 /* Fill the particle state with particle coordinates */
176 p[i].rprt = rpz[0];
177 p[i].phiprt = rpz[1];
178 p[i].zprt = rpz[2];
179 p[i].p_r = vrpz[0] * gamma * inj->mass;
180 p[i].p_phi = vrpz[1] * gamma * inj->mass;
181 p[i].p_z = vrpz[2] * gamma * inj->mass;
182 p[i].mass = inj->mass;
183 p[i].charge = 0.0;
184 p[i].anum = inj->anum;
185 p[i].znum = inj->znum;
186 p[i].weight = weight;
187 p[i].time = time;
188 p[i].mileage = 0.0;
189 p[i].cputime = 0.0;
190 p[i].id = ngenerated + i + 1;
191 p[i].endcond = 0;
192 p[i].walltile = 0;
193 p[i].err = 0;
194 }
195}
196
207 int cycle[NSIMD] __memalign__;
209 int shinethrough[NSIMD] __memalign__;
210 real remaining[NSIMD] __memalign__;
211 real threshold[NSIMD] __memalign__;
212 particle_simd_fo p, p0, pdiag;
213
214 int n_species = plasma_get_n_species(&sim->plasma_data);
215 const int* pls_anum = plasma_get_species_anum(&sim->plasma_data);
216 const int* pls_znum = plasma_get_species_znum(&sim->plasma_data);
217
218 /* Init dummy markers */
222 for(int i=0; i< NSIMD; i++) {
223 p.id[i] = -1;
224 p.running[i] = 0;
225 hin[i] = 1e-10;
226 threshold[i] = random_uniform(&sim->random_data);
227 remaining[i] = 1.0;
228 shinethrough[i] = 0;
229 }
230
231 /* Initialize running particles */
232 int n_running = particle_cycle_fo(pq, &p, &sim->B_data, cycle);
233 while(n_running > 0) {
234
235 #pragma omp simd
236 for(int i=0; i< NSIMD; i++) {
237 /* Store marker states */
238 particle_copy_fo(&p, i, &p0, i);
239
240 if(p.running[i]) {
241 a5err err = 0;
242
243 /* These are needed later */
244 real pnorm = math_normc(p.p_r[i], p.p_phi[i], p.p_z[i]);
245 real gamma = physlib_gamma_pnorm(p.mass[i], pnorm);
246 real ekin = physlib_Ekin_pnorm(p.mass[i], pnorm);
247
248 /* Advance ballistic trajectory by converting momentum to
249 * cartesian coordinates */
250 real prpz[3] = {p.p_r[i], p.p_phi[i], p.p_z[i]};
251 real pxyz[3];
252 math_vec_rpz2xyz(prpz, pxyz, p.phi[i]);
253
254 real posrpz[3] = {p.r[i], p.phi[i], p.z[i]};
255 real posxyz[3], fposxyz[3];
256 math_rpz2xyz(posrpz, posxyz);
257 fposxyz[0] = posxyz[0] + pxyz[0] * hin[i] / (gamma * p.mass[i]);
258 fposxyz[1] = posxyz[1] + pxyz[1] * hin[i] / (gamma * p.mass[i]);
259 fposxyz[2] = posxyz[2] + pxyz[2] * hin[i] / (gamma * p.mass[i]);
260
261 /* Back to cylindrical coordinates (note phi is cumulative) */
262 p.r[i] = sqrt(fposxyz[0]*fposxyz[0] + fposxyz[1]*fposxyz[1]);
263 p.phi[i] += atan2(
264 posxyz[0] * fposxyz[1] - posxyz[1] * fposxyz[0],
265 posxyz[0] * fposxyz[0] + posxyz[1] * fposxyz[1] );
266 p.z[i] = fposxyz[2];
267
268 real cosp = cos(p.phi[i]);
269 real sinp = sin(p.phi[i]);
270 p.p_r[i] = pxyz[0] * cosp + pxyz[1] * sinp;
271 p.p_phi[i] = -pxyz[0] * sinp + pxyz[1] * cosp;
272 p.p_z[i] = pxyz[2];
273
274 real ds = hin[i];
275 p.mileage[i] += hin[i];
276
277 /* Update background values at the new position */
278 real psi, rho[2], pls_dens[MAX_SPECIES], pls_temp[MAX_SPECIES];
279 err = B_field_eval_psi(
280 &psi, p.r[i], p.phi[i], p.z[i], p.time[i], &sim->B_data);
281 if(!err) {
282 err = B_field_eval_rho(rho, psi, &sim->B_data);
283 }
284 if(!err && p.rho[i] <= 1.0 && rho[0] > 1.0) {
285 shinethrough[i] = 1;
286 }
287 p.rho[i] = rho[0];
288 /* Update theta value */
289 real axisrz[2];
290 if(!err) {
291 B_field_get_axis_rz(axisrz, &sim->B_data, p.phi[i]);
292 }
293 p.theta[i] = atan2(p.z[i]-axisrz[1], p.r[i]-axisrz[0]);
294
295 if(!err) {
297 pls_dens, pls_temp, rho[0], p.r[i], p.phi[i], p.z[i],
298 p.time[i], &sim->plasma_data);
299 }
300
301 /* Calculate ionization rate */
302 real rate = 0.0;
303 if(!err) {
304 real sigmav;
305 if( asigma_eval_bms(
306 &sigmav, p.znum[i], p.anum[i], ekin, p.mass[i],
307 n_species-1, pls_znum, pls_anum, pls_temp[0],
308 &(pls_dens[1]), &sim->asigma_data) ) {
309 err = 1;
310 }
311 rate = pls_dens[0] * sigmav;
312 }
313 remaining[i] *= exp(-rate * ds);
314
315 /* Check for end conditions */
316 if(!err) {
317 real w_coll = 0;
318 int tile = 0;
319 if(shinethrough[i]) {
320 tile = wall_hit_wall(
321 p0.r[i], p0.phi[i], p0.z[i],
322 p.r[i], p.phi[i], p.z[i], &sim->wall_data, &w_coll);
323 }
324 if(tile > 0) {
325 real w = w_coll;
326 p.time[i] = p0.time[i] + w*(p.time[i] - p0.time[i]);
327 p.r[i] = p0.r[i] + w*(p.r[i] - p0.r[i]);
328 p.phi[i] = p0.phi[i] + w*(p.phi[i] - p0.phi[i]);
329 p.z[i] = p0.z[i] + w*(p.z[i] - p0.z[i]);
330
331 p.walltile[i] = tile;
332 p.endcond[i] |= endcond_wall;
333 p.running[i] = 0;
334 }
335 if(p.mileage[i] > NBI_MAX_DISTANCE) {
336 p.endcond[i] |= endcond_tlim;
337 p.running[i] = 0;
338 }
339 if(remaining[i] < threshold[i]) {
340 p.charge[i] = 1*CONST_E;
341 p.endcond[i] |= endcond_ioniz;
342 p.running[i] = 0;
343 }
344 } else {
345 p.err[i] = err;
346 p.running[i] = 0;
347 }
348 }
349 }
350
351 /* Update markers that just finished */
352 #pragma omp simd
353 for(int i=0; i< NSIMD; i++) {
354 /* Use this as a flag for which markers to update in diagnostics */
355 pdiag.running[i] = 0;
356 if(!p.running[i] && p.id[i] >= 0) {
357 p.time[i] += p.mileage[i];
358
359 /* Reset these for the next marker */
360 threshold[i] = random_uniform(&sim->random_data);
361 remaining[i] = 1.0;
362 shinethrough[i] = 0;
363
364 /* Update the magnetic field at the marker position */
365 if(!p.err[i]) {
366 real B_dB[15];
367 B_field_eval_B_dB(B_dB, p.r[i], p.phi[i], p.z[i], p.time[i],
368 &sim->B_data);
369 p.B_r[i] = B_dB[0];
370 p.B_r_dr[i] = B_dB[1];
371 p.B_r_dphi[i] = B_dB[2];
372 p.B_r_dz[i] = B_dB[3];
373
374 p.B_phi[i] = B_dB[4];
375 p.B_phi_dr[i] = B_dB[5];
376 p.B_phi_dphi[i] = B_dB[6];
377 p.B_phi_dz[i] = B_dB[7];
378
379 p.B_z[i] = B_dB[8];
380 p.B_z_dr[i] = B_dB[9];
381 p.B_z_dphi[i] = B_dB[10];
382 p.B_z_dz[i] = B_dB[11];
383 }
384 }
385 particle_copy_fo(&p, i, &pdiag, i);
386 if(!p.running[i] && p.id[i] >= 0) {
387 pdiag.running[i] = 1;
388 }
389
390 /* Normalize weight with time and add hin so that we don't divide
391 * with zero when updating distributions */
392 pdiag.time[i] += hin[i];
393 pdiag.weight[i] /= hin[i];
394 }
395
396 /* Update distributions for markers that finished */
397 diag_update_fo(&sim->diag_data, &sim->B_data, &pdiag, &p);
398
399 /* Update running particles */
400 n_running = particle_cycle_fo(pq, &p, &sim->B_data, cycle);
401 }
402}
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:206
Functions to execute bbnbi externally.
Header file containing physical and mathematical constants.
#define CONST_E
Elementary charge [C].
Definition consts.h:35
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:182
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:94
#define math_unit(a, b)
Calculate unit vector b from a 3D vector a.
Definition math.h:74
#define math_xyz2rpz(xyz, rpz)
Convert cartesian coordinates xyz to cylindrical coordinates rpz.
Definition math.h:78
#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:87
#define math_rpz2xyz(rpz, xyz)
Convert cylindrical coordinates rpz to cartesian coordinates xyz.
Definition math.h:82
#define math_normc(a1, a2, a3)
Calculate norm of 3D vector from its components a1, a2, a3.
Definition math.h:71
#define math_norm(a)
Calculate norm of 3D vector a.
Definition math.h:68
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:372
int plasma_get_n_species(plasma_data *pls_data)
Get the number of plasma species.
Definition plasma.c:284
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:400
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:315
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:58
plasma_data plasma_data
Definition simulate.h:62
int mpi_rank
Definition simulate.h:136
random_data random_data
Definition simulate.h:74
B_field_data B_data
Definition simulate.h:60
wall_data wall_data
Definition simulate.h:64
int mpi_root
Definition simulate.h:135
asigma_data asigma_data
Definition simulate.h:67
diag_data diag_data
Definition simulate.h:69
nbi_data nbi_data
Definition simulate.h:68
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.