ASCOT5
Loading...
Searching...
No Matches
mccc_gc_milstein.c
Go to the documentation of this file.
1
5#include <math.h>
6#include <float.h>
7#include "../../ascot5.h"
8#include "../../consts.h"
9#include "../../math.h"
10#include "../../physlib.h"
11#include "../../error.h"
12#include "../../particle.h"
13#include "../../B_field.h"
14#include "../../plasma.h"
15#include "../../random.h"
16#include "mccc_wiener.h"
17#include "mccc_coefs.h"
18#include "mccc.h"
19
34void mccc_gc_milstein(particle_simd_gc* p, real* hin, real* acc, real* collfreq, real* hout, real tol,
35 mccc_wienarr* w, B_field_data* Bdata, plasma_data* pdata,
36 mccc_data* mdata, real* rnd) {
37
38 /* Get plasma information before going to the SIMD loop */
39 int n_species = plasma_get_n_species(pdata);
40 const real* qb = plasma_get_species_charge(pdata);
41 const real* mb = plasma_get_species_mass(pdata);
42
43 GPU_DATA_IS_MAPPED(hin[0:p->n_mrk], hout[0:p->n_mrk], rnd[0:5*p->n_mrk], w[0:p->n_mrk], acc[0:p->n_mrk], collfreq[0:p->n_mrk])
44 GPU_PARALLEL_LOOP_ALL_LEVELS
45 for(int i = 0; i < p->n_mrk; i++) {
46 if(p->running[i]) {
47 a5err errflag = 0;
48
49 /* Initial (R,z) position and magnetic field are needed for later */
50 real Brpz[3] = {p->B_r[i], p->B_phi[i], p->B_z[i]};
51 real Bnorm = math_norm(Brpz);
52 real Bxyz[3];
53 math_vec_rpz2xyz(Brpz, Bxyz, p->phi[i]);
54 real R0 = p->r[i];
55 real z0 = p->z[i];
56
57 /* Move guiding center to (x, y, z, vnorm, xi) coordinates */
58 real vin, pin, vflow, xiin, Xin_xyz[3], vpar, vperp2;
59 Xin_xyz[0] = p->r[i] * cos(p->phi[i]);
60 Xin_xyz[1] = p->r[i] * sin(p->phi[i]);
61 Xin_xyz[2] = p->z[i];
62 if(!errflag) {
63 errflag = plasma_eval_flow(
64 &vflow, p->rho[i], p->r[i], p->phi[i], p->z[i], p->time[i],
65 pdata);
66 }
67 pin = physlib_gc_p(p->mass[i], p->mu[i], p->ppar[i], Bnorm);
68 xiin = physlib_gc_xi(p->mass[i], p->mu[i], p->ppar[i], Bnorm);
69 vin = physlib_vnorm_pnorm(p->mass[i], pin);
70 vpar = xiin * vin;
71 vperp2 = (1 - xiin * xiin) * vin * vin;
72 vin = sqrt((vpar - vflow) * (vpar - vflow) + vperp2);
73 xiin = (vpar - vflow) / vin;
74
75 /* Evaluate plasma density and temperature */
77 if(!errflag) {
78 errflag = plasma_eval_densandtemp(nb, Tb, p->rho[i],
79 p->r[i], p->phi[i], p->z[i],
80 p->time[i], pdata);
81 }
82
83 /* Coulomb logarithm */
84 real clogab[MAX_SPECIES];
85 mccc_coefs_clog(clogab, p->mass[i], p->charge[i], vin,
86 n_species, mb, qb, nb, Tb);
87
88 /* Evaluate collision coefficients and sum them for each *
89 * species */
90 real gyrofreq = phys_gyrofreq_pnorm(p->mass[i], p->charge[i], pin,
91 Bnorm);
92 real K = 0, Dpara = 0, dDpara = 0, dQ = 0, nu = 0, DX = 0;
93 GPU_SEQUENTIAL_LOOP
94 for(int j = 0; j < n_species; j++) {
95 real vb = sqrt( 2 * Tb[j] / mb[j] );
96 real x = vin / vb;
97 real mufun[3];
98 mccc_coefs_mufun(mufun, x, mdata);
99
100 real Qb = mccc_coefs_Q(p->mass[i], p->charge[i], mb[j],
101 qb[j], nb[j], vb, clogab[j],
102 mufun[0]);
103 real Dparab = mccc_coefs_Dpara(p->mass[i], p->charge[i], vin,
104 qb[j], nb[j], vb, clogab[j],
105 mufun[0]);
106 real Dperpb = mccc_coefs_Dperp(p->mass[i], p->charge[i], vin,
107 qb[j], nb[j], vb, clogab[j],
108 mufun[1]);
109 real dDparab = mccc_coefs_dDpara(p->mass[i], p->charge[i], vin,
110 qb[j], nb[j], vb, clogab[j],
111 mufun[0], mufun[2]);
112
113 K += mccc_coefs_K(vin, Dparab, dDparab, Qb);
114 dQ += mccc_coefs_dQ(p->mass[i], p->charge[i], mb[j],
115 qb[j], nb[j], vb, clogab[j], mufun[2]);
116 Dpara += Dparab;
117 dDpara += dDparab;
118 nu += mccc_coefs_nu(vin, Dperpb);
119 DX += mccc_coefs_DX(xiin, Dparab, Dperpb, gyrofreq);
120 }
121
122 /* Generate Wiener process for this step */
123 int tindex;
124 if(!errflag) {
125 errflag = mccc_wiener_generate(&w[i], w[i].time[0]+hin[i]*acc[i],
126 &tindex, &rnd[i*5]);
127 }
128 real dW[5] = {0, 0, 0, 0, 0};
129 if(!errflag) {
130 dW[0] = w[i].wiener[tindex*5 + 0] - w[i].wiener[0]; // For X_1
131 dW[1] = w[i].wiener[tindex*5 + 1] - w[i].wiener[1]; // For X_2
132 dW[2] = w[i].wiener[tindex*5 + 2] - w[i].wiener[2]; // For X_3
133 dW[3] = w[i].wiener[tindex*5 + 3] - w[i].wiener[3]; // For v
134 dW[4] = w[i].wiener[tindex*5 + 4] - w[i].wiener[4]; // For xi
135 }
136
137 /* Evaluate collisions */
138
139 real bhat[3];
140 math_unit(Bxyz,bhat);
141
142 real k1 = sqrt(2*DX);
143 real k2 = math_dot(bhat, dW);
144
145 real vout, xiout, Xout_xyz[3];
146 Xout_xyz[0] = Xin_xyz[0] + k1 * ( dW[0] - k2 * bhat[0] );
147 Xout_xyz[1] = Xin_xyz[1] + k1 * ( dW[1] - k2 * bhat[1] );
148 Xout_xyz[2] = Xin_xyz[2] + k1 * ( dW[2] - k2 * bhat[2] );
149 vout = vin + K*hin[i]*acc[i] + sqrt( 2 * Dpara ) * dW[3]
150 + 0.5 * dDpara * ( dW[3]*dW[3] - hin[i]*acc[i] );
151 xiout = xiin - xiin*nu*hin[i]*acc[i] + sqrt( ( 1 - xiin*xiin ) * nu )*dW[4]
152 - 0.5 * xiin * nu * ( dW[4]*dW[4] - hin[i]*acc[i] );
153
154 /* Enforce boundary conditions */
155 real cutoff = MCCC_CUTOFF * sqrt( Tb[0] / p->mass[i] );
156 if(vout < cutoff){
157 vout = 2 * cutoff - vout;
158 }
159
160 if(fabs(xiout) > 1){
161 xiout = ( (xiout > 0) - (xiout < 0) )
162 * ( 2 - fabs( xiout ) );
163 }
164
165 /* Compute error estimates for drift and diffusion limits */
166
167 // xi is limited to interval [-1, 1] but for v we need some value
168 // to translate relative error to absolute error.
169 real v0 = ( vin + fabs(K) * hin[i]*acc[i] + sqrt( 2*Dpara*hin[i]*acc[i] ) )
170 + DBL_EPSILON;
171 real verr = fabs( K*dQ ) / (2*tol*v0);
172 real xierr = fabs( xiin*nu*nu ) / (2*tol);
173
174 // kappa_k is error due to drift
175 real kappa_k;
176 if(verr > xierr){
177 kappa_k = verr*hin[i]*hin[i]*acc[i]*acc[i];
178 }
179 else{
180 kappa_k = xierr*hin[i]*hin[i]*acc[i]*acc[i];
181 }
182
183 // kappa_d is error due to diffusion (v and xi are both needed)
184 real kappa_d0 = fabs( dW[3]*dW[3]*dW[3]
185 * dDpara*dDpara / sqrt( Dpara ) ) / (6*tol*v0);
186 real kappa_d1 = sqrt( 1 - xiin*xiin ) * nu * sqrt( nu )
187 * fabs( dW[4] + sqrt( hin[i]*acc[i]/3 ) ) * hin[i]*acc[i] / (2*tol);
188
189 /* Remove energy or pitch change or spatial diffusion from the *
190 * results if that is requested */
191 if(!mdata->include_energy) {
192 vout = vin;
193 }
194 if(!mdata->include_pitch) {
195 xiout = xiin;
196 }
197 if(!mdata->include_gcdiff) {
198 Xout_xyz[0] = Xin_xyz[0];
199 Xout_xyz[1] = Xin_xyz[1];
200 Xout_xyz[2] = Xin_xyz[2];
201 }
202
203 vpar = xiout * vout;
204 vperp2 = (1 - xiout * xiout) * vout * vout;
205 vout = sqrt((vpar + vflow) * (vpar + vflow) + vperp2);
206 xiout = (vpar + vflow) / vout;
207 real pout = physlib_pnorm_vnorm(p->mass[i], vout);
208
209 /* Back to cylindrical coordinates */
210 real Xout_rpz[3];
211 math_xyz2rpz(Xout_xyz, Xout_rpz);
212
213 /* Evaluate magnetic field (and gradient) and rho at new position */
214 real B_dB[15], psi[1], rho[2];
215 if(!errflag) {
216 errflag = B_field_eval_B_dB(B_dB, Xout_rpz[0], Xout_rpz[1],
217 Xout_rpz[2], p->time[i] + hin[i]*acc[i],
218 Bdata);
219 }
220 if(!errflag) {
221 errflag = B_field_eval_psi(psi, Xout_rpz[0], Xout_rpz[1],
222 Xout_rpz[2], p->time[i] + hin[i]*acc[i],
223 Bdata);
224 }
225 if(!errflag) {
226 errflag = B_field_eval_rho(rho, psi[0], Bdata);
227 }
228
229 if(!errflag) {
230 /* Update marker coordinates at the new position */
231 p->B_r[i] = B_dB[0];
232 p->B_r_dr[i] = B_dB[1];
233 p->B_r_dphi[i] = B_dB[2];
234 p->B_r_dz[i] = B_dB[3];
235
236 p->B_phi[i] = B_dB[4];
237 p->B_phi_dr[i] = B_dB[5];
238 p->B_phi_dphi[i] = B_dB[6];
239 p->B_phi_dz[i] = B_dB[7];
240
241 p->B_z[i] = B_dB[8];
242 p->B_z_dr[i] = B_dB[9];
243 p->B_z_dphi[i] = B_dB[10];
244 p->B_z_dz[i] = B_dB[11];
245
246 p->rho[i] = rho[0];
247
248 Bnorm = math_normc(B_dB[0], B_dB[4], B_dB[8]);
249
250 p->r[i] = Xout_rpz[0];
251 p->z[i] = Xout_rpz[2];
252 p->mu[i] = physlib_gc_mu(p->mass[i], pout, xiout, Bnorm);
253 p->ppar[i] = physlib_gc_ppar(pout, xiout);
254
255 /* Evaluate phi and theta angles so that they are cumulative */
256 real axisrz[2];
257 errflag = B_field_get_axis_rz(axisrz, Bdata, p->phi[i]);
258 p->theta[i] += atan2( (R0-axisrz[0]) * (p->z[i]-axisrz[1])
259 - (z0-axisrz[1]) * (p->r[i]-axisrz[0]),
260 (R0-axisrz[0]) * (p->r[i]-axisrz[0])
261 + (z0-axisrz[1]) * (p->z[i]-axisrz[1]) );
262 p->phi[i] += atan2( Xin_xyz[0] * Xout_xyz[1]
263 - Xin_xyz[1] * Xout_xyz[0],
264 Xin_xyz[0] * Xout_xyz[0]
265 + Xin_xyz[1] * Xout_xyz[1] );
266 }
267
268 /* Check whether timestep was rejected and suggest next time step */
269
270 if( kappa_k >= kappa_d0 && kappa_k >= kappa_d1 ) {
271 /* Drift error dominates */
272 hout[i] = 0.8 * hin[i] / sqrt( kappa_k );
273 }
274 else if( kappa_d0 >= kappa_k && kappa_d0 >= kappa_d1 ) {
275 /* Velocity diffusion error dominates */
276 hout[i] = 0.9 * hin[i] * pow( kappa_d0, -2.0/3.0 );
277 }
278 else {
279 /* Pitch diffusion error dominates */
280 hout[i] = 0.9 * hin[i] * pow( kappa_d1, -2.0/3.0 );
281 }
282
283 /* Negative value indicates time step was rejected*/
284 if( kappa_k > 1 || kappa_d0 > 1 || kappa_d1 > 1 ){
285 hout[i] = -hout[i];
286 }
287 else if(hout[i] > 1.5*hin[i]) {
288 /* Make sure we don't increase time step too much */
289 hout[i] = 1.5*hin[i];
290 }
291
292 /* Error handling */
293 if(errflag) {
294 p->err[i] = errflag;
295 p->running[i] = 0;
296 }
297
298 collfreq[i] = nu;
299 }
300 }
301}
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.
double real
Definition ascot5.h:85
#define MAX_SPECIES
Maximum number of plasma species.
Definition ascot5.h:95
Header file containing physical and mathematical constants.
Error module for ASCOT5.
unsigned long int a5err
Simulation error flag.
Definition error.h:17
Header file for math.c.
#define math_dot(a, b)
Calculate dot product a[3] dot b[3].
Definition math.h:32
#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_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
Header file for mccc package.
#define MCCC_CUTOFF
Defines minimum energy boundary condition.
Definition mccc.h:22
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.
Routines to evaluate coefficients needed to evaluate collisions.
#define mccc_coefs_Dpara(ma, qa, va, qb, nb, vb, clogab, mu0)
Evaluate non-relativistic parallel diffusion coefficient [m^2/s^3].
Definition mccc_coefs.h:103
#define mccc_coefs_dDpara(ma, qa, va, qb, nb, vb, clogab, mu0, dmu0)
Evaluate derivative of non-relativistic parallel diffusion coefficient [m/s^2].
Definition mccc_coefs.h:126
#define mccc_coefs_dQ(ma, qa, mb, qb, nb, vb, clogab, dmu0)
Evaluate derivative of non-relativistic drag coefficient [m/s^2].
Definition mccc_coefs.h:61
#define mccc_coefs_K(va, Dpara, dDpara, Q)
Evaluate guiding center drag coefficient [m/s^2].
Definition mccc_coefs.h:167
#define mccc_coefs_Dperp(ma, qa, va, qb, nb, vb, clogab, mu1)
Evaluate non-relativistic perpendicular diffusion coefficient [m^2/s^3].
Definition mccc_coefs.h:150
#define mccc_coefs_nu(va, Dperp)
Evaluate pitch collision frequency [1/s].
Definition mccc_coefs.h:180
#define mccc_coefs_DX(xi, Dpara, Dperp, gyrofreq)
Evaluate spatial diffusion coefficient [m^2/s].
Definition mccc_coefs.h:195
#define mccc_coefs_Q(ma, qa, mb, qb, nb, vb, clogab, mu0)
Evaluate non-relativistic drag coefficient [m/s^2].
Definition mccc_coefs.h:43
a5err mccc_wiener_generate(mccc_wienarr *w, real t, int *windex, real *rand5)
Generates a new Wiener process at a given time instant.
Definition mccc_wiener.c:96
header file for mccc_wiener.c
Header file for particle.c.
Methods to evaluate elementary physical quantities.
#define physlib_gc_xi(m, mu, ppar, B)
Evaluate guiding center pitch from parallel momentum and magnetic moment.
Definition physlib.h:214
#define physlib_pnorm_vnorm(m, v)
Evaluate momentum norm [kg m/s] from velocity norm.
Definition physlib.h:154
#define physlib_vnorm_pnorm(m, p)
Evaluate velocity norm [m/s] from momentum norm.
Definition physlib.h:141
#define phys_gyrofreq_pnorm(m, q, p, B)
Evaluate gyrofrequency [rad/s] from momentum norm.
Definition physlib.h:261
#define physlib_gc_ppar(p, xi)
Evaluate guiding center parallel momentum [kg m/s] from momentum norm and pitch.
Definition physlib.h:167
#define physlib_gc_p(m, mu, ppar, B)
Evaluate guiding center momentum norm [kg m/s] from parallel momentum and magnetic moment.
Definition physlib.h:198
#define physlib_gc_mu(m, p, xi, B)
Evaluate guiding center magnetic moment [J/T] from momentum norm and pitch.
Definition physlib.h:182
const real * plasma_get_species_mass(plasma_data *pls_data)
Get mass of all plasma species.
Definition plasma.c:345
int plasma_get_n_species(plasma_data *pls_data)
Get the number of plasma species.
Definition plasma.c:311
a5err plasma_eval_flow(real *vflow, real rho, real r, real phi, real z, real t, plasma_data *pls_data)
Evalate plasma flow along the field lines.
Definition plasma.c:258
const real * plasma_get_species_charge(plasma_data *pls_data)
Get charge of all plasma species.
Definition plasma.c:379
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:204
Header file for plasma.c.
Header file for random.c.
Magnetic field simulation data.
Definition B_field.h:41
Parameters and data required to evaluate Coulomb collisions.
Definition mccc.h:27
int include_pitch
Definition mccc.h:30
int include_gcdiff
Definition mccc.h:31
int include_energy
Definition mccc.h:29
Struct for storing Wiener processes.
Definition mccc_wiener.h:28
real wiener[MCCC_NDIM *MCCC_NSLOTS]
Definition mccc_wiener.h:35
Struct representing NSIMD guiding center markers.
Definition particle.h:275
integer * running
Definition particle.h:320
real * B_phi_dphi
Definition particle.h:299
Plasma simulation data.
Definition plasma.h:34