PLUTO
glm.c
Go to the documentation of this file.
1 /* ///////////////////////////////////////////////////////////////////// */
2 /*!
3  \file
4  \brief GLM module implementation.
5 
6  Contains functions for the GLM module.
7 
8  \authors A. Mignone (mignone@ph.unito.it)\n
9  P. Tzeferacos (petros.tzeferacos@ph.unito.it)
10  \date Aug 16, 2012
11 
12  \b Reference
13  - "Hyperbolic Divergence Cleaning for the MHD equations"
14  Dedner et al., JCP (2002) 175, 645
15  - "A second-order unsplit Godunov scheme for cell-centered MHD:
16  the CTU-GLM scheme" Mignone \& Tzeferacos, JCP (2010) 229, 2117
17 */
18 /* ///////////////////////////////////////////////////////////////////// */
19 #include "pluto.h"
20 
21 double glm_ch = -1.0;
22 
23 /* ********************************************************************* */
24 void GLM_Solve (const State_1D *state, double **VL, double **VR,
25  int beg, int end, Grid *grid)
26 /*!
27  * Solve the 2x2 linear hyperbolic GLM-MHD system given by the divergence
28  * cleaning approach.
29  * Build new states VL and VR for Riemann problem.
30  * We use Eq. (42) of Dedner et al (2002)
31  *
32  * \param [in,out] state pointer to a State_1D structure
33  * \param [out] VL left-interface state to be passed to the
34  * Riemann solver
35  * \param [out] VR right-interface state to be passed to the
36  * Riemann solver
37  * \param [in] beg starting index of computation
38  * \param [in] end final index of computation
39  * \param [in] grid pointer to array of Grid structures
40  *
41  * The purpose of this function is two-fold:
42  *
43  * 1. assign a unique value to the normal component of magnetic field
44  * and to te scalar psi \e before the actual Riemann solver is called.
45  * 2. compute GLM fluxes in Bn and psi.
46  *
47  * The following MAPLE script has been used
48  * \code
49  * restart;
50  * with(linalg);
51  * A := matrix(2,2,[ 0, 1, c^2, 0]);
52  *
53  * R := matrix(2,2,[ 1, 1, c, -c]);
54  * L := inverse(R);
55  * E := matrix(2,2,[c,0,0,-c]);
56  * multiply(R,multiply(E,L));
57  * \endcode
58  *
59  *********************************************************************** */
60 {
61  int i, nv, nflag=0;
62  double Bm, psim;
63  double dB, dpsi;
64  double **v;
65  static double *wp, *wm, *Bxp, *Bxm;
66 
67  if (wp == NULL){
68  wp = ARRAY_1D(NMAX_POINT, double);
69  wm = ARRAY_1D(NMAX_POINT, double);
70  Bxp = ARRAY_1D(NMAX_POINT, double);
71  Bxm = ARRAY_1D(NMAX_POINT, double);
72  }
73 
74  #ifdef CTU
75  #if PARABOLIC_FLUX != EXPLICIT
76  if (g_intStage == 1) nflag = 1;
77  #endif
78 
79  /* ------------------------------------------------------
80  The nflag = 1 is not necessary but it improves the
81  results when dimensionally unsplit CTU is used.
82  In practice, it redefines the input normal field
83  Bn to be at time level n rather than n+1/2.
84  ------------------------------------------------------- */
85 
86  if (nflag){
87  v = state->v;
88  for (i = beg-1; i <= end+1; i++){
89  dB = v[i+1][BXn] - v[i][BXn];
90  dpsi = v[i+1][PSI_GLM] - v[i][PSI_GLM];
91  wm[i] = 0.5*(dB - dpsi/glm_ch);
92  wp[i] = 0.5*(dB + dpsi/glm_ch);
93  }
94  for (i = beg; i <= end+1; i++){
95  dB = MC(wm[i], wm[i-1]);
96  dB += MC(wp[i], wp[i-1]);
97  Bxp[i] = v[i][BXn] + 0.5*dB;
98  Bxm[i] = v[i][BXn] - 0.5*dB;
99  }
100  }
101  #endif
102 
103 /* -------------------------------------------------
104  Solve the Riemann problem for the 2x2 linear
105  GLM system. Use the solution to assign a single
106  value to both left and right (Bn,psi).
107  ------------------------------------------------- */
108 
109  for (i = beg; i <= end; i++){
110  for (nv = NVAR; nv--; ){
111  VL[i][nv] = state->vL[i][nv];
112  VR[i][nv] = state->vR[i][nv];
113  }
114 
115  #ifdef CTU
116  if (nflag){
117  VL[i][BXn] = Bxp[i];
118  VR[i][BXn] = Bxm[i+1];
119  }
120  #endif
121 
122  dB = VR[i][BXn] - VL[i][BXn];
123  dpsi = VR[i][PSI_GLM] - VL[i][PSI_GLM];
124 
125  Bm = 0.5*(VL[i][BXn] + VR[i][BXn]) - 0.5*dpsi/glm_ch;
126  psim = 0.5*(VL[i][PSI_GLM] + VR[i][PSI_GLM]) - 0.5*glm_ch*dB;
127 
128  state->bn[i] = VL[i][BXn] = VR[i][BXn] = Bm;
129  VL[i][PSI_GLM] = VR[i][PSI_GLM] = psim;
130  }
131 
132  #if COMPUTE_DIVB == YES
133  if (g_intStage == 1) GLM_ComputeDivB(state, grid);
134  #endif
135 
136 }
137 
138 /* ********************************************************************* */
139 void GLM_Source (const Data_Arr Q, double dt, Grid *grid)
140 /*!
141  * Include the parabolic source term of the Lagrangian multiplier
142  * equation in a split fashion for the mixed GLM formulation.
143  * Ref. Mignone & Tzeferacos, JCP (2010) 229, 2117, Equation (27).
144  *
145  *
146  *********************************************************************** */
147 {
148  int i,j,k;
149  double cr, cp, scrh;
150  double dx, dy, dz, dtdx;
151 
152  #ifdef CHOMBO
153  dtdx = dt;
154  #else
155  dx = grid[IDIR].dx[IBEG];
156  dtdx = dt/dx;
157  #endif
158 
159  cp = sqrt(dx*glm_ch/GLM_ALPHA);
160  scrh = dtdx*glm_ch*GLM_ALPHA; /* CFL*g_inputParam[ALPHA]; */
161 
162  scrh = exp(-scrh);
163  DOM_LOOP(k,j,i) Q[PSI_GLM][k][j][i] *= scrh;
164  return;
165 }
166 
167 /* ********************************************************************* */
168 void GLM_ExtendedSource (const State_1D *state, double dt,
169  int beg, int end, Grid *grid)
170 /*!
171  * Add source terms to the right hand side of the conservative equations,
172  * momentum and energy equations only.
173  * This yields the extended GLM equations given by Eq. (24a)--(24c) in
174  *
175  * "Hyperbolic Divergence cleaning for the MHD Equations"
176  * Dedner et al. (2002), JcP, 175, 645
177  *
178  *********************************************************************** */
179 {
180  int i;
181  double bx, by, bz;
182  double ch2, r, s;
183  double *Ar, *Ath, **bgf;
184  double *rhs, *v, *Bm, *pm;
185  static double *divB, *dpsi, *Bp, *pp;
186  Grid *GG;
187 /*
188  #if PHYSICS == RMHD
189  print1 ("! EGLM not working for the RMHD module\n");
190  QUIT_PLUTO(1);
191  #endif
192 */
193  if (divB == NULL){
194  divB = ARRAY_1D(NMAX_POINT, double);
195  dpsi = ARRAY_1D(NMAX_POINT, double);
196  Bp = ARRAY_1D(NMAX_POINT, double);
197  pp = ARRAY_1D(NMAX_POINT, double);
198  }
199 
200 /* ----------------------- ---------------------
201  compute magnetic field normal component
202  interface value by arithmetic averaging
203  -------------------------------------------- */
204 
205  ch2 = glm_ch*glm_ch;
206  for (i = beg - 1; i <= end; i++) {
207  Bp[i] = state->flux[i][PSI_GLM]/ch2;
208  pp[i] = state->flux[i][BXn];
209  }
210  Bm = Bp - 1;
211  pm = pp - 1;
212  GG = grid + g_dir;
213 
214 /* --------------------------------------------
215  Compute div.B contribution from the normal
216  direction (1) in different geometries
217  -------------------------------------------- */
218 
219  #if GEOMETRY == CARTESIAN
220 
221  for (i = beg; i <= end; i++) {
222  divB[i] = (Bp[i] - Bm[i])/GG->dx[i];
223  dpsi[i] = (pp[i] - pm[i])/GG->dx[i];
224  }
225 
226  #elif GEOMETRY == CYLINDRICAL
227 
228  if (g_dir == IDIR){ /* -- r -- */
229  Ar = grid[IDIR].A;
230  for (i = beg; i <= end; i++) {
231  divB[i] = (Bp[i]*Ar[i] - Bm[i]*Ar[i - 1])/GG->dV[i];
232  dpsi[i] = (pp[i] - pm[i])/GG->dx[i];
233  }
234  }else if (g_dir == JDIR){ /* -- z -- */
235  for (i = beg; i <= end; i++) {
236  divB[i] = (Bp[i] - Bm[i])/GG->dx[i];
237  dpsi[i] = (pp[i] - pm[i])/GG->dx[i];
238  }
239  }
240 
241  #elif GEOMETRY == POLAR
242 
243  if (g_dir == IDIR){ /* -- r -- */
244  Ar = grid[IDIR].A;
245  for (i = beg; i <= end; i++) {
246  divB[i] = (Bp[i]*Ar[i] - Bm[i]*Ar[i - 1])/GG->dV[i];
247  dpsi[i] = (pp[i] - pm[i])/GG->dx[i];
248  }
249  }else if (g_dir == JDIR){ /* -- phi -- */
250  r = grid[IDIR].x[g_i];
251  for (i = beg; i <= end; i++) {
252  divB[i] = (Bp[i] - Bm[i])/(r*GG->dx[i]);
253  dpsi[i] = (pp[i] - pm[i])/(r*GG->dx[i]);
254  }
255  }else if (g_dir == KDIR){ /* -- z -- */
256  for (i = beg; i <= end; i++) {
257  divB[i] = (Bp[i] - Bm[i])/GG->dx[i];
258  dpsi[i] = (pp[i] - pm[i])/GG->dx[i];
259  }
260  }
261 
262  #elif GEOMETRY == SPHERICAL
263 
264  if (g_dir == IDIR){ /* -- r -- */
265  Ar = grid[IDIR].A;
266  for (i = beg; i <= end; i++) {
267  divB[i] = (Bp[i]*Ar[i] - Bm[i]*Ar[i - 1])/GG->dV[i];
268  dpsi[i] = (pp[i] - pm[i])/GG->dx[i];
269  }
270  }else if (g_dir == JDIR){ /* -- theta -- */
271  Ath = grid[JDIR].A;
272  r = grid[IDIR].x[g_i];
273  for (i = beg; i <= end; i++) {
274  divB[i] = (Bp[i]*Ath[i] - Bm[i]*Ath[i - 1]) /
275  (r*GG->dV[i]);
276  dpsi[i] = (pp[i] - pm[i])/(r*GG->dx[i]);
277 
278  }
279  }else if (g_dir == KDIR){ /* -- phi -- */
280  r = grid[IDIR].x[g_i];
281  s = sin(grid[JDIR].x[g_j]);
282  for (i = beg; i <= end; i++) {
283  divB[i] = (Bp[i] - Bm[i])/(r*s*GG->dx[i]);
284  dpsi[i] = (pp[i] - pm[i])/(r*s*GG->dx[i]);
285  }
286  }
287 
288  #endif
289 
290  #if BACKGROUND_FIELD == YES
291  bgf = GetBackgroundField (beg - 1, end, CELL_CENTER, grid);
292  #endif
293 
294 /* ---------------------
295  Add source terms
296  -------------------- */
297 
298  for (i = beg; i <= end; i++) {
299 
300  v = state->vh[i];
301  rhs = state->rhs[i];
302 
303  EXPAND(bx = v[BX1]; ,
304  by = v[BX2]; ,
305  bz = v[BX3];)
306 
307  #if BACKGROUND_FIELD == YES
308  EXPAND(bx += bgf[i][BX1]; ,
309  by += bgf[i][BX2]; ,
310  bz += bgf[i][BX3];)
311  #endif
312 
313  EXPAND (rhs[MX1] -= dt*divB[i]*bx; ,
314  rhs[MX2] -= dt*divB[i]*by; ,
315  rhs[MX3] -= dt*divB[i]*bz;)
316 
317  #if HAVE_ENERGY
318  rhs[ENG] -= dt*v[BXn]*dpsi[i];
319  #endif
320  }
321 }
322 
323 /* ********************************************************************* */
324 void GLM_Init (const Data *d, const Time_Step *Dts, Grid *grid)
325 /*!
326  * Initialize the maximum propagation speed ::glm_ch.
327  *
328  *
329  *********************************************************************** */
330 {
331  int i,j,k,nv;
332  double dxmin, gmaxc;
333 
334  #if PHYSICS == MHD
335  if (glm_ch < 0.0){
336  double *x1, *x2, *x3;
337  static double **v, **lambda, *cs2;
338  Index indx;
339 
340  if (v == NULL) {
341  v = ARRAY_2D(NMAX_POINT, NVAR, double);
342  lambda = ARRAY_2D(NMAX_POINT, NVAR, double);
343  cs2 = ARRAY_1D(NMAX_POINT, double);
344  }
345  x1 = grid[IDIR].x;
346  x2 = grid[JDIR].x;
347  x3 = grid[KDIR].x;
348 
349  glm_ch = 0.0;
350 
351  /* -- X1 - g_dir -- */
352 
353  g_dir = IDIR; SetIndexes (&indx, grid);
354  KDOM_LOOP(k) JDOM_LOOP(j){
355  g_j = j; g_k = k;
356  IDOM_LOOP(i) for (nv = NVAR; nv--; ) v[i][nv] = d->Vc[nv][k][j][i];
357  SoundSpeed2 (v, cs2, NULL, IBEG, IEND, CELL_CENTER, grid);
358  Eigenvalues (v, cs2, lambda, IBEG, IEND);
359  IDOM_LOOP(i){
360  glm_ch = MAX(glm_ch, fabs(lambda[i][KFASTP]));
361  glm_ch = MAX(glm_ch, fabs(lambda[i][KFASTM]));
362  }
363  }
364 
365  /* -- X2 - g_dir -- */
366 
367  #if DIMENSIONS >= 2
368  g_dir = JDIR; SetIndexes (&indx, grid);
369  KDOM_LOOP(k) IDOM_LOOP(i){
370  g_i = i; g_k = k;
371  JDOM_LOOP(j) for (nv = NVAR; nv--; ) v[j][nv] = d->Vc[nv][k][j][i];
372  SoundSpeed2 (v, cs2, NULL, JBEG, JEND, CELL_CENTER, grid);
373  Eigenvalues (v, cs2, lambda, JBEG, JEND);
374  JDOM_LOOP(j){
375  glm_ch = MAX(glm_ch, fabs(lambda[j][KFASTP]));
376  glm_ch = MAX(glm_ch, fabs(lambda[j][KFASTM]));
377  }
378  }
379  #endif
380 
381  /* -- X3 - g_dir -- */
382 
383  #if DIMENSIONS == 3
384  g_dir = KDIR; SetIndexes (&indx, grid);
385  JDOM_LOOP(j) IDOM_LOOP(i){
386  g_i = i; g_j = j;
387  KDOM_LOOP(k) for (nv = NVAR; nv--; ) v[k][nv] = d->Vc[nv][k][j][i];
388  SoundSpeed2 (v, cs2, NULL, KBEG, KEND, CELL_CENTER, grid);
389  Eigenvalues (v, cs2, lambda, KBEG, KEND);
390  KDOM_LOOP(k){
391  glm_ch = MAX(glm_ch, fabs(lambda[k][KFASTP]));
392  glm_ch = MAX(glm_ch, fabs(lambda[k][KFASTM]));
393  }
394  }
395  #endif
396 
397  }
398  #elif PHYSICS == RMHD
399  if (glm_ch < 0.0) glm_ch = 1.0;
400  #endif
401 
402  #ifdef PARALLEL
403  MPI_Allreduce (&glm_ch, &gmaxc, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
404  glm_ch = gmaxc;
405  #endif
406 }
407 
408 #if COMPUTE_DIVB == YES
409 static double ***divB;
410 /* ********************************************************* */
411 void GLM_ComputeDivB(const State_1D *state, Grid *grid)
412 /*
413  *
414  *
415  *
416  *
417  *********************************************************** */
418 {
419  int i,j,k;
420  static int old_nstep = -1;
421  double *B, *A, *dV;
422 
423  if (divB == NULL) divB = ARRAY_3D(NX3_TOT, NX2_TOT, NX1_TOT, double);
424 
425  if (old_nstep != g_stepNumber){
426  old_nstep = g_stepNumber;
427  DOM_LOOP(k,j,i) divB[k][j][i] = 0.0;
428  }
429 
430  B = state->bn;
431  A = grid[g_dir].A;
432  dV = grid[g_dir].dV;
433  if (g_dir == IDIR){
434  k = g_k; j = g_j;
435  IDOM_LOOP(i) divB[k][j][i] += (A[i]*B[i] - A[i-1]*B[i-1])/dV[i];
436  }else if (g_dir == JDIR){
437  k = g_k; i = g_i;
438  JDOM_LOOP(j) divB[k][j][i] += (A[j]*B[j] - A[j-1]*B[j-1])/dV[j];
439  }else if (g_dir == KDIR){
440  j = g_j; i = g_i;
441  KDOM_LOOP(k) divB[k][j][i] += (A[k]*B[k] - A[k-1]*B[k-1])/dV[k];
442  }
443 }
444 
445 double ***GLM_GetDivB(void)
446 {
447  return divB;
448 }
449 #endif
#define MX3
Definition: mod_defs.h:22
#define MAX(a, b)
Definition: macros.h:101
double ** v
Cell-centered primitive varables at the base time level, v[i] = .
Definition: structs.h:134
#define KDOM_LOOP(k)
Definition: macros.h:36
void GLM_Solve(const State_1D *state, double **VL, double **VR, int beg, int end, Grid *grid)
Definition: glm.c:24
#define MX1
Definition: mod_defs.h:20
double ** vh
Primitive state at n+1/2 (only for one step method)
Definition: structs.h:162
double **** Data_Arr
Definition: pluto.h:492
DOM_LOOP(k, j, i)
Definition: analysis.c:22
double ** flux
upwind flux computed with the Riemann solver
Definition: structs.h:149
void Eigenvalues(double **v, double *csound2, double **lambda, int beg, int end)
Definition: eigenv.c:67
void GLM_ExtendedSource(const State_1D *state, double dt, int beg, int end, Grid *grid)
Definition: glm.c:168
double dB
Definition: analysis.c:4
double ** rhs
Conservative right hand side.
Definition: structs.h:163
tuple scrh
Definition: configure.py:200
#define YES
Definition: pluto.h:25
long int NX2_TOT
Total number of zones in the X2 direction (boundaries included) for the local processor.
Definition: globals.h:57
int g_intStage
Gives the current integration stage of the time stepping method (predictor = 0, 1st corrector = 1...
Definition: globals.h:98
double **** Vc
The main four-index data array used for cell-centered primitive variables.
Definition: structs.h:31
#define PSI_GLM
Definition: mod_defs.h:34
double * dV
Cell volume.
Definition: structs.h:86
double ** vR
Primitive variables to the right of the interface, .
Definition: structs.h:139
#define ARRAY_3D(nx, ny, nz, type)
Definition: prototypes.h:172
double * dx
Definition: structs.h:83
double ** GetBackgroundField(int beg, int end, int where, Grid *grid)
Definition: bckgrnd_field.c:5
#define KDIR
Definition: pluto.h:195
void SoundSpeed2(double **v, double *cs2, double *h, int beg, int end, int pos, Grid *grid)
Definition: eos.c:16
#define JDOM_LOOP(j)
Definition: macros.h:35
int BXn
Definition: globals.h:75
int g_i
x1 grid index when sweeping along the x2 or x3 direction.
Definition: globals.h:82
#define CELL_CENTER
Definition: pluto.h:205
long int g_stepNumber
Gives the current integration step number.
Definition: globals.h:97
#define IDIR
Definition: pluto.h:193
int g_dir
Specifies the current sweep or direction of integration.
Definition: globals.h:86
int g_j
x2 grid index when sweeping along the x1 or x3 direction.
Definition: globals.h:83
Definition: structs.h:78
long int NX3_TOT
Total number of zones in the X3 direction (boundaries included) for the local processor.
Definition: globals.h:59
int j
Definition: analysis.c:2
#define MX2
Definition: mod_defs.h:21
long int IEND
Upper grid index of the computational domain in the the X1 direction for the local processor...
Definition: globals.h:37
int k
Definition: analysis.c:2
void SetIndexes(Index *indx, Grid *grid)
Definition: set_indexes.c:49
double * x
Definition: structs.h:80
int g_k
x3 grid index when sweeping along the x1 or x2 direction.
Definition: globals.h:84
#define s
#define BX3
Definition: mod_defs.h:27
#define MC(a, b)
Definition: macros.h:114
PLUTO main header file.
#define ARRAY_1D(nx, type)
Definition: prototypes.h:170
static double *** divB
Definition: glm.c:409
Definition: structs.h:30
long int NMAX_POINT
Maximum number of points among the three directions, boundaries excluded.
Definition: globals.h:62
double *** GLM_GetDivB(void)
Definition: glm.c:445
int i
Definition: analysis.c:2
double * bn
Face magentic field, bn = bx(i+1/2)
Definition: structs.h:165
Definition: structs.h:317
void BACKGROUND_FIELD(real x1, real x2, real x3, real *B0)
Definition: init.c:95
#define BX1
Definition: mod_defs.h:25
double ** vL
Primitive variables to the left of the interface, .
Definition: structs.h:136
long int KBEG
Lower grid index of the computational domain in the the X3 direction for the local processor...
Definition: globals.h:43
#define ARRAY_2D(nx, ny, type)
Definition: prototypes.h:171
#define BX2
Definition: mod_defs.h:26
long int KEND
Upper grid index of the computational domain in the the X3 direction for the local processor...
Definition: globals.h:45
double glm_ch
The propagation speed of divergence error.
Definition: glm.c:21
#define JDIR
Definition: pluto.h:194
long int JBEG
Lower grid index of the computational domain in the the X2 direction for the local processor...
Definition: globals.h:39
#define NVAR
Definition: pluto.h:609
#define GLM_ALPHA
Sets the damping rate of monopoles.
Definition: glm.h:28
#define IDOM_LOOP(i)
Definition: macros.h:34
void GLM_Source(const Data_Arr Q, double dt, Grid *grid)
Definition: glm.c:139
void GLM_Init(const Data *d, const Time_Step *Dts, Grid *grid)
Definition: glm.c:324
long int JEND
Upper grid index of the computational domain in the the X2 direction for the local processor...
Definition: globals.h:41
void GLM_ComputeDivB(const State_1D *state, Grid *grid)
Definition: glm.c:411
long int NX1_TOT
Total number of zones in the X1 direction (boundaries included) for the local processor.
Definition: globals.h:55
long int IBEG
Lower grid index of the computational domain in the the X1 direction for the local processor...
Definition: globals.h:35
double * A
Right interface area, A[i] = .
Definition: structs.h:87
#define HAVE_ENERGY
Definition: pluto.h:395