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, xiin, Xin_xyz[3];
58 pin = physlib_gc_p( p->mass[i], p->mu[i], p->ppar[i], Bnorm);
59 xiin = physlib_gc_xi(p->mass[i], p->mu[i], p->ppar[i], Bnorm);
60 vin = physlib_vnorm_pnorm(p->mass[i], pin);
61 Xin_xyz[0] = p->r[i] * cos(p->phi[i]);
62 Xin_xyz[1] = p->r[i] * sin(p->phi[i]);
63 Xin_xyz[2] = p->z[i];
64
65 /* Evaluate plasma density and temperature */
67 if(!errflag) {
68 errflag = plasma_eval_densandtemp(nb, Tb, p->rho[i],
69 p->r[i], p->phi[i], p->z[i],
70 p->time[i], pdata);
71 }
72
73 /* Coulomb logarithm */
74 real clogab[MAX_SPECIES];
75 mccc_coefs_clog(clogab, p->mass[i], p->charge[i], vin,
76 n_species, mb, qb, nb, Tb);
77
78 /* Evaluate collision coefficients and sum them for each *
79 * species */
80 real gyrofreq = phys_gyrofreq_pnorm(p->mass[i], p->charge[i], pin,
81 Bnorm);
82 real K = 0, Dpara = 0, dDpara = 0, dQ = 0, nu = 0, DX = 0;
83 for(int j = 0; j < n_species; j++) {
84 real vb = sqrt( 2 * Tb[j] / mb[j] );
85 real x = vin / vb;
86 real mufun[3];
87 mccc_coefs_mufun(mufun, x, mdata);
88
89 real Qb = mccc_coefs_Q(p->mass[i], p->charge[i], mb[j],
90 qb[j], nb[j], vb, clogab[j],
91 mufun[0]);
92 real Dparab = mccc_coefs_Dpara(p->mass[i], p->charge[i], vin,
93 qb[j], nb[j], vb, clogab[j],
94 mufun[0]);
95 real Dperpb = mccc_coefs_Dperp(p->mass[i], p->charge[i], vin,
96 qb[j], nb[j], vb, clogab[j],
97 mufun[1]);
98 real dDparab = mccc_coefs_dDpara(p->mass[i], p->charge[i], vin,
99 qb[j], nb[j], vb, clogab[j],
100 mufun[0], mufun[2]);
101
102 K += mccc_coefs_K(vin, Dparab, dDparab, Qb);
103 dQ += mccc_coefs_dQ(p->mass[i], p->charge[i], mb[j],
104 qb[j], nb[j], vb, clogab[j], mufun[2]);
105 Dpara += Dparab;
106 dDpara += dDparab;
107 nu += mccc_coefs_nu(vin, Dperpb);
108 DX += mccc_coefs_DX(xiin, Dparab, Dperpb, gyrofreq);
109 }
110
111 /* Generate Wiener process for this step */
112 int tindex;
113 if(!errflag) {
114 errflag = mccc_wiener_generate(&w[i], w[i].time[0]+hin[i],
115 &tindex, &rnd[i*5]);
116 }
117 real dW[5] = {0, 0, 0, 0, 0};
118 if(!errflag) {
119 dW[0] = w[i].wiener[tindex*5 + 0] - w[i].wiener[0]; // For X_1
120 dW[1] = w[i].wiener[tindex*5 + 1] - w[i].wiener[1]; // For X_2
121 dW[2] = w[i].wiener[tindex*5 + 2] - w[i].wiener[2]; // For X_3
122 dW[3] = w[i].wiener[tindex*5 + 3] - w[i].wiener[3]; // For v
123 dW[4] = w[i].wiener[tindex*5 + 4] - w[i].wiener[4]; // For xi
124 }
125
126 /* Evaluate collisions */
127
128 real bhat[3];
129 math_unit(Bxyz,bhat);
130
131 real k1 = sqrt(2*DX);
132 real k2 = math_dot(bhat, dW);
133
134 real vout, xiout, Xout_xyz[3];
135 Xout_xyz[0] = Xin_xyz[0] + k1 * ( dW[0] - k2 * bhat[0] );
136 Xout_xyz[1] = Xin_xyz[1] + k1 * ( dW[1] - k2 * bhat[1] );
137 Xout_xyz[2] = Xin_xyz[2] + k1 * ( dW[2] - k2 * bhat[2] );
138 vout = vin + K*hin[i] + sqrt( 2 * Dpara ) * dW[3]
139 + 0.5 * dDpara * ( dW[3]*dW[3] - hin[i] );
140 xiout = xiin - xiin*nu*hin[i] + sqrt( ( 1 - xiin*xiin ) * nu )*dW[4]
141 - 0.5 * xiin * nu * ( dW[4]*dW[4] - hin[i] );
142
143 /* Enforce boundary conditions */
144 real cutoff = MCCC_CUTOFF * sqrt( Tb[0] / p->mass[i] );
145 if(vout < cutoff){
146 vout = 2 * cutoff - vout;
147 }
148
149 if(fabs(xiout) > 1){
150 xiout = ( (xiout > 0) - (xiout < 0) )
151 * ( 2 - fabs( xiout ) );
152 }
153
154 /* Compute error estimates for drift and diffusion limits */
155
156 // xi is limited to interval [-1, 1] but for v we need some value
157 // to translate relative error to absolute error.
158 real v0 = ( vin + fabs(K) * hin[i] + sqrt( 2*Dpara*hin[i] ) )
159 + DBL_EPSILON;
160 real verr = fabs( K*dQ ) / (2*tol*v0);
161 real xierr = fabs( xiin*nu*nu ) / (2*tol);
162
163 // kappa_k is error due to drift
164 real kappa_k;
165 if(verr > xierr){
166 kappa_k = verr*hin[i]*hin[i];
167 }
168 else{
169 kappa_k = xierr*hin[i]*hin[i];
170 }
171
172 // kappa_d is error due to diffusion (v and xi are both needed)
173 real kappa_d0 = fabs( dW[3]*dW[3]*dW[3]
174 * dDpara*dDpara / sqrt( Dpara ) ) / (6*tol*v0);
175 real kappa_d1 = sqrt( 1 - xiin*xiin ) * nu * sqrt( nu )
176 * fabs( dW[4] + sqrt( hin[i]/3 ) ) * hin[i] / (2*tol);
177
178 /* Remove energy or pitch change or spatial diffusion from the *
179 * results if that is requested */
180 if(!mdata->include_energy) {
181 vout = vin;
182 }
183 if(!mdata->include_pitch) {
184 xiout = xiin;
185 }
186 if(!mdata->include_gcdiff) {
187 Xout_xyz[0] = Xin_xyz[0];
188 Xout_xyz[1] = Xin_xyz[1];
189 Xout_xyz[2] = Xin_xyz[2];
190 }
191 real pout = physlib_pnorm_vnorm(p->mass[i], vout);
192
193 /* Back to cylindrical coordinates */
194 real Xout_rpz[3];
195 math_xyz2rpz(Xout_xyz, Xout_rpz);
196
197 /* Evaluate magnetic field (and gradient) and rho at new position */
198 real B_dB[15], psi[1], rho[2];
199 if(!errflag) {
200 errflag = B_field_eval_B_dB(B_dB, Xout_rpz[0], Xout_rpz[1],
201 Xout_rpz[2], p->time[i] + hin[i],
202 Bdata);
203 }
204 if(!errflag) {
205 errflag = B_field_eval_psi(psi, Xout_rpz[0], Xout_rpz[1],
206 Xout_rpz[2], p->time[i] + hin[i],
207 Bdata);
208 }
209 if(!errflag) {
210 errflag = B_field_eval_rho(rho, psi[0], Bdata);
211 }
212
213 if(!errflag) {
214 /* Update marker coordinates at the new position */
215 p->B_r[i] = B_dB[0];
216 p->B_r_dr[i] = B_dB[1];
217 p->B_r_dphi[i] = B_dB[2];
218 p->B_r_dz[i] = B_dB[3];
219
220 p->B_phi[i] = B_dB[4];
221 p->B_phi_dr[i] = B_dB[5];
222 p->B_phi_dphi[i] = B_dB[6];
223 p->B_phi_dz[i] = B_dB[7];
224
225 p->B_z[i] = B_dB[8];
226 p->B_z_dr[i] = B_dB[9];
227 p->B_z_dphi[i] = B_dB[10];
228 p->B_z_dz[i] = B_dB[11];
229
230 p->rho[i] = rho[0];
231
232 Bnorm = math_normc(B_dB[0], B_dB[4], B_dB[8]);
233
234 p->r[i] = Xout_rpz[0];
235 p->z[i] = Xout_rpz[2];
236 p->ppar[i] = physlib_gc_ppar(pout, xiout);
237 p->mu[i] = physlib_gc_mu(p->mass[i], pout, xiout, Bnorm);
238
239 /* Evaluate phi and theta angles so that they are cumulative */
240 real axisrz[2];
241 errflag = B_field_get_axis_rz(axisrz, Bdata, p->phi[i]);
242 p->theta[i] += atan2( (R0-axisrz[0]) * (p->z[i]-axisrz[1])
243 - (z0-axisrz[1]) * (p->r[i]-axisrz[0]),
244 (R0-axisrz[0]) * (p->r[i]-axisrz[0])
245 + (z0-axisrz[1]) * (p->z[i]-axisrz[1]) );
246 p->phi[i] += atan2( Xin_xyz[0] * Xout_xyz[1]
247 - Xin_xyz[1] * Xout_xyz[0],
248 Xin_xyz[0] * Xout_xyz[0]
249 + Xin_xyz[1] * Xout_xyz[1] );
250 }
251
252 /* Check whether timestep was rejected and suggest next time step */
253
254 if( kappa_k >= kappa_d0 && kappa_k >= kappa_d1 ) {
255 /* Drift error dominates */
256 hout[i] = 0.8 * hin[i] / sqrt( kappa_k );
257 }
258 else if( kappa_d0 >= kappa_k && kappa_d0 >= kappa_d1 ) {
259 /* Velocity diffusion error dominates */
260 hout[i] = 0.9 * hin[i] * pow( kappa_d0, -2.0/3.0 );
261 }
262 else {
263 /* Pitch diffusion error dominates */
264 hout[i] = 0.9 * hin[i] * pow( kappa_d1, -2.0/3.0 );
265 }
266
267 /* Negative value indicates time step was rejected*/
268 if( kappa_k > 1 || kappa_d0 > 1 || kappa_d1 > 1 ){
269 hout[i] = -hout[i];
270 }
271 else if(hout[i] > 1.5*hin[i]) {
272 /* Make sure we don't increase time step too much */
273 hout[i] = 1.5*hin[i];
274 }
275
276 /* Error handling */
277 if(errflag) {
278 p->err[i] = errflag;
279 p->running[i] = 0;
280 }
281 }
282 }
283}
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
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.
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:28
#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_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
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_gc_xi(m, mu, ppar, B)
Evaluate guiding center pitch from parallel momentum and magnetic moment.
Definition physlib.h:210
#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:257
#define physlib_gc_ppar(p, xi)
Evaluate guiding center parallel momentum [kg m/s] from momentum norm and pitch.
Definition physlib.h:166
#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:195
#define physlib_gc_mu(m, p, xi, B)
Evaluate guiding center magnetic moment [J/T] from momentum norm and pitch.
Definition physlib.h:180
const real * plasma_get_species_mass(plasma_data *pls_data)
Get mass of all plasma species.
Definition plasma.c:361
int plasma_get_n_species(plasma_data *pls_data)
Get the number of plasma species.
Definition plasma.c:331
const real * plasma_get_species_charge(plasma_data *pls_data)
Get charge of all plasma species.
Definition plasma.c:391
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
Header file for plasma.c.
Header file for random.c.
Magnetic field simulation data.
Definition B_field.h:63
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:57