• Main Page
  • Classes
  • Files
  • File List
  • File Members

prob/turb.c

Go to the documentation of this file.
00001 #include "copyright.h"
00002 /*============================================================================*/
00003 /*! \file turb.c
00004  *  \brief Problem generator for driven and decaying turbulence.
00005  *
00006  * PURPOSE: Problem generator for driven and decaying turbulence. Only works
00007  *   in 3D with periodic BC.  Arbitrary power spectrum specified using ispect:
00008  *  -  ispect=1: power law - original form
00009  *  -  ispect=2: form from Gammie&Ostriker
00010  *  Driving specified using idrive
00011  *  -  idrive=0: driven turbulence (de=dedt*dt before each time step,
00012  *                                     unless IMPULSIVE_DRIVING enabled)
00013  *  -  idrive=1: decaying turbulence (de=dedt before first time step ONLY;
00014  *                                     this mode uses less memory!)
00015  *
00016  *  HISTORY:
00017  *  - Original ZEUS version (gmc.f) written by J. Stone, 24 Jan 1996
00018  *  - First Athena version written by J. Stone, 9 June 2004
00019  *  - Major rewrite to add MPI and use FFTW by Nicole Lemaster, 28 Sept 2006
00020  *
00021  *  - Last updated May 11, 2007
00022  *
00023  *  REFERENCE: "Dissipation in Compressible MHD Turbulence", by J. Stone,
00024  *    E. Ostriker, & C. Gammie, ApJ 508, L99 (1998)                           */
00025 /*============================================================================*/
00026 
00027 #include <float.h>
00028 #include <math.h>
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include "defs.h"
00033 #include "athena.h"
00034 #include "prototypes.h"
00035 #include "globals.h"
00036 
00037 #ifndef ISOTHERMAL
00038 #error Problem generator only works for isothermal turbulence
00039 #endif /* ISOTHERMAL */
00040 
00041 #ifdef MPI_PARALLEL
00042 #include "mpi.h"
00043 #ifdef DOUBLE_PREC
00044 #define MPI_RL MPI_DOUBLE
00045 #else /* DOUBLE_PREC */
00046 #define MPI_RL MPI_FLOAT
00047 #endif /* DOUBLE_PREC */
00048 #endif /* MPI_PARALLEL */
00049 
00050 /* Uncomment the following define to drive the flow in an impulsive manner
00051    as was done originally.  Restarts for this mode not yet implemented! */
00052 /* #define IMPULSIVE_DRIVING */
00053 
00054 /* KEEP SEMI-COLONS OUT OF THESE PRE-PROCESSOR DIRECTIVES! */
00055 /* FFT indexing Nfast=k, Nmid=j, Nslow=i (opposite to Athena)
00056  * For OFST, i,j,k,nx2,nx3 reference the local grid */
00057 #define OFST(i, j, k) ((k) + nx3*((j) + nx2*(i)))
00058 /* KWVM: magnitude of wavenumber k in units of dkx */
00059 #define KWVM(i, j, k) (sqrt(SQR(KCOMP(i,gis,gnx1))+ \
00060                             SQR(KCOMP(j,gjs,gnx2))+SQR(KCOMP(k,gks,gnx3))))
00061 
00062 /* FFTW - Variables, Plan, etc. */
00063 /* These are made static global variables so that they need not be
00064    allocated AND destroyed with each call to pspect! */
00065 static struct ath_3d_fft_plan *plan;
00066 /* Between calls to generate(), these have unshifted, unnormalized
00067  * velocity perturbations. */
00068 static ath_fft_data *fv1=NULL, *fv2=NULL, *fv3=NULL;
00069 
00070 /* Normalized, shifted velocity perturbations */
00071 static Real ***dv1=NULL, ***dv2=NULL, ***dv3=NULL;
00072 /* Cutoff wavenumbers, G&O spect peak, power law spect exponent, 2 pi/L */
00073 static Real klow,khigh,kpeak,expo,dkx;
00074 /* Energy injection rate, last planned driving time, driving interval.
00075  * If not using impulsive driving, then the time quantities above are for 
00076  * computing a new spectrum, not driving */
00077 static Real dedt,tdrive,dtdrive;
00078 /* Driving properties */
00079 static int ispect,idrive;
00080 /* Number of cells in local grid, number of cells in global grid */
00081 static int nx1,nx2,nx3,gnx1,gnx2,gnx3;
00082 /* Starting and ending indices for global grid */
00083 static int gis,gie,gjs,gje,gks,gke;
00084 /* Seed for random number generator */
00085 long int rseed;
00086 #ifdef MHD
00087 /* beta = isothermal pressure / magnetic pressure
00088  * B0 = sqrt(2.0*Iso_csound2*rhobar/beta) is init magnetic field strength */
00089 static Real beta,B0;
00090 #endif /* MHD */
00091 /* Initial density (will be average density throughout simulation) */
00092 static const Real rhobar = 1.0;
00093 
00094 /* Functions appear in this file in the same order that they appear in the
00095  * prototypes below */
00096 
00097 /* Function prototypes for generating velocity perturbations */
00098 static void pspect(ath_fft_data *ampl);
00099 static void project();
00100 static inline void transform();
00101 static inline void generate();
00102 static void perturb(Grid *pGrid, Real dt);
00103 
00104 /* Function prototypes for initializing and interfacing with Athena */
00105 static void initialize(Grid *pGrid, Domain *pD);
00106 /* void problem(Grid *pGrid, Domain *pD); */
00107 /* void Userwork_in_loop(Grid *pGrid, Domain *pD); */
00108 /* void Userwork_after_loop(Grid *pGrid, Domain *pD); */
00109 /* void problem_write_restart(Grid *pG, Domain *pD, FILE *fp); */
00110 /* void problem_read_restart(Grid *pG, Domain *pD, FILE *fp); */
00111 /* Gasfun_t get_usr_expr(const char *expr); */
00112 
00113 /* Function prototypes for analysis and outputs */
00114 static Real hst_dEk(const Grid *pG, const int i, const int j, const int k);
00115 static Real hst_dEb(const Grid *pG, const int i, const int j, const int k);
00116 
00117 /* Function prototypes for Numerical Recipes functions */
00118 static double ran2(long int *idum);
00119 
00120 /* ========================================================================== */
00121 
00122 /*! \fn static void pspect(ath_fft_data *ampl)
00123  *  \brief computes component of velocity with specific power
00124  *  spectrum in Fourier space determined by ispect
00125  *
00126  *  Velocity power spectrum returned in ampl
00127  *  - klow   = multiple of 2 pi/L for cut-off at low  wavenumbers
00128  *  - khigh  = multiple of 2 pi/L for cut-off at high wavenumbers
00129  *  - expo   = exponent of power law
00130  *  - ispect = integer flag which specifies spectrum
00131  *
00132  *  Note that the fourier amplitudes are stored in an array with no
00133  *  ghost zones
00134  */
00135 static void pspect(ath_fft_data *ampl)
00136 {
00137   int i,j,k;
00138   double q1,q2,q3;
00139 
00140   /* set random amplitudes with gaussian deviation */
00141   for (k=0; k<nx3; k++) {
00142     for (j=0; j<nx2; j++) {
00143       for (i=0; i<nx1; i++) {
00144         q1 = ran2(&rseed);
00145         q2 = ran2(&rseed);
00146         q3 = sqrt(-2.0*log(q1+1.0e-20))*cos(2.0*PI*q2);
00147         q1 = ran2(&rseed);
00148         ampl[OFST(i,j,k)][0] = q3*cos(2.0*PI*q1);
00149         ampl[OFST(i,j,k)][1] = q3*sin(2.0*PI*q1);
00150       }
00151     }
00152   }
00153 
00154   /* set power spectrum
00155    *   ispect=1: power law - original form
00156    *   ispect=2: form from Gammie&Ostriker
00157    */
00158   for (k=0; k<nx3; k++) {
00159     for (j=0; j<nx2; j++) {
00160       for (i=0; i<nx1; i++) {
00161         /* compute k/dkx */
00162         q3 = KWVM(i,j,k);
00163         if ((q3 > klow) && (q3 < khigh)) {
00164           q3 *= dkx; /* multiply by 2 pi/L */
00165           if (ispect == 1) {
00166             /* decreasing power law */
00167             ampl[OFST(i,j,k)][0] /= pow(q3,(expo+2.0)/2.0);
00168             ampl[OFST(i,j,k)][1] /= pow(q3,(expo+2.0)/2.0);
00169           } else if (ispect == 2) {
00170             /* G&O form */
00171             ampl[OFST(i,j,k)][0] *= pow(q3,3.0)*exp(-4.0*q3/kpeak);
00172             ampl[OFST(i,j,k)][1] *= pow(q3,3.0)*exp(-4.0*q3/kpeak);
00173           }
00174         } else {
00175           /* introduce cut-offs at klow and khigh */
00176           ampl[OFST(i,j,k)][0] = 0.0;
00177           ampl[OFST(i,j,k)][1] = 0.0;
00178         }
00179       }
00180     }
00181   }
00182   ampl[0][0] = 0.0;
00183   ampl[0][1] = 0.0;
00184 
00185   return;
00186 }
00187 
00188 /* ========================================================================== */
00189 
00190 /*! \fn static void project()
00191  *  \brief Makes velocity perturbations divergence free
00192  */
00193 static void project()
00194 {
00195   int i,j,k,m,ind;
00196   double kap[3], kapn[3], mag;
00197   ath_fft_data dot;
00198   
00199   /* Project off non-solenoidal component of velocity */
00200   for (k=0; k<nx3; k++) {
00201     kap[2] = sin(2.0*PI*(gks+k)/gnx3);
00202     for (j=0; j<nx2; j++) {
00203       kap[1] = sin(2.0*PI*(gjs+j)/gnx2);
00204       for (i=0; i<nx1; i++) {
00205         if (((gis+i)+(gjs+j)+(gks+k)) != 0) {
00206           kap[0] = sin(2.0*PI*(gis+i)/gnx1);
00207           ind = OFST(i,j,k);
00208 
00209           /* make kapn a unit vector */
00210           mag = sqrt(SQR(kap[0]) + SQR(kap[1]) + SQR(kap[2]));
00211           for (m=0; m<3; m++) kapn[m] = kap[m] / mag;
00212 
00213           /* find fv_0 dot kapn */
00214           dot[0] = fv1[ind][0]*kapn[0]+fv2[ind][0]*kapn[1]+fv3[ind][0]*kapn[2];
00215           dot[1] = fv1[ind][1]*kapn[0]+fv2[ind][1]*kapn[1]+fv3[ind][1]*kapn[2];
00216 
00217           /* fv = fv_0 - (fv_0 dot kapn) * kapn */
00218           fv1[ind][0] -= dot[0]*kapn[0];
00219           fv2[ind][0] -= dot[0]*kapn[1];
00220           fv3[ind][0] -= dot[0]*kapn[2];
00221 
00222           fv1[ind][1] -= dot[1]*kapn[0];
00223           fv2[ind][1] -= dot[1]*kapn[1];
00224           fv3[ind][1] -= dot[1]*kapn[2];
00225         }
00226       }
00227     }
00228   }
00229 
00230   return;
00231 }
00232 
00233 /* ========================================================================== */
00234 
00235 /*! \fn static inline void transform()
00236  *  \brief Generate velocities from fourier transform
00237  */
00238 static inline void transform()
00239 {
00240   /* Transform velocities from k space to physical space */
00241   ath_3d_fft(plan, fv1);
00242   ath_3d_fft(plan, fv2);
00243   ath_3d_fft(plan, fv3);
00244 
00245   /* Should technically renormalize (divide by gnx1*gnx2*gnx3) here, but
00246    * since we're going to renormalize to get the desired energy injection
00247    * rate anyway, there's no point */
00248  
00249   return;
00250 }
00251 
00252 /* ========================================================================== */
00253 
00254 /*! \fn static inline void generate()
00255  *  \brief Generate the velocity perturbations
00256  */
00257 static inline void generate()
00258 {
00259   /* Generate new perturbations following appropriate power spectrum */
00260   pspect(fv1);
00261   pspect(fv2);
00262   pspect(fv3);
00263 
00264   /* Require div V = 0 */
00265   project();
00266 
00267   /* Transform perturbations to real space, but don't normalize until
00268    * just before we apply them in perturb() */
00269   transform();
00270 
00271   return;
00272 }
00273 
00274 /* ========================================================================== */
00275 
00276 /*! \fn static void perturb(Grid *pGrid, Real dt)
00277  *  \brief  Shifts velocities so no net momentum change, normalizes to keep
00278  *  dedt fixed, and then sets velocities
00279  */
00280 static void perturb(Grid *pGrid, Real dt)
00281 {
00282   int i, is=pGrid->is, ie = pGrid->ie;
00283   int j, js=pGrid->js, je = pGrid->je;
00284   int k, ks=pGrid->ks, ke = pGrid->ke;
00285   int ind, mpierr;
00286   Real dvol, aa, b, c, s, de, qa, v1, v2, v3;
00287   Real t0, t0ij, t0i, t1, t1ij, t1i;
00288   Real t2, t2ij, t2i, t3, t3ij, t3i;
00289   Real m[4], gm[4];
00290 
00291   /* Set the velocities in real space */
00292   dvol = 1.0/((Real)(gnx1*gnx2*gnx3));
00293   for (k=ks; k<=ke; k++) {
00294     for (j=js; j<=je; j++) {
00295       for (i=is; i<=ie; i++) {
00296         ind = OFST(i-is,j-js,k-ks);
00297         dv1[k][j][i] = fv1[ind][0]*dvol;
00298         dv2[k][j][i] = fv2[ind][0]*dvol;
00299         dv3[k][j][i] = fv3[ind][0]*dvol;
00300       }
00301     }
00302   }
00303 
00304   /* Calculate net momentum pertubation components t1, t2, t3 */
00305   t0 = 0.0;  t1 = 0.0;  t2 = 0.0;  t3 = 0.0;
00306   for (k=ks; k<=ke; k++) {
00307     t0ij = 0.0;  t1ij = 0.0;  t2ij = 0.0;  t3ij = 0.0;
00308     for (j=js; j<=je; j++) {
00309       t0i = 0.0;  t1i = 0.0;  t2i = 0.0;  t3i = 0.0;
00310       for (i=is; i<=ie; i++) {
00311         t0i += pGrid->U[k][j][i].d;
00312 
00313         /* The net momentum perturbation */
00314         t1i += pGrid->U[k][j][i].d * dv1[k][j][i];
00315         t2i += pGrid->U[k][j][i].d * dv2[k][j][i];
00316         t3i += pGrid->U[k][j][i].d * dv3[k][j][i];
00317       }
00318       t0ij += t0i;  t1ij += t1i;  t2ij += t2i;  t3ij += t3i;
00319     }
00320     t0 += t0ij;  t1 += t1ij;  t2 += t2ij;  t3 += t3ij;
00321   }
00322 
00323 #ifdef MPI_PARALLEL
00324   /* Sum the perturbations over all processors */
00325   m[0] = t0;  m[1] = t1;  m[2] = t2;  m[3] = t3;
00326   mpierr = MPI_Allreduce(m, gm, 4, MPI_RL, MPI_SUM, MPI_COMM_WORLD);
00327   if (mpierr) ath_error("[normalize]: MPI_Allreduce error = %d\n", mpierr);
00328   t0 = gm[0];  t1 = gm[1];  t2 = gm[2];  t3 = gm[3];
00329 #endif /* MPI_PARALLEL */
00330 
00331   /* Subtract the mean velocity perturbation so that the net momentum
00332    * perturbation is zero. */
00333   for (k=ks; k<=ke; k++) {
00334     for (j=js; j<=je; j++) {
00335       for (i=is; i<=ie; i++) {
00336         dv1[k][j][i] -= t1/t0;
00337         dv2[k][j][i] -= t2/t0;
00338         dv3[k][j][i] -= t3/t0;
00339       }
00340     }
00341   }
00342 
00343   /* Calculate unscaled energy of perturbations */
00344   t1 = 0.0;  t2 = 0.0;
00345   for (k=ks; k<=ke; k++) {
00346     t1ij = 0.0;  t2ij = 0.0;
00347     for (j=js; j<=je; j++) {
00348       t1i = 0.0;  t2i = 0.0;
00349       for (i=is; i<=ie; i++) {
00350         /* Calculate velocity pertubation at cell center from
00351          * perturbations at cell faces */
00352         v1 = dv1[k][j][i];
00353         v2 = dv2[k][j][i];
00354         v3 = dv3[k][j][i];
00355 
00356         t1i += (pGrid->U[k][j][i].d)*(SQR(v1) + SQR(v2) + SQR(v3));
00357         t2i +=  (pGrid->U[k][j][i].M1)*v1 + (pGrid->U[k][j][i].M2)*v2 +
00358                      (pGrid->U[k][j][i].M3)*v3;
00359       }
00360       t1ij += t1i;  t2ij += t2i;
00361     }
00362     t1 += t1ij;  t2 += t2ij;
00363   }
00364 
00365 #ifdef MPI_PARALLEL
00366   /* Sum the perturbations over all processors */
00367   m[0] = t1;  m[1] = t2;
00368   mpierr = MPI_Allreduce(m, gm, 2, MPI_RL, MPI_SUM, MPI_COMM_WORLD);
00369   if (mpierr) ath_error("[normalize]: MPI_Allreduce error = %d\n", mpierr);
00370   t1 = gm[0];  t2 = gm[1];
00371 #endif /* MPI_PARALLEL */
00372 
00373   /* Rescale to give the correct energy injection rate */
00374   dvol = pGrid->dx1*pGrid->dx2*pGrid->dx3;
00375   if (idrive == 0) {
00376     /* driven turbulence */
00377     de = dedt*dt;
00378   } else {
00379     /* decaying turbulence (all in one shot) */
00380     de = dedt;
00381   }
00382   aa = 0.5*t1;
00383   aa = MAX(aa,1.0e-20);
00384   b = t2;
00385   c = -de/dvol;
00386   if(b >= 0.0)
00387     s = (-2.0*c)/(b + sqrt(b*b - 4.0*aa*c));
00388   else
00389     s = (-b + sqrt(b*b - 4.0*aa*c))/(2.0*aa);
00390 
00391   if (isnan(s)) ath_error("[perturb]: s is NaN!\n");
00392 
00393   /* Apply momentum pertubations */
00394   for (k=ks; k<=ke; k++) {
00395     for (j=js; j<=je; j++) {
00396       for (i=is; i<=ie; i++) {
00397         qa = s*pGrid->U[k][j][i].d;
00398         pGrid->U[k][j][i].M1 += qa*dv1[k][j][i];
00399         pGrid->U[k][j][i].M2 += qa*dv2[k][j][i];
00400         pGrid->U[k][j][i].M3 += qa*dv3[k][j][i];
00401       }
00402     }
00403   }
00404 
00405   return;
00406 }
00407 
00408 /* ========================================================================== */
00409 /*! \fn static void initialize(Grid *pGrid, Domain *pD)
00410  *  \brief  Allocate memory and initialize FFT plans */
00411 static void initialize(Grid *pGrid, Domain *pD)
00412 {
00413   int i, is=pGrid->is, ie = pGrid->ie;
00414   int j, js=pGrid->js, je = pGrid->je;
00415   int k, ks=pGrid->ks, ke = pGrid->ke;
00416   int nbuf, mpierr, nx1gh, nx2gh, nx3gh;
00417   float kwv, kpara, kperp;
00418   char donedrive = 0;
00419 
00420 /* -----------------------------------------------------------
00421  * Variables within this block are stored globally, and used
00422  * within preprocessor macros.  Don't create variables with
00423  * these names within your function if you are going to use
00424  * OFST(), KCOMP(), or KWVM() within the function! */
00425 
00426   /* Get local grid size */
00427   nx1 = (ie-is+1);
00428   nx2 = (je-js+1);
00429   nx3 = (ke-ks+1);
00430 
00431   /* Get global grid size */
00432   gnx1 = pD->ide - pD->ids + 1;
00433   gnx2 = pD->jde - pD->jds + 1;
00434   gnx3 = pD->kde - pD->kds + 1;
00435 
00436   /* Get extents of local FFT grid in global coordinates */
00437   gis=is+pGrid->idisp;  gie=ie+pGrid->idisp;
00438   gjs=js+pGrid->jdisp;  gje=je+pGrid->jdisp;
00439   gks=ks+pGrid->kdisp;  gke=ke+pGrid->kdisp;
00440 /* ----------------------------------------------------------- */
00441 
00442   /* Get size of arrays with ghost cells */
00443   nx1gh = nx1 + 2*nghost;
00444   nx2gh = nx2 + 2*nghost;
00445   nx3gh = nx3 + 2*nghost;
00446 
00447   /* Get input parameters */
00448 
00449   /* interval for generating new driving spectrum; also interval for
00450    * driving when IMPULSIVE_DRIVING is used */
00451   dtdrive = par_getd("problem","dtdrive");
00452 #ifdef MHD
00453   /* magnetic field strength */
00454   beta = par_getd("problem","beta");
00455   /* beta = isothermal pressure/magnetic pressure */
00456   B0 = sqrt(2.0*Iso_csound2*rhobar/beta);
00457 #endif /* MHD */
00458   /* energy injection rate */
00459   dedt = par_getd("problem","dedt");
00460 
00461   /* parameters for spectrum */
00462   ispect = par_geti("problem","ispect");
00463   if (ispect == 1) {
00464     expo = par_getd("problem","expo");
00465   } else if (ispect == 2) {
00466     kpeak = par_getd("problem","kpeak")*2.0*PI;
00467   } else {
00468     ath_error("Invalid value for ispect\n");
00469   }
00470   /* Cutoff wavenumbers of spectrum */
00471   klow = par_getd("problem","klow"); /* in integer units */
00472   khigh = par_getd("problem","khigh"); /* in integer units */
00473   dkx = 2.0*PI/(pGrid->dx1*gnx1); /* convert k from integer */
00474 
00475   /* Driven or decaying */
00476   idrive = par_geti("problem","idrive");
00477   if ((idrive < 0) || (idrive > 1)) ath_error("Invalid value for idrive\n");
00478   /* If restarting with decaying turbulence, no driving necessary. */
00479   if ((idrive == 1) && (pGrid->nstep > 0)) {
00480     donedrive = 1;
00481   }
00482 
00483   if (donedrive == 0) {
00484     /* Allocate memory for components of velocity perturbation */
00485     if ((dv1=(Real***)calloc_3d_array(nx3gh,nx2gh,nx1gh,sizeof(Real)))==NULL) {
00486       ath_error("[problem]: Error allocating memory for vel pert\n");
00487     }
00488     if ((dv2=(Real***)calloc_3d_array(nx3gh,nx2gh,nx1gh,sizeof(Real)))==NULL) {
00489       ath_error("[problem]: Error allocating memory for vel pert\n");
00490     }
00491     if ((dv3=(Real***)calloc_3d_array(nx3gh,nx2gh,nx1gh,sizeof(Real)))==NULL) {
00492       ath_error("[problem]: Error allocating memory for vel pert\n");
00493     }
00494   }
00495 
00496   /* Initialize the FFT plan */
00497   plan = ath_3d_fft_quick_plan(pGrid, pD, NULL, ATH_FFT_BACKWARD);
00498 
00499   /* Allocate memory for FFTs */
00500   if (donedrive == 0) {
00501     fv1 = ath_3d_fft_malloc(plan);
00502     fv2 = ath_3d_fft_malloc(plan);
00503     fv3 = ath_3d_fft_malloc(plan);
00504   }
00505 
00506   /* Enroll outputs */
00507   dump_history_enroll(hst_dEk,"<dE_K>");
00508   dump_history_enroll(hst_dEb,"<dE_B>");
00509 
00510   return;
00511 }
00512 
00513 /* ========================================================================== */
00514 
00515 /*
00516  *  Function problem
00517  *
00518  *  Set up initial conditions, allocate memory, and initialize FFT plans
00519  */
00520 
00521 void problem(Grid *pGrid, Domain *pD)
00522 {
00523   int i, is=pGrid->is, ie = pGrid->ie;
00524   int j, js=pGrid->js, je = pGrid->je;
00525   int k, ks=pGrid->ks, ke = pGrid->ke;
00526 
00527   rseed = (pGrid->my_id+1);
00528   initialize(pGrid, pD);
00529   tdrive = 0.0;
00530 
00531   /* Initialize uniform density and momenta */
00532   for (k=ks-nghost; k<=ke+nghost; k++) {
00533     for (j=js-nghost; j<=je+nghost; j++) {
00534       for (i=is-nghost; i<=ie+nghost; i++) {
00535         pGrid->U[k][j][i].d = rhobar;
00536         pGrid->U[k][j][i].M1 = 0.0;
00537         pGrid->U[k][j][i].M2 = 0.0;
00538         pGrid->U[k][j][i].M3 = 0.0;
00539       }
00540     }
00541   }
00542 
00543 #ifdef MHD
00544   /* Initialize uniform magnetic field */
00545   for (k=ks-nghost; k<=ke+nghost; k++) {
00546     for (j=js-nghost; j<=je+nghost; j++) {
00547       for (i=is-nghost; i<=ie+nghost; i++) {
00548         pGrid->U[k][j][i].B1c  = B0;
00549         pGrid->U[k][j][i].B2c  = 0.0;
00550         pGrid->U[k][j][i].B3c  = 0.0;
00551         pGrid->B1i[k][j][i] = B0;
00552         pGrid->B2i[k][j][i] = 0.0;
00553         pGrid->B3i[k][j][i] = 0.0;
00554       }
00555     }
00556   }
00557 #endif /* MHD */
00558 
00559   /* Set the initial perturbations.  Note that we're putting in too much
00560    * energy this time.  This is okay since we're only interested in the
00561    * saturated state. */
00562   generate();
00563   perturb(pGrid, dtdrive);
00564 
00565   /* If decaying turbulence, no longer need the driving memory */
00566   if (idrive == 1) {
00567     ath_pout(0,"De-allocating driving memory.\n");
00568 
00569     /* Free Athena-style arrays */
00570     free_3d_array(dv1);
00571     free_3d_array(dv2);
00572     free_3d_array(dv3);
00573 
00574     /* Free FFTW-style arrays */
00575     ath_3d_fft_free(fv1);
00576     ath_3d_fft_free(fv2);
00577     ath_3d_fft_free(fv3);
00578   }
00579 
00580   return;
00581 }
00582 
00583 /* ========================================================================== */
00584 
00585 /*
00586  *  Function Userwork_in_loop
00587  *
00588  *  Drive velocity field for turbulence in GMC problems
00589  */
00590 
00591 void Userwork_in_loop(Grid *pGrid, Domain *pD)
00592 {
00593   int i, is=pGrid->is, ie = pGrid->ie;
00594   int j, js=pGrid->js, je = pGrid->je;
00595   int k, ks=pGrid->ks, ke = pGrid->ke;
00596   Real newtime;
00597 
00598   if (isnan(pGrid->dt)) ath_error("Time step is NaN!");
00599 
00600   if (idrive == 0) {  /* driven turbulence */
00601     /* Integration has already been done, but time not yet updated */
00602     newtime = pGrid->time + pGrid->dt;
00603 
00604 #ifndef IMPULSIVE_DRIVING
00605     /* Drive on every time step */
00606     perturb(pGrid, pGrid->dt);
00607 #endif /* IMPULSIVE_DRIVING */
00608 
00609     if (newtime >= (tdrive+dtdrive)) {
00610       /* If we start with large time steps so that tdrive would get way
00611        * behind newtime, this makes sure we don't keep generating after
00612        * dropping down to smaller time steps */
00613       while ((tdrive+dtdrive) <= newtime) tdrive += dtdrive;
00614 
00615 #ifdef IMPULSIVE_DRIVING
00616       /* Only drive at intervals of dtdrive */
00617       perturb(pGrid, dtdrive);
00618 #endif /* IMPULSIVE_DRIVING */
00619 
00620       /* Compute new spectrum after dtdrive.  Putting this after perturb()
00621        * means we won't be applying perturbations from a new power spectrum
00622        * just before writing outputs.  At the very beginning, we'll go a
00623        * little longer before regenerating, but the energy injection rate
00624        * was off on the very first timestep anyway.  When studying driven
00625        * turbulence, all we care about is the saturated state. */
00626       generate();
00627     }
00628   }
00629 
00630   return;
00631 }
00632 
00633 /* ========================================================================== */
00634 
00635 void Userwork_after_loop(Grid *pGrid, Domain *pD)
00636 {
00637   /* Don't free memory here if doing any analysis because final
00638    * output hasn't been written yet!! */
00639   return;
00640 }
00641 
00642 void problem_write_restart(Grid *pG, Domain *pD, FILE *fp)
00643 {  return;  }
00644 
00645 void problem_read_restart(Grid *pG, Domain *pD, FILE *fp)
00646 {  
00647   /* Allocate memory and initialize everything */
00648   rseed  = (pG->my_id+1);
00649   initialize(pG, pD);
00650   tdrive = pG->time;
00651 
00652   /* Generate a new power spectrum */
00653   if (idrive == 0) generate();
00654 
00655   return;
00656 }
00657 
00658 Gasfun_t get_usr_expr(const char *expr)
00659 {  return NULL;  }
00660 
00661 VGFunout_t get_usr_out_fun(const char *name){
00662   return NULL;
00663 }
00664 
00665 /* ========================================================================== */
00666 
00667 /*
00668  *  Function hst_*
00669  *
00670  *  Dumps to history file
00671  */
00672 
00673 /*! \fn static Real hst_dEk(const Grid *pG, const int i,const int j,const int k)
00674  *  \brief Dump kinetic energy in perturbations */
00675 static Real hst_dEk(const Grid *pG, const int i, const int j, const int k)
00676 { /* The kinetic energy in perturbations is 0.5*d*V^2 */
00677   return 0.5*(pG->U[k][j][i].M1*pG->U[k][j][i].M1 +
00678               pG->U[k][j][i].M2*pG->U[k][j][i].M2 +
00679               pG->U[k][j][i].M3*pG->U[k][j][i].M3)/pG->U[k][j][i].d;
00680 }
00681 
00682 /*! \fn static Real hst_dEb(const Grid *pG, const int i,const int j,const int k)
00683  *  \brief Dump magnetic energy in perturbations */
00684 static Real hst_dEb(const Grid *pG, const int i, const int j, const int k)
00685 { /* The magnetic energy in perturbations is 0.5*B^2 - 0.5*B0^2 */
00686 #ifdef MHD
00687   return 0.5*((pG->U[k][j][i].B1c*pG->U[k][j][i].B1c +
00688                pG->U[k][j][i].B2c*pG->U[k][j][i].B2c +
00689                pG->U[k][j][i].B3c*pG->U[k][j][i].B3c)-B0*B0);
00690 #else /* MHD */
00691   return 0.0;
00692 #endif /* MHD */
00693 }
00694 
00695 /* ========================================================================== */
00696 
00697 #define IM1 2147483563
00698 #define IM2 2147483399
00699 #define AM (1.0/IM1)
00700 #define IMM1 (IM1-1)
00701 #define IA1 40014
00702 #define IA2 40692
00703 #define IQ1 53668
00704 #define IQ2 52774
00705 #define IR1 12211
00706 #define IR2 3791
00707 #define NDIV (1+IMM1/NTAB)
00708 #define RNMX (1.0-DBL_EPSILON)
00709 #define NTAB 32
00710 
00711 /*! \fn double ran2(long int *idum){
00712  *  \brief The routine ran2() is extracted from the Numerical Recipes in C 
00713  *
00714  * The routine ran2() is extracted from the Numerical Recipes in C
00715  * (version 2) code.  I've modified it to use doubles instead of
00716  * floats. -- T. A. Gardiner -- Aug. 12, 2003 
00717  *
00718  * Long period (> 2 x 10^{18}) random number generator of L'Ecuyer
00719  * with Bays-Durham shuffle and added safeguards.  Returns a uniform
00720  * random deviate between 0.0 and 1.0 (exclusive of the endpoint
00721  * values).  Call with idum = a negative integer to initialize;
00722  * thereafter, do not alter idum between successive deviates in a
00723  * sequence.  RNMX should appriximate the largest floating point value
00724  * that is less than 1. */
00725 
00726 double ran2(long int *idum){
00727   int j;
00728   long int k;
00729   static long int idum2=123456789;
00730   static long int iy=0;
00731   static long int iv[NTAB];
00732   double temp;
00733 
00734   if (*idum <= 0) { /* Initialize */
00735     if (-(*idum) < 1) *idum=1; /* Be sure to prevent idum = 0 */
00736     else *idum = -(*idum);
00737     idum2=(*idum);
00738     for (j=NTAB+7;j>=0;j--) { /* Load the shuffle table (after 8 warm-ups) */
00739       k=(*idum)/IQ1;
00740       *idum=IA1*(*idum-k*IQ1)-k*IR1;
00741       if (*idum < 0) *idum += IM1;
00742       if (j < NTAB) iv[j] = *idum;
00743     }
00744     iy=iv[0];
00745   }
00746   k=(*idum)/IQ1;                 /* Start here when not initializing */
00747   *idum=IA1*(*idum-k*IQ1)-k*IR1; /* Compute idum=(IA1*idum) % IM1 without */
00748   if (*idum < 0) *idum += IM1;   /* overflows by Schrage's method */
00749   k=idum2/IQ2;
00750   idum2=IA2*(idum2-k*IQ2)-k*IR2; /* Compute idum2=(IA2*idum) % IM2 likewise */
00751   if (idum2 < 0) idum2 += IM2;
00752   j=(int)(iy/NDIV);              /* Will be in the range 0...NTAB-1 */
00753   iy=iv[j]-idum2;                /* Here idum is shuffled, idum and idum2 */
00754   iv[j] = *idum;                 /* are combined to generate output */
00755   if (iy < 1) iy += IMM1;
00756   if ((temp=AM*iy) > RNMX) return RNMX; /* No endpoint values */
00757   else return temp;
00758 }
00759 
00760 #undef IM1
00761 #undef IM2
00762 #undef AM
00763 #undef IMM1
00764 #undef IA1
00765 #undef IA2
00766 #undef IQ1
00767 #undef IQ2
00768 #undef IR1
00769 #undef IR2
00770 #undef NTAB
00771 #undef NDIV
00772 #undef RNMX
00773 
00774 #undef OFST
00775 #undef KCOMP
00776 #undef KWVM

Generated on Mon Sep 27 2010 23:03:08 for Athena by  doxygen 1.7.1