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
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 #pragma omp simd
44 for(int i = 0; i < NSIMD; i++) {
45 if(p->running[i]) {
46 a5err errflag = 0;
47
48 /* Initial (R,z) position and magnetic field are needed for later */
49 real Brpz[3] = {p->B_r[i], p->B_phi[i], p->B_z[i]};
50 real Bnorm = math_norm(Brpz);
51 real Bxyz[3];
52 math_vec_rpz2xyz(Brpz, Bxyz, p->phi[i]);
53 real R0 = p->r[i];
54 real z0 = p->z[i];
55
56 /* Move guiding center to (x, y, z, vnorm, xi) coordinates */
57 real vin, pin, vflow, gamma, ppar_flow, xiin, Xin_xyz[3];
58 Xin_xyz[0] = p->r[i] * cos(p->phi[i]);
59 Xin_xyz[1] = p->r[i] * sin(p->phi[i]);
60 Xin_xyz[2] = p->z[i];
61 if(!errflag) {
62 errflag = plasma_eval_flow(
63 &vflow, p->rho[i], p->r[i], p->phi[i], p->z[i], p->time[i],
64 pdata);
65 }
66 gamma = physlib_gamma_ppar(p->mass[i], p->mu[i], p->ppar[i], Bnorm);
67 ppar_flow = p->ppar[i] - gamma * vflow * p->mass[i];
68 pin = physlib_gc_p(p->mass[i], p->mu[i], ppar_flow, Bnorm);
69 xiin = physlib_gc_xi(p->mass[i], p->mu[i], ppar_flow, Bnorm);
70 vin = physlib_vnorm_pnorm(p->mass[i], pin);
71
72 /* Evaluate plasma density and temperature */
74 if(!errflag) {
75 errflag = plasma_eval_densandtemp(nb, Tb, p->rho[i],
76 p->r[i], p->phi[i], p->z[i],
77 p->time[i], pdata);
78 }
79
80 /* Coulomb logarithm */
81 real clogab[MAX_SPECIES];
82 mccc_coefs_clog(clogab, p->mass[i], p->charge[i], vin,
83 n_species, mb, qb, nb, Tb);
84
85 /* Evaluate collision coefficients and sum them for each *
86 * species */
87 real gyrofreq = phys_gyrofreq_pnorm(p->mass[i], p->charge[i], pin,
88 Bnorm);
89 real K = 0, Dpara = 0, dDpara = 0, dQ = 0, nu = 0, DX = 0;
90 for(int j = 0; j < n_species; j++) {
91 real vb = sqrt( 2 * Tb[j] / mb[j] );
92 real x = vin / vb;
93 real mufun[3];
94 mccc_coefs_mufun(mufun, x, mdata);
95
96 real Qb = mccc_coefs_Q(p->mass[i], p->charge[i], mb[j],
97 qb[j], nb[j], vb, clogab[j],
98 mufun[0]);
99 real Dparab = mccc_coefs_Dpara(p->mass[i], p->charge[i], vin,
100 qb[j], nb[j], vb, clogab[j],
101 mufun[0]);
102 real Dperpb = mccc_coefs_Dperp(p->mass[i], p->charge[i], vin,
103 qb[j], nb[j], vb, clogab[j],
104 mufun[1]);
105 real dDparab = mccc_coefs_dDpara(p->mass[i], p->charge[i], vin,
106 qb[j], nb[j], vb, clogab[j],
107 mufun[0], mufun[2]);
108
109 K += mccc_coefs_K(vin, Dparab, dDparab, Qb);
110 dQ += mccc_coefs_dQ(p->mass[i], p->charge[i], mb[j],
111 qb[j], nb[j], vb, clogab[j], mufun[2]);
112 Dpara += Dparab;
113 dDpara += dDparab;
114 nu += mccc_coefs_nu(vin, Dperpb);
115 DX += mccc_coefs_DX(xiin, Dparab, Dperpb, gyrofreq);
116 }
117
118 /* Generate Wiener process for this step */
119 int tindex;
120 if(!errflag) {
121 errflag = mccc_wiener_generate(&w[i], w[i].time[0]+hin[i],
122 &tindex, &rnd[i*5]);
123 }
124 real dW[5] = {0, 0, 0, 0, 0};
125 if(!errflag) {
126 dW[0] = w[i].wiener[tindex*5 + 0] - w[i].wiener[0]; // For X_1
127 dW[1] = w[i].wiener[tindex*5 + 1] - w[i].wiener[1]; // For X_2
128 dW[2] = w[i].wiener[tindex*5 + 2] - w[i].wiener[2]; // For X_3
129 dW[3] = w[i].wiener[tindex*5 + 3] - w[i].wiener[3]; // For v
130 dW[4] = w[i].wiener[tindex*5 + 4] - w[i].wiener[4]; // For xi
131 }
132
133 /* Evaluate collisions */
134
135 real bhat[3];
136 math_unit(Bxyz,bhat);
137
138 real k1 = sqrt(2*DX);
139 real k2 = math_dot(bhat, dW);
140
141 real vout, xiout, Xout_xyz[3];
142 Xout_xyz[0] = Xin_xyz[0] + k1 * ( dW[0] - k2 * bhat[0] );
143 Xout_xyz[1] = Xin_xyz[1] + k1 * ( dW[1] - k2 * bhat[1] );
144 Xout_xyz[2] = Xin_xyz[2] + k1 * ( dW[2] - k2 * bhat[2] );
145 vout = vin + K*hin[i] + sqrt( 2 * Dpara ) * dW[3]
146 + 0.5 * dDpara * ( dW[3]*dW[3] - hin[i] );
147 xiout = xiin - xiin*nu*hin[i] + sqrt( ( 1 - xiin*xiin ) * nu )*dW[4]
148 - 0.5 * xiin * nu * ( dW[4]*dW[4] - hin[i] );
149
150 /* Enforce boundary conditions */
151 real cutoff = MCCC_CUTOFF * sqrt( Tb[0] / p->mass[i] );
152 if(vout < cutoff){
153 vout = 2 * cutoff - vout;
154 }
155
156 if(fabs(xiout) > 1){
157 xiout = ( (xiout > 0) - (xiout < 0) )
158 * ( 2 - fabs( xiout ) );
159 }
160
161 /* Compute error estimates for drift and diffusion limits */
162
163 // xi is limited to interval [-1, 1] but for v we need some value
164 // to translate relative error to absolute error.
165 real v0 = ( vin + fabs(K) * hin[i] + sqrt( 2*Dpara*hin[i] ) )
166 + DBL_EPSILON;
167 real verr = fabs( K*dQ ) / (2*tol*v0);
168 real xierr = fabs( xiin*nu*nu ) / (2*tol);
169
170 // kappa_k is error due to drift
171 real kappa_k;
172 if(verr > xierr){
173 kappa_k = verr*hin[i]*hin[i];
174 }
175 else{
176 kappa_k = xierr*hin[i]*hin[i];
177 }
178
179 // kappa_d is error due to diffusion (v and xi are both needed)
180 real kappa_d0 = fabs( dW[3]*dW[3]*dW[3]
181 * dDpara*dDpara / sqrt( Dpara ) ) / (6*tol*v0);
182 real kappa_d1 = sqrt( 1 - xiin*xiin ) * nu * sqrt( nu )
183 * fabs( dW[4] + sqrt( hin[i]/3 ) ) * hin[i] / (2*tol);
184
185 /* Remove energy or pitch change or spatial diffusion from the *
186 * results if that is requested */
187 if(!mdata->include_energy) {
188 vout = vin;
189 }
190 if(!mdata->include_pitch) {
191 xiout = xiin;
192 }
193 if(!mdata->include_gcdiff) {
194 Xout_xyz[0] = Xin_xyz[0];
195 Xout_xyz[1] = Xin_xyz[1];
196 Xout_xyz[2] = Xin_xyz[2];
197 }
198 real pout = physlib_pnorm_vnorm(p->mass[i], vout);
199
200 /* Back to cylindrical coordinates */
201 real Xout_rpz[3];
202 math_xyz2rpz(Xout_xyz, Xout_rpz);
203
204 /* Evaluate magnetic field (and gradient) and rho at new position */
205 real B_dB[15], psi[1], rho[2];
206 if(!errflag) {
207 errflag = B_field_eval_B_dB(B_dB, Xout_rpz[0], Xout_rpz[1],
208 Xout_rpz[2], p->time[i] + hin[i],
209 Bdata);
210 }
211 if(!errflag) {
212 errflag = B_field_eval_psi(psi, Xout_rpz[0], Xout_rpz[1],
213 Xout_rpz[2], p->time[i] + hin[i],
214 Bdata);
215 }
216 if(!errflag) {
217 errflag = B_field_eval_rho(rho, psi[0], Bdata);
218 }
219
220 if(!errflag) {
221 /* Update marker coordinates at the new position */
222 p->B_r[i] = B_dB[0];
223 p->B_r_dr[i] = B_dB[1];
224 p->B_r_dphi[i] = B_dB[2];
225 p->B_r_dz[i] = B_dB[3];
226
227 p->B_phi[i] = B_dB[4];
228 p->B_phi_dr[i] = B_dB[5];
229 p->B_phi_dphi[i] = B_dB[6];
230 p->B_phi_dz[i] = B_dB[7];
231
232 p->B_z[i] = B_dB[8];
233 p->B_z_dr[i] = B_dB[9];
234 p->B_z_dphi[i] = B_dB[10];
235 p->B_z_dz[i] = B_dB[11];
236
237 p->rho[i] = rho[0];
238
239 Bnorm = math_normc(B_dB[0], B_dB[4], B_dB[8]);
240
241 p->r[i] = Xout_rpz[0];
242 p->z[i] = Xout_rpz[2];
243
244 /*Since we use xiout (in "flow frame") here, we will get the mu
245 in the "flow frame". If we assume that the flow frame has the
246 same perpendicular velocity, mu remains unchanged under the
247 co-ordinate transformation and thus this mu is then the same as
248 the mu in the lab frame.*/
249 p->mu[i] = physlib_gc_mu(p->mass[i], pout, xiout, Bnorm);
250 gamma = physlib_gamma_pnorm(p->mass[i], pout);
251 /* difference in ppar between the lab frame and "flow frame" */
252 real dppar = gamma * p->mass[i] * vflow;
253
254 /* p->ppar is in the lab frame; xiout and pout are in the
255 "flow frame" */
256 p->ppar[i] = physlib_gc_ppar(pout, xiout) + dppar;
257
258 /* Evaluate phi and theta angles so that they are cumulative */
259 real axisrz[2];
260 errflag = B_field_get_axis_rz(axisrz, Bdata, p->phi[i]);
261 p->theta[i] += atan2( (R0-axisrz[0]) * (p->z[i]-axisrz[1])
262 - (z0-axisrz[1]) * (p->r[i]-axisrz[0]),
263 (R0-axisrz[0]) * (p->r[i]-axisrz[0])
264 + (z0-axisrz[1]) * (p->z[i]-axisrz[1]) );
265 p->phi[i] += atan2( Xin_xyz[0] * Xout_xyz[1]
266 - Xin_xyz[1] * Xout_xyz[0],
267 Xin_xyz[0] * Xout_xyz[0]
268 + Xin_xyz[1] * Xout_xyz[1] );
269 }
270
271 /* Check whether timestep was rejected and suggest next time step */
272
273 if( kappa_k >= kappa_d0 && kappa_k >= kappa_d1 ) {
274 /* Drift error dominates */
275 hout[i] = 0.8 * hin[i] / sqrt( kappa_k );
276 }
277 else if( kappa_d0 >= kappa_k && kappa_d0 >= kappa_d1 ) {
278 /* Velocity diffusion error dominates */
279 hout[i] = 0.9 * hin[i] * pow( kappa_d0, -2.0/3.0 );
280 }
281 else {
282 /* Pitch diffusion error dominates */
283 hout[i] = 0.9 * hin[i] * pow( kappa_d1, -2.0/3.0 );
284 }
285
286 /* Negative value indicates time step was rejected*/
287 if( kappa_k > 1 || kappa_d0 > 1 || kappa_d1 > 1 ){
288 hout[i] = -hout[i];
289 }
290 else if(hout[i] > 1.5*hin[i]) {
291 /* Make sure we don't increase time step too much */
292 hout[i] = 1.5*hin[i];
293 }
294
295 /* Error handling */
296 if(errflag) {
297 p->err[i] = errflag;
298 p->running[i] = 0;
299 }
300 }
301 }
302}
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 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
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:31
#define math_unit(a, b)
Calculate unit vector b from a 3D vector a.
Definition math.h:73
#define math_xyz2rpz(xyz, rpz)
Convert cartesian coordinates xyz to cylindrical coordinates rpz.
Definition math.h:77
#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:86
#define math_normc(a1, a2, a3)
Calculate norm of 3D vector from its components a1, a2, a3.
Definition math.h:70
#define math_norm(a)
Calculate norm of 3D vector a.
Definition math.h:67
Header file for mccc package.
#define MCCC_CUTOFF
Defines minimum energy boundary condition.
Definition mccc.h:22
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
static void mccc_coefs_mufun(real mufun[3], real x, mccc_data *mdata)
Evaluate special functions needed by collision coefficients.
Definition mccc_coefs.h:275
#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
static DECLARE_TARGET_END void mccc_coefs_clog(real *clogab, real ma, real qa, real va, int nspec, const real *mb, const real *qb, const real *nb, const real *Tb)
Evaluate Coulomb logarithm.
Definition mccc_coefs.h:228
#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
void mccc_gc_milstein(particle_simd_gc *p, real *hin, 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.
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:66
header file for mccc_wiener.c
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_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_gamma_ppar(m, mu, ppar, B)
Evaluate Lorentz factor from parallel momentum.
Definition physlib.h:77
#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:314
int plasma_get_n_species(plasma_data *pls_data)
Get the number of plasma species.
Definition plasma.c:284
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:236
const real * plasma_get_species_charge(plasma_data *pls_data)
Get charge of all plasma species.
Definition plasma.c:344
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
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
Plasma simulation data.
Definition plasma.h:32