ASCOT5
Loading...
Searching...
No Matches
simulate_gc_adaptive.c
Go to the documentation of this file.
1
5#include <stdio.h>
6#include <stdlib.h>
7#include <time.h>
8#include <omp.h>
9#include <math.h>
10#include "../ascot5.h"
11#include "../endcond.h"
12#include "../math.h"
13#include "../consts.h"
14#include "../physlib.h"
15#include "../simulate.h"
16#include "../particle.h"
17#include "../wall.h"
18#include "../diag.h"
19#include "../B_field.h"
20#include "../E_field.h"
21#include "../boozer.h"
22#include "../mhd.h"
23#include "../rfof.h"
24#include "../plasma.h"
26#include "step/step_gc_cashkarp.h"
27#include "mccc/mccc.h"
28#include "mccc/mccc_wiener.h"
29
30DECLARE_TARGET_SIMD_UNIFORM(sim)
32
33#define DUMMY_TIMESTEP_VAL 1.0
34
55void simulate_gc_adaptive(particle_queue* pq, sim_data* sim, int mrk_array_size) {
56
57 /* Wiener arrays needed for the adaptive time step */
58 mccc_wienarr* wienarr = (mccc_wienarr*) malloc(mrk_array_size*sizeof(mccc_wienarr));
59
60 /* Current time step, suggestions for the next time step and next time
61 * step */
62 real* hin = (real*) malloc(mrk_array_size*sizeof(real));
63 real* hout_orb = (real*) malloc(mrk_array_size*sizeof(real));
64 real* hout_col = (real*) malloc(mrk_array_size*sizeof(real));
65 real* hout_rfof = (real*) malloc(mrk_array_size*sizeof(real));
66 real* hnext = (real*) malloc(mrk_array_size*sizeof(real));
67
68 Acceleration acceleration;
69 acceleration_allocate(&acceleration, mrk_array_size);
70 /* Flag indicateing whether a new marker was initialized */
71 int* cycle = (int*) malloc(mrk_array_size*sizeof(int));
72
73 real tol_col = sim->ada_tol_clmbcol;
74 real tol_orb = sim->ada_tol_orbfol;
75
76 real cputime, cputime_last; // Global cpu time: recent and previous record
77
78 particle_simd_gc p; // This array holds current states
79 particle_simd_gc p0; // This array stores previous states
80 particle_allocate_gc(&p, mrk_array_size);
81 particle_allocate_gc(&p0, mrk_array_size);
82
83 rfof_marker rfof_mrk; // RFOF specific data
84
85 for(int i=0; i< mrk_array_size; i++) {
86 p.id[i] = -1;
87 p.running[i] = 0;
88 acceleration.acc[i] = 1.0;
89 acceleration.orbittime[i] = -1;
90 acceleration.cross[i].crossed_once = 0;
91 }
92
93 /* Initialize running particles */
94 int n_running = particle_cycle_gc(pq, &p, &sim->B_data, cycle);
95
96 if(sim->enable_icrh) {
97 rfof_set_up(&rfof_mrk, &sim->rfof_data);
98 }
99
100 #pragma omp simd
101 for(int i = 0; i < mrk_array_size; i++) {
102 if(cycle[i] > 0) {
103 /* Determine initial time-step */
104 hin[i] = simulate_gc_adaptive_inidt(sim, &p, i);
105 if(sim->enable_clmbcol) {
106 /* Allocate array storing the Wiener processes */
107 mccc_wiener_initialize(&(wienarr[i]), p.time[i]);
108 }
109 }
110 }
111
112 cputime_last = A5_WTIME;
113
114 /* MAIN SIMULATION LOOP
115 * - Store current state
116 * - Integrate motion due to bacgkround EM-field (orbit-following)
117 * - Integrate scattering due to Coulomb collisions
118 * - Check whether time step was accepted
119 * - NO: revert to initial state and ignore the end of the loop
120 * (except CPU_TIME_MAX end condition if this is implemented)
121 * - YES: update particle time, clean redundant Wiener processes, and
122 * proceed
123 * - Check for end condition(s)
124 * - Update diagnostics
125 */
126 real* rnd = (real*) malloc(5*mrk_array_size*sizeof(real));
129 acceleration_offload(&acceleration,mrk_array_size);
130 GPU_MAP_TO_DEVICE(hin[0:mrk_array_size],rnd[0:5*mrk_array_size],hout_orb[0:mrk_array_size],hout_col[0:mrk_array_size],hout_rfof[0:mrk_array_size],hnext[0:mrk_array_size],cycle[0:mrk_array_size])
131 mccc_wiener_offload(wienarr,mrk_array_size);
132 while(n_running > 0) {
133
134 /* Store marker states in case time step will be rejected */
135 GPU_PARALLEL_LOOP_ALL_LEVELS
136 for(int i = 0; i < p.n_mrk; i++) {
137 particle_copy_gc(&p, i, &p0, i);
138 hout_orb[i] = DUMMY_TIMESTEP_VAL;
139 hout_col[i] = DUMMY_TIMESTEP_VAL;
140 hout_rfof[i] = DUMMY_TIMESTEP_VAL;
141 hnext[i] = DUMMY_TIMESTEP_VAL;
142 }
143
144 /*************************** Physics **********************************/
145
146 /* Set time-step negative if tracing backwards in time */
147 GPU_PARALLEL_LOOP_ALL_LEVELS
148 for(int i = 0; i < p.n_mrk; i++) {
149 if(sim->reverse_time) {
150 hin[i] = -hin[i];
151 }
152 }
153
154 /* Cash-Karp method for orbit-following */
155 if(sim->enable_orbfol) {
156 if(sim->enable_mhd) {
158 &p, hin, hout_orb, tol_orb, &sim->B_data, &sim->E_data,
159 &sim->boozer_data, &sim->mhd_data, sim->enable_aldforce);
160 }
161 else {
163 &p, hin, hout_orb, tol_orb, &sim->B_data, &sim->E_data,
164 sim->enable_aldforce);
165 }
166 /* Check whether time step was rejected */
167 GPU_PARALLEL_LOOP_ALL_LEVELS
168 for(int i = 0; i < p.n_mrk; i++) {
169 /* Switch sign of the time-step again if it was reverted earlier
170 */
171 if(sim->reverse_time) {
172 hout_orb[i] = -hout_orb[i];
173 hin[i] = -hin[i];
174 }
175 if(p.running[i] && hout_orb[i] < 0){
176 p.running[i] = 0;
177 hnext[i] = hout_orb[i];
178 }
179 }
180 }
181
182 /* Milstein method for collisions */
183 if(sim->enable_clmbcol) {
184 random_normal_simd(sim->random_data, 5*p.n_mrk, rnd);
185 mccc_gc_milstein(&p, hin, acceleration.acc, acceleration.collfreq,
186 hout_col, tol_col, wienarr, &sim->B_data,
187 &sim->plasma_data, &sim->mccc_data, rnd);
188
189 /* Check whether time step was rejected */
190 GPU_PARALLEL_LOOP_ALL_LEVELS
191 for(int i = 0; i < p.n_mrk; i++) {
192 if(p.running[i] && hout_col[i] < 0){
193 p.running[i] = 0;
194 hnext[i] = hout_col[i];
195 }
196 }
197 }
198
199 /* Performs the ICRH kick if in resonance. */
200 if(sim->enable_icrh) {
201 rfof_resonance_check_and_kick_gc(
202 &p, hin, hout_rfof, &rfof_mrk, &sim->rfof_data, &sim->B_data);
203
204 /* Check whether time step was rejected */
205 GPU_PARALLEL_LOOP_ALL_LEVELS
206 for(int i = 0; i < p.n_mrk; i++) {
207 if(p.running[i] && hout_rfof[i] < 0){
208 p.running[i] = 0;
209 hnext[i] = hout_rfof[i];
210 }
211 }
212 }
213
214 /**********************************************************************/
215
216 cputime = A5_WTIME;
217 GPU_PARALLEL_LOOP_ALL_LEVELS
218 for(int i = 0; i < p.n_mrk; i++) {
219 if(p.id[i] > 0 && !p.err[i]) {
220 /* Check other time step limitations */
221 if(hnext[i] > 0) {
222 real dphi = fabs(p0.phi[i]-p.phi[i]) / sim->ada_max_dphi;
223 real drho = fabs(p0.rho[i]-p.rho[i]) / sim->ada_max_drho;
224
225 if(dphi > 1 && dphi > drho) {
226 hnext[i] = -hin[i]/dphi;
227 }
228 else if(drho > 1 && drho > dphi) {
229 hnext[i] = -hin[i]/drho;
230 }
231 }
232
233 /* Retrieve marker states in case time step was rejected */
234 if(hnext[i] < 0) {
235 particle_copy_gc(&p0, i, &p, i);
236 }
237 if(p.running[i]){
238
239 /* Advance time (if time step was accepted) and determine
240 next time step */
241 if(hnext[i] < 0){
242 /* if hnext < 0, you screwed up and had to copy the
243 previous state. Therefore, let us use the suggestion
244 given by the integrator when retaking the failed step.*/
245 hin[i] = -hnext[i];
246 }
247 else {
248 p.time[i] += ( 1.0 - 2.0 * ( sim->reverse_time > 0 ) )
249 * hin[i] * acceleration.acc[i];
250 p.mileage[i] += hin[i] * acceleration.acc[i];
251 if(acceleration.orbittime[i] >= 0)
252 acceleration.orbittime[i] += hin[i] * acceleration.acc[i];
253 /* In case the time step was succesful, pick the
254 smallest recommended value for the next step */
255 if(hnext[i] > hout_orb[i]) {
256 /* Use time step suggested by the orbit-following
257 integrator */
258 hnext[i] = hout_orb[i];
259 }
260 if(hnext[i] > hout_col[i]) {
261 /* Use time step suggested by the collision
262 integrator */
263 hnext[i] = hout_col[i];
264 }
265 if(hnext[i] > hout_rfof[i]) {
266 /* Use time step suggested by RFOF */
267 hnext[i] = hout_rfof[i];
268 }
269 if(hnext[i] == 1.0) {
270 /* Time step is unchanged (happens when no physics
271 are enabled) */
272 hnext[i] = hin[i];
273 }
274 hin[i] = hnext[i];
275 if(sim->enable_clmbcol) {
276 /* Clear wiener processes */
277 mccc_wiener_clean(&(wienarr[i]), p.time[i]);
278 }
279 }
280
281 p.cputime[i] += cputime - cputime_last;
282 }
283 }
284 }
285 cputime_last = cputime;
286
287 /* If OMP is crossed, adjust acceleration */
288 if(sim->enable_ada > 1) {
289 recalculate_acceleration(&acceleration, sim, &p, &p0);
290 }
291
292 /* Check possible end conditions */
293 endcond_check_gc(&p, &p0, sim);
294
295 /* Update diagnostics */
296 diag_update_gc(&sim->diag_data, &sim->B_data, &p, &p0);
297
298 /* Update number of running particles */
299#ifdef GPU
300 n_running = 0;
301 GPU_PARALLEL_LOOP_ALL_LEVELS_REDUCTION(n_running)
302 for(int i = 0; i < p.n_mrk; i++)
303 {
304 if(p.running[i] > 0) n_running++;
305 }
306#else
307 n_running = particle_cycle_gc(pq, &p, &sim->B_data, cycle);
308
309 /* Determine simulation time-step for new particles */
310 #pragma omp simd
311 for(int i = 0; i <p.n_mrk; i++) {
312 if(cycle[i] > 0) {
313 hin[i] = simulate_gc_adaptive_inidt(sim, &p, i);
314 acceleration.acc[i] = 1.0;
315 acceleration.orbittime[i] = -1;
316 acceleration.cross[i].crossed_once = 0;
317 if(sim->enable_clmbcol) {
318 /* Re-allocate array storing the Wiener processes */
319 mccc_wiener_initialize(&(wienarr[i]), p.time[i]);
320 }
321 if(sim->enable_icrh) {
322 /* Reset icrh (rfof) resonance memory matrix. */
323 rfof_clear_history(&rfof_mrk, i);
324 }
325 }
326 }
327#endif
328 }
329
330 /* All markers simulated! */
331#ifdef GPU
332 GPU_MAP_FROM_DEVICE(sim[0:1])
334 n_running = particle_cycle_gc(pq, &p, &sim->B_data, cycle);
335 mccc_wiener_onload(wienarr,mrk_array_size);
336#endif
337 free(cycle);
338 free(hin);
339 free(rnd);
340 free(hout_orb);
341 free(hout_col);
342 free(hout_rfof);
343 free(hnext);
344 /* Deallocate rfof structs */
345 if(sim->enable_icrh) {
346 rfof_tear_down(&rfof_mrk);
347 }
348}
349
363 /* Just use some large value if no physics are defined */
365
366 /* Value defined directly by user */
367 if(sim->fix_usrdef_use) {
368 h = sim->fix_usrdef_val;
369 }
370 else {
371 /* Value calculated from gyrotime */
372 if(sim->enable_orbfol) {
373 real Bnorm = math_normc(p->B_r[i], p->B_phi[i], p->B_z[i]);
374 real gyrotime = CONST_2PI /
375 phys_gyrofreq_ppar(p->mass[i], p->charge[i], p->mu[i],
376 p->ppar[i], Bnorm);
377 if(h > gyrotime) {
378 h = gyrotime;
379 }
380 }
381
382 /* Value calculated from collision frequency */
383 if(sim->enable_clmbcol) {
384 real nu = 1;
385 /*mccc_collfreq_gc(p, &sim->B_data, &sim->plasma_data,
386 sim->coldata, &nu, i); */
387
388 /* Only small angle collisions so divide this by 100 */
389 real colltime = 1/(100*nu);
390 if(h > colltime) {h=colltime;}
391 }
392 }
393 return h;
394}
395
396
414{
415 real rz[2];
416 real SAFETY_FACTOR = (float)sim->enable_ada / 1000.0;
417 GPU_PARALLEL_LOOP_ALL_LEVELS
418 for(int i = 0; i < p->n_mrk; i++) {
419 B_field_get_axis_rz(rz, &sim->B_data, p->phi[i]);
420 int omp_crossed = ((p->z[i] - rz[1]) * (p0->z[i] - rz[1]) < 0) &&
421 p->r[i] > rz[0];
422 if(omp_crossed && acc->cross[i].crossed_twice) {
423 if( ((float)acc->cross[i].first_ppar - 0.5) * p->ppar[i] > 0 ) {
424 acc->acc[i] = fmax(1.0, SAFETY_FACTOR / (acc->orbittime[i] * acc->collfreq[i]));
425 }
426 else {
427 acc->acc[i] = 1;
428 }
429 acc->cross[i].crossed_once = 1;
430 acc->cross[i].crossed_twice = 0;
431 acc->cross[i].first_ppar = p->ppar[i] > 0;
432 acc->orbittime[i] = 0;
433 }
434 else if(omp_crossed && acc->cross[i].crossed_once) {
435 acc->cross[i].crossed_twice = 1;
436 if( ((float)acc->cross[i].first_ppar - 0.5) * p->ppar[i] > 0 ) {
437 acc->acc[i] = fmax(1.0, SAFETY_FACTOR / (acc->orbittime[i] * acc->collfreq[i]));
438 acc->cross[i].crossed_once = 1;
439 acc->cross[i].crossed_twice = 0;
440 acc->cross[i].first_ppar = p->ppar[i] > 0;
441 acc->orbittime[i] = 0;
442 }
443 }
444 else if(omp_crossed) {
445 acc->cross[i].crossed_once = 1;
446 acc->cross[i].first_ppar = p->ppar[i] > 0;
447 acc->orbittime[i] = 0;
448 }
449 }
450}
451
461void acceleration_allocate(Acceleration* acceleration, int nmrk){
462 acceleration->acc = malloc(nmrk * sizeof(acceleration->acc) );
463 acceleration->orbittime = malloc(nmrk * sizeof(acceleration->orbittime) );
464 acceleration->collfreq = malloc(nmrk * sizeof(acceleration->collfreq) );
465 acceleration->cross = malloc(nmrk * sizeof(acceleration->cross) );
466}
467
472void acceleration_offload(Acceleration* acceleration, int mrk_array_size) {
473 GPU_MAP_TO_DEVICE(
474 acceleration[0:1],\
475 acceleration->acc [0:mrk_array_size],\
476 acceleration->orbittime [0:mrk_array_size],\
477 acceleration->collfreq [0:mrk_array_size],\
478 acceleration->cross [0:mrk_array_size]
479 )
480}
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.
Header file for E_field.c.
Main header file for ASCOT5.
double real
Definition ascot5.h:85
#define A5_WTIME
Wall time.
Definition ascot5.h:124
Header file for boozer.c.
Header file containing physical and mathematical constants.
#define CONST_2PI
2*pi
Definition consts.h:14
void diag_update_gc(diag_data *data, B_field_data *Bdata, particle_simd_gc *p_f, particle_simd_gc *p_i)
Collects diagnostics when marker represents a guiding center.
Definition diag.c:217
Header file for diag.c.
void endcond_check_gc(particle_simd_gc *p_f, particle_simd_gc *p_i, sim_data *sim)
Check end conditions for GC markers.
Definition endcond.c:277
Header file for endcond.c.
Header file for math.c.
#define math_normc(a1, a2, a3)
Calculate norm of 3D vector from its components a1, a2, a3.
Definition math.h:71
Header file for mccc package.
void mccc_gc_milstein(particle_simd_gc *p, real *hin, real *acc, real *collfreq, real *hout, real tol, mccc_wienarr *w, B_field_data *Bdata, plasma_data *pdata, mccc_data *mdata, real *rnd)
Integrate collisions for one time-step.
void mccc_wiener_onload(mccc_wienarr *w, int mrk_array_size)
Onload data from the GPU.
Definition mccc_wiener.c:73
a5err mccc_wiener_clean(mccc_wienarr *w, real t)
Removes Wiener processes from the array that are no longer required.
void mccc_wiener_offload(mccc_wienarr *w, int mrk_array_size)
Offload data to the accelerator.
Definition mccc_wiener.c:57
void mccc_wiener_initialize(mccc_wienarr *w, real initime)
Initializes a struct that stores generated Wiener processes.
Definition mccc_wiener.c:36
header file for mccc_wiener.c
Header file for mhd.c.
void particle_allocate_gc(particle_simd_gc *p_gc, int nmrk)
Allocates guiding center struct representing particle markers.
Definition particle.c:126
void particle_onload_gc(particle_simd_gc *p)
Onload guiding center particle struct from the GPU.
Definition particle.c:1914
void particle_copy_gc(particle_simd_gc *p1, int i, particle_simd_gc *p2, int j)
Copy GC struct.
Definition particle.c:1407
void particle_offload_gc(particle_simd_gc *p)
Offload guiding center particle struct to GPU.
Definition particle.c:1870
int particle_cycle_gc(particle_queue *q, particle_simd_gc *p, B_field_data *Bdata, int *cycle)
Replace finished GC markers with new ones or dummies.
Definition particle.c:419
Header file for particle.c.
Methods to evaluate elementary physical quantities.
#define phys_gyrofreq_ppar(m, q, mu, ppar, B)
Evaluate gyrofrequency [rad/s] from parallel momentum and magnetic moment.
Definition physlib.h:278
Header file for plasma.c.
#define random_normal_simd(data, n, r)
Definition random.h:115
Contains the functions to be called from the simulation loop when using ICRH.
Header file for simulate.c.
void simulate_gc_adaptive(particle_queue *pq, sim_data *sim, int mrk_array_size)
Simulates guiding centers using adaptive time-step.
void recalculate_acceleration(Acceleration *acc, sim_data *sim, particle_simd_gc *p, particle_simd_gc *p0)
void acceleration_offload(Acceleration *acceleration, int mrk_array_size)
Offload acceleration struct to GPU.
real simulate_gc_adaptive_inidt(sim_data *sim, particle_simd_gc *p, int i)
Calculates time step value.
#define DUMMY_TIMESTEP_VAL
void acceleration_allocate(Acceleration *acceleration, int nmrk)
Allocates struct representing acceleration struc.
Header file for simulate_gc_adaptive.c.
void step_gc_cashkarp_mhd(particle_simd_gc *p, real *h, real *hnext, real tol, B_field_data *Bdata, E_field_data *Edata, boozer_data *boozer, mhd_data *mhd, int aldforce)
Integrate a guiding center step for a struct of markers with MHD.
void step_gc_cashkarp(particle_simd_gc *p, real *h, real *hnext, real tol, B_field_data *Bdata, E_field_data *Edata, int aldforce)
Integrate a guiding center step for a struct of markers.
Struct for storing Wiener processes.
Definition mccc_wiener.h:28
Marker queue.
Definition particle.h:154
Struct representing NSIMD guiding center markers.
Definition particle.h:275
integer * running
Definition particle.h:320
integer * id
Definition particle.h:312
Reusable struct for storing marker specific data during the simulation loop.
Definition rfof.h:19
Simulation data struct.
Definition simulate.h:58
int enable_orbfol
Definition simulate.h:99
real ada_max_drho
Definition simulate.h:93
real ada_tol_clmbcol
Definition simulate.h:91
plasma_data plasma_data
Definition simulate.h:62
mhd_data mhd_data
Definition simulate.h:66
rfof_data rfof_data
Definition simulate.h:70
real fix_usrdef_val
Definition simulate.h:84
E_field_data E_data
Definition simulate.h:61
int enable_aldforce
Definition simulate.h:104
int enable_mhd
Definition simulate.h:101
int fix_usrdef_use
Definition simulate.h:83
random_data * random_data
Definition simulate.h:74
mccc_data mccc_data
Definition simulate.h:75
boozer_data boozer_data
Definition simulate.h:65
int enable_ada
Definition simulate.h:79
B_field_data B_data
Definition simulate.h:60
int reverse_time
Definition simulate.h:113
real ada_max_dphi
Definition simulate.h:95
int enable_icrh
Definition simulate.h:103
int enable_clmbcol
Definition simulate.h:100
real ada_tol_orbfol
Definition simulate.h:89
diag_data diag_data
Definition simulate.h:69
Header file for wall.c.