The FARGO_THORIN code developer's guide
 All Data Structures Files Functions Variables Typedefs Macros
SourceEuler.c
Go to the documentation of this file.
1 /** \file SourceEuler.c
2 
3 Contains routines used by the hydrodynamical loop. More specifically,
4 it contains the main loop itself and all the source term substeps
5 (with the exception of the evaluation of the viscous force). The
6 transport substep is treated elsewhere.
7 
8 @author THORIN modifications by
9 Ondřej Chrenko <chrenko@sirrah.troja.mff.cuni.cz>, Copyright (C) 2017;
10 original code by Frédéric Masset
11 
12 */
13 
14 #include "fargo.h"
15 
16 #define CFLSECURITY 0.5 /* Maximum fraction of zone size */
17  /* swept in one timestep */
18 
19 #define CVNR 1.41 /* Shocks are spread over CVNR zones: */
20  /* von Neumann-Richtmyer viscosity constant */
21  /* Beware of misprint in Stone and Norman's */
22  /* paper : use C2^2 instead of C2 */
26 static real timeCRASH;
27 extern boolean Corotating;
28 
29 static PolarGrid *EnergyNew, *EnergyInt; /* #THORIN */
30 
32 
33 extern int TimeStep;
34 extern boolean FastTransport, IsDisk;
35 
36 
37 boolean DetectCrash (array)
38 PolarGrid *array;
39 {
40  int i, j, l, nr, ns;
41  real *ptr;
42  boolean bool = NO;
43  nr = array->Nrad;
44  ns = array->Nsec;
45  ptr= array->Field;
46 #pragma omp parallel for private(j,l) shared(bool)
47  for (i = 0; i < nr; i++) {
48  for (j = 0; j < ns; j++) {
49  l = j+i*ns;
50  if (ptr[l] < 0.0)
51  bool = YES;
52  }
53  }
54  return bool;
55 }
56 
57 void FillPolar1DArrays () /* #THORIN */
58 {
59  FILE *input, *output;
60  int i,ii;
61  real drrsep;
62  float temporary;
63  char InputName[256], OutputName[256];
64  drrsep = (RMAX-RMIN)/(real)GLOBALNRAD;
65  sprintf (InputName, "%s%s", OUTPUTDIR, "radii.dat");
66  sprintf (OutputName, "%s%s", OUTPUTDIR, "used_rad.dat");
67  input = fopen (InputName, "r");
68  if (input == NULL) {
69  mastererr ("Warning : no `radii.dat' file found. Using default.\n");
70  if (LogGrid == YES) {
71  for (i = 0; i <= GLOBALNRAD; i++) {
72  Radii[i] = RMIN*exp((real)i/(real)GLOBALNRAD*log(RMAX/RMIN));
73  }
74  } else {
75  for (i = 0; i <= GLOBALNRAD; i++) {
76  Radii[i] = RMIN+drrsep*(real)(i);
77  }
78  }
79  } else {
80  mastererr ("Reading 'radii.dat' file.\n");
81  for (i = 0; i <= GLOBALNRAD; i++) {
82  fscanf (input, "%f", &temporary);
83  Radii[i] = (real)temporary;
84  }
85  }
86  for (i = 0; i < GLOBALNRAD; i++) {
87  GlobalRmed[i] = 2.0/3.0*(Radii[i+1]*Radii[i+1]*Radii[i+1]-Radii[i]*Radii[i]*Radii[i]);
88  GlobalRmed[i] = GlobalRmed[i] / (Radii[i+1]*Radii[i+1]-Radii[i]*Radii[i]);
89  }
90  for (i = 0; i < NRAD; i++) {
91  ii = i+IMIN;
92  Rinf[i] = Radii[ii];
93  Rsup[i] = Radii[ii+1];
94  Rmed[i] = 2.0/3.0*(Rsup[i]*Rsup[i]*Rsup[i]-Rinf[i]*Rinf[i]*Rinf[i]);
95  Rmed[i] = Rmed[i] / (Rsup[i]*Rsup[i]-Rinf[i]*Rinf[i]);
96  Surf[i] = PI*(Rsup[i]*Rsup[i]-Rinf[i]*Rinf[i])/(real)NSEC;
97  InvRmed[i] = 1.0/Rmed[i];
98  InvSurf[i] = 1.0/Surf[i];
99  InvDiffRsup[i] = 1.0/(Rsup[i]-Rinf[i]);
100  InvRinf[i] = 1.0/Rinf[i];
101  OmegaInv[i] = pow(Rmed[i],1.5); /* #THORIN */
102  Rmed2[i] = Rmed[i]*Rmed[i]; /* #THORIN */
103  }
104  Rinf[NRAD]=Radii[NRAD+IMIN];
105  for (i = 1; i < NRAD; i++) {
106  InvDiffRmed[i] = 1.0/(Rmed[i]-Rmed[i-1]);
107  }
108  if (CPU_Master) {
109  output = fopen (OutputName, "w");
110  if (output == NULL) {
111  mastererr ("Can't write %s.\nProgram stopped.\n", OutputName);
112  prs_exit (1);
113  }
114  for (i = 0; i <= GLOBALNRAD; i++) {
115  fprintf (output, "%.18g\n", Radii[i]);
116  }
117  fclose (output);
118  }
119  if (input != NULL) fclose (input);
120 }
121 
122 void InitEuler (Rho, Vr, Vt, En) /* #THORIN */
123 PolarGrid *Rho, *Vr, *Vt, *En;
124 {
125  SQRT_ADIABIND_INV = 1.0/sqrt(ADIABIND);
126  FillPolar1DArrays (); /* #THORIN: construct the arrays related to the grid (Rinf, Rsup, ...) here */
127  FillSigma (); /* calculate SigmaMed[] and SigmaInf[] based on the surf.density profile */
128  FillEnergy (); /* #THORIN: calculate EnergyMed; see Theo.c */
129  InitGasDensityEnergy (Rho, En); /* #THORIN: we initialize the density and energy
130  here because they are needed to compute
131  the sound speed, pressure and temperature;
132  see Pframeforce.c */
133  InitTransport (); /* allocate fields related to the transport step (transported momenta etc.) */
134  InitViscosity (); /* allocate fields related to the viscous terms (velocity divergence and viscous stress tensor) */
135  RhoStar = CreatePolarGrid(NRAD, NSEC, "RhoStar");
136  RhoInt = CreatePolarGrid(NRAD, NSEC, "RhoInt");
137  VradNew = CreatePolarGrid(NRAD, NSEC, "VradNew");
138  VradInt = CreatePolarGrid(NRAD, NSEC, "VradInt");
139  VthetaNew = CreatePolarGrid(NRAD, NSEC, "VthetaNew");
140  VthetaInt = CreatePolarGrid(NRAD, NSEC, "VthetaInt");
141  TemperInt = CreatePolarGrid(NRAD, NSEC, "TemperInt");
142  GravAccelRad = CreatePolarGrid(NRAD, NSEC, "agr"); /* #THORIN -----> */
144  if (Pebbles) {
147  }
148  EnergyInt = CreatePolarGrid(NRAD, NSEC, "EnergyInt"); /* intermediate energy, calculated in SubStep2, used in SubStep3; defined above as static */
149  EnergyNew = CreatePolarGrid(NRAD, NSEC, "EnergyNew"); /* calculated in SubStep3, then used to update the energy HD field; defined above as static */
150  SoundSpeed = CreatePolarGrid(NRAD, NSEC, "SoundSpeed"); /* see global.h */
151  Pressure = CreatePolarGrid(NRAD, NSEC, "Pressure");
152  Temperature = CreatePolarGrid(NRAD, NSEC, "temper");
153  Qplus = CreatePolarGrid(NRAD, NSEC, "qplus");
154  /* note: 'Name' member of the 'PolarGrid' structure is "gas" + "argument" */
155  ComputeSoundSpeed (Rho, En); /* fill the HD pressure field; fill 'globcsvec[]' which is a 1D vector with azimuth-averaged values of sound speed */
156  ComputePressureField (Rho, En);
157  ComputeTemperatureField (Rho, En);
158  InitGasVelocity (Vr, Vt);
159  if (DampInit) FillVtheta (Vt); /* construct the initial field of azim-averaged Vtheta for damping boundary condition */
161  if (Damping || Pebbles) SetWaveKillingZones ();
162  if (Pebbles) InitPebbleArrays ();
163  if (TorqueDensity) Torque = CreatePolarGrid(NRAD, NSEC, "torque"); /* #THORIN <----- */
164 }
165 
167 real a,b;
168 {
169  if (b < a) return b;
170  return a;
171 }
172 
174 real a,b;
175 {
176  if (b > a) return b;
177  return a;
178 }
179 
180 
181 void ActualiseGas (array, newarray)
182 PolarGrid *array, *newarray;
183 {
184  int i,j,l,ns,nr;
185  real *old, *new;
186  nr = array->Nrad;
187  ns = array->Nsec;
188  old= array->Field;
189  new= newarray->Field;
190 #pragma omp parallel for private(j,l)
191  for (i = 0; i <= nr; i++) {
192  for (j = 0; j < ns; j++) {
193  l = j+ns*i;
194  old[l] = new[l];
195  }
196  }
197 }
198 
199 void AlgoGas (Rho,Vrad,Vtheta,Energy,Label,sys,rsim) /* #THORIN non-isothermal gas, pebble disk and rebound simulation added */
200 PolarGrid *Rho, *Vrad, *Vtheta, *Energy, *Label;
201 PlanetarySystem *sys;
202 struct reb_simulation *rsim;
203 {
204  real dt, dtemp=0.0;
205  real OmegaNew, domega;
206  int gastimestepcfl;
207  boolean Crashed=NO;
208  gastimestepcfl = 1;
209  /* ----- */
210  if (EnergyEq) { /* #THORIN */
211  ComputeSoundSpeed (Rho, Energy);
212  }
213  if (IsDisk == YES) {
214  CommunicateBoundaries (Rho,Vrad,Vtheta,Energy,Label); /* see commbound.c*/
215  if (Pebbles) SynchronizePebbleDisc (); /* #THORIN */
216  if (SloppyCFL == YES) /* 2DO not tested */
217  gastimestepcfl = ConditionCFL (Vrad, Vtheta, DT-dtemp);
218  }
219  MPI_Allreduce (&gastimestepcfl, &GasTimeStepsCFL, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
220  dt = DT / (real)GasTimeStepsCFL;
221  while (dtemp < 0.999999999*DT) {
222  MassTaper = PhysicalTime/(MASSTAPER*2.0*M_PI);
223  MassTaper = (MassTaper > 1.0 ? 1.0 : pow(sin(MassTaper*M_PI/2.0),2.0));
224  if (IsDisk == YES) {
225  CommunicateBoundaries (Rho,Vrad,Vtheta,Energy,Label); /* #THORIN */
226  if (Pebbles) {
227  SynchronizePebbleDisc (); /* #THORIN */
228  PebbleStokesNumbers (Rho); /* #THORIN: need to update Stokes numbers as the HD background evolves */
229  }
230  if (SloppyCFL == NO) {
231  gastimestepcfl = 1;
232  MinStepForRebound (rsim); /* #THORIN */
233  if (Pebbles) CriticalCharTime (Vrad, Vtheta); /* #THORIN */
234  gastimestepcfl = ConditionCFL (Vrad, Vtheta, DT-dtemp);
235  MPI_Allreduce (&gastimestepcfl, &GasTimeStepsCFL, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
236  dt = (DT-dtemp)/(real)GasTimeStepsCFL;
237  }
238  AccreteOntoPlanets (Rho, Vrad, Vtheta, dt, sys);
240  if (Pebbles) AccretePebblesOntoPlanets (sys, Rho, Energy, Vtheta, dt); /* #THORIN */
241  }
242  dtemp += dt;
243  if (Corotating == YES) GetPsysInfo (sys, MARK);
244  if (IsDisk == YES) {
245  FillForcesArrays (Rho, sys); /* #THORIN: calculate grav.accelerations (instead of standard potential) */
246  AdvanceSystemFromDisk (Rho, sys, dt); /* #THORIN: use accelerations from the above function + Tanaka&Ward(04) damping */
247  }
248  AdvanceSystemRebound (sys, rsim, dt); /* #THORIN */
249  if (Corotating == YES) {
250  OmegaNew = GetPsysInfoFromRsim (rsim, GET) / dt; /* #THORIN: FARGO and REBOUND not yet synchronized -> has to get info from 'rsim' */
251  domega = OmegaNew-OmegaFrame;
252  if (IsDisk == YES) CorrectVtheta (Vtheta, domega);
253  if (Pebbles) CorrectPebblesVtheta (domega); /* #THORIN */
254  OmegaFrame = OmegaNew;
255  }
256  RotatePsys (rsim, OmegaFrame*dt); /* #THORIN, see psys.c */
257  SynchronizeFargoRebound (sys, rsim); /* #THORIN */
258  if (IsDisk == YES) {
259  ApplyBoundaryCondition (Vrad, Vtheta, Rho, Energy, dt); /* #THORIN */
260  Crashed = DetectCrash (Rho); /* test for negative density values */
261  Crashed = DetectCrash (Energy); /* #THORIN: test for negative energy values */
262  if (Pebbles) Crashed = DetectCrashPebbles (); /* #THORIN: test for negative pebble values */
263  if (Crashed == YES) {
264  if (AlreadyCrashed == 0) {
265  timeCRASH=PhysicalTime; /* if it appears to be the first crash */
266  fprintf (stdout,"\nCrash! at time %.12g\n", timeCRASH);
267  WriteDiskPolar (Rho, 999); /* We write the HD arrays */
268  WriteDiskPolar (Vrad, 999); /* in order to keep a track */
269  WriteDiskPolar (Vtheta, 999); /* of what happened */
270  WriteDiskPolar (Temperature, 999); /* #THORIN */
271  }
272  AlreadyCrashed++;
273  masterprint ("c");
274  } else {
275  masterprint (".");
276  }
277  fflush (stdout);
278  ComputePressureField (Rho, Energy); /* #THORIN */
279  if (Pebbles) SourceTermsPebbles (Vrad, Vtheta, dt); /* #THORIN: this must be before SubStep1 because of backreaction */
280  SubStep1 (Vrad, Vtheta, Rho, dt);
281  if (Pebbles) {
282  SubStep1Pebbles (Vrad, Vtheta, dt); /* #THORIN !!! has to send non-updated velocities (thus sending Vrad instead of VradInt etc) */
283  if (DiffusiveParticles) ParticleDiffusion (Rho); /* #THORIN */
284  }
285  SubStep2 (Rho, Energy, dt); /* #THORIN */
286  ActualiseGas (Vrad, VradNew);
287  ActualiseGas (Vtheta, VthetaNew);
288  ApplyBoundaryCondition (Vrad, Vtheta, Rho, Energy, dt); /* #THORIN */
289  if (EnergyEq) {
290  UpdateDivVelocAndStressTensor (Vrad, Vtheta, Rho); /* #THORIN: we have to update the viscous stress tensor and div(v) here */
291  SubStep3 (Rho, dt); /* #THORIN */
292  ActualiseGas (Energy, EnergyNew); /* #THORIN */
293  }
294  Transport (Rho, Vrad, Vtheta, Energy, Label, dt); /* #THORIN */
295  ApplyBoundaryCondition (Vrad, Vtheta, Rho, Energy, dt); /* #THORIN */
296  ComputeTemperatureField (Rho, Energy); /* #THORIN */
297  if (Pebbles) EvolvePebbleDisk (dt); /* #THORIN: advance the disk of pebbles */
298  }
299  PhysicalTime += dt;
300  }
301  masterprint ("\n");
302 }
303 
304 void SubStep1 (Vrad, Vtheta, Rho, dt) /* #THORIN */
305 PolarGrid *Vrad, *Vtheta, *Rho;
306 real dt;
307 {
308  int i, j, l, lim, ljm, ljp, nr, ns;
309  real *vrad, *vtheta, *rho;
310  real *vradint, *vthetaint;
311  real *accr, *acct, *dragr, *dragt; /* #THORIN: gas acceleration and backreaction (needed for pebble disk) */
312  real gradp, vt2, dxtheta, tmp;
313  real invdxtheta;
314  real supp_torque=0.0; /* for imposed disk drift */
315  real *press; /* #THORIN */
316  real *agr, *agt; /* #THORIN */
317  /* ----- */
318  nr = Vrad->Nrad;
319  ns = Vrad->Nsec;
320  rho = Rho->Field;
321  vrad = Vrad->Field;
322  vtheta = Vtheta->Field;
323  vradint = VradInt->Field;
324  vthetaint = VthetaInt->Field;
325  press = Pressure->Field; /* #THORIN: used instead of cs^2*\rho terms */
326  agr = GravAccelRad->Field;
327  agt = GravAccelTheta->Field;
328  if (Pebbles) { /* #THORIN */
329  accr = GasAccelrad->Field;
330  acct = GasAcceltheta->Field;
331  if (BackReaction) {
332  dragr = DragForceRad->Field;
333  dragt = DragForceTheta->Field;
334  }
335  }
336  /* In this substep we take into account */
337  /* the source part of Euler equations */
338  /* (i.e. the R.H.S. in classical notation). */
339 #pragma omp parallel private(j,l,lim,ljm,ljp,dxtheta,vt2,gradp,invdxtheta,supp_torque,tmp)
340  {
341 #pragma omp for nowait
342  for (i = 1; i < nr; i++) {
343  for (j = 0; j < ns; j++) {
344  l = j+i*ns;
345  lim = l-ns;
346  ljm = l-1;
347  if (j == 0) ljm = i*ns+ns-1;
348  ljp = l+1;
349  if (j == ns-1) ljp = i*ns;
350  gradp = (press[l]-press[lim])*2.0/(rho[l]+rho[lim])*InvDiffRmed[i];
351  vt2 = vtheta[l]+vtheta[ljp]+vtheta[lim]+vtheta[ljp-ns];
352  vt2 = vt2/4.0+Rinf[i]*OmegaFrame;
353  vt2 = vt2*vt2;
354  tmp = -gradp+0.5*(agr[l]+agr[lim])+vt2*InvRinf[i]; /* #THORIN */
355  vradint[l] = vrad[l]+dt*tmp;
356  if (Pebbles) { /* #THORIN */
357  accr[l] = tmp;
358  if (BackReaction) {
359  tmp = dragr[l]*2.0/(rho[l]+rho[lim]);
360  vradint[l] += dt*tmp;
361  accr[l] += tmp;
362  }
363  }
364  }
365  }
366 #pragma omp for
367  for (i = 0; i < nr; i++) {
368  supp_torque = IMPOSEDDISKDRIFT*.5*pow(Rmed[i],-2.5+SIGMASLOPE);
369  dxtheta = 2.0*PI/(real)ns*Rmed[i];
370  invdxtheta = 1.0/dxtheta;
371  for (j = 0; j < ns; j++) {
372  l = j+i*ns;
373  lim = l-ns;
374  ljm = l-1;
375  if (j == 0) ljm = i*ns+ns-1;
376  ljp = l+1;
377  if (j == ns-1) ljp = i*ns;
378  gradp = (press[l]-press[ljm])*2.0/(rho[l]+rho[ljm])*invdxtheta;
379  tmp = -gradp + 0.5*(agt[l]+agt[ljm]); /* #THORIN */
380  vthetaint[l] = vtheta[l]+dt*tmp;
381  vthetaint[l] += dt*supp_torque;
382  if (Pebbles) { /* #THORIN */
383  acct[l] = tmp + supp_torque;
384  if (BackReaction) {
385  tmp = dragt[l]*2.0/(rho[l]+rho[ljm]);
386  vthetaint[l] += dt*tmp;
387  acct[l] += tmp;
388  }
389  }
390  }
391  }
392  }
393  /* #THORIN: original scheme was wrapped in a single routine ViscousTerms().
394  * Now we use separate routines to: 1) update div(v) & stress tensor,
395  * 2) modify the velocities using viscous terms, 3) impose Keplerian
396  * rotation at the boundaries but only if the Damping condition is not used */
397  UpdateDivVelocAndStressTensor (VradInt, VthetaInt, Rho);
398  UpdateVelocityWithViscousTerms (VradInt, VthetaInt, Rho, dt);
399  if (!Damping) ImposeKeplerianEdges (VthetaInt);
400 }
401 
402 void SubStep2 (Rho, Energy, dt) /* #THORIN */
403 PolarGrid *Rho, *Energy;
404 real dt;
405 {
406  int i, j, l, lim, lip, ljm, ljp, nr, ns;
407  real *vrad, *vtheta, *rho, *energy;
408  real *vradnew, *vthetanew, *qt, *qr, *energyint; /* #THORIN */
409  real dxtheta, invdxtheta;
410  real dv;
411  nr = VradInt->Nrad;
412  ns = VradInt->Nsec;
413  rho = Rho->Field;
414  vrad = VradInt->Field;
415  vtheta = VthetaInt->Field;
416  qr = RhoInt->Field;
417  vradnew = VradNew->Field;
418  vthetanew = VthetaNew->Field;
419  qt = TemperInt->Field;
420  energy = Energy->Field;
421  energyint = EnergyInt->Field; /* note: while intermediate values of vr and vt
422  are already computed at this point, this is
423  the first time we update the energy. We thus
424  calculate the value asociated with the
425  'Intermediate' array here. */
426 #pragma omp parallel for private(j,dxtheta,l,lim,lip,ljm,ljp,dv) /* #THORIN: dxtheta, ljm and lim deleted below (not needed in this loop) */
427  for (i = 0; i < nr; i++) {
428  for (j = 0; j < ns; j++) {
429  l = j+i*ns;
430  lip = l+ns;
431  ljp = l+1;
432  if (j == ns-1) ljp = i*ns;
433  dv = vrad[lip]-vrad[l];
434  if (dv < 0.0)
435  qr[l] = CVNR*CVNR*rho[l]*dv*dv;
436  else
437  qr[l] = 0.0;
438  dv = vtheta[ljp]-vtheta[l];
439  if (dv < 0.0)
440  qt[l] = CVNR*CVNR*rho[l]*dv*dv;
441  else
442  qt[l] = 0.0;
443  }
444  }
445 #pragma omp parallel private(l,lim,lip,ljm,ljp,j,dxtheta,invdxtheta)
446  {
447 #pragma omp for nowait
448  /* #THORIN: lip, ljm and ljp deleted below (not needed in this loop) */
449  for (i = 1; i < nr; i++) {
450  for (j = 0; j < ns; j++) {
451  l = j+i*ns;
452  lim = l-ns;
453  vradnew[l] = vrad[l]-dt*2.0/(rho[l]+rho[lim])*(qr[l]-qr[lim])*InvDiffRmed[i];
454  }
455  }
456 #pragma omp for
457  /* #THORIN: lim, lip and ljp deleted below (not needed in this loop) */
458  for (i = 0; i < nr; i++) {
459  dxtheta = 2.0*PI/(real)ns*Rmed[i];
460  invdxtheta = 1.0/dxtheta;
461  for (j = 0; j < ns; j++) {
462  l = j+i*ns;
463  ljm = l-1;
464  if (j == 0) ljm = i*ns+ns-1;
465  vthetanew[l] = vtheta[l]-dt*2.0/(rho[l]+rho[ljm])*(qt[l]-qt[ljm])*invdxtheta;
466  }
467  }
468  /* #THORIN: Artifical source term of the energy equation stemming from
469  * the artificial viscosity which was designed
470  * to suppress shocks on the staggered mesh.
471  * (see Stone & Norman 1992). */
472  if (EnergyEq) {
473 #pragma omp for nowait
474  for (i=0; i<nr; i++) {
475  dxtheta = 2.0*PI/(real)ns*Rmed[i];
476  invdxtheta = 1.0/dxtheta;
477  for (j = 0; j < ns; j++) {
478  l = j+i*ns;
479  lip = l+ns;
480  ljp = l+1;
481  if (j == ns-1) ljp = i*ns;
482  energyint[l] = energy[l] - \
483  dt*qr[l]*(vrad[lip]-vrad[l])*InvDiffRsup[i] - \
484  dt*qt[l]*(vtheta[ljp]-vtheta[l])*invdxtheta;
485  }
486  }
487  }
488  }
489 }
490 
491 /** Numerical step reponsible for the energy
492  * update. Calls the energy equation solver. */
493 void SubStep3 (Rho, dt) /* #THORIN */
494 PolarGrid *Rho;
495 real dt;
496 {
497  real *rho, *energy, *energynew, *qplus, *Trr, *Trp, *Tpp, *divergence;
498  int i, j, l, nr, ns;
499  real viscosity, r0, r1, r2, qplus1, qplus2, tmp1, tmp2;
500  /* --- */
501  rho = Rho->Field;
502  nr = Rho->Nrad;
503  ns = Rho->Nsec;
504  energy = EnergyInt->Field; /* we use the intermediate value of energy
505  calculated in SubStep2 as the initial value here */
506  energynew = EnergyNew->Field;
507  divergence = DivergenceVelocity->Field;
508  qplus = Qplus->Field;
509  Trr = TAURR->Field;
510  Trp = TAURP->Field;
511  Tpp = TAUPP->Field;
512  /* Stuff needed to extrapolate the heating term for i=0 */
513  r0 = Rmed[0];
514  r1 = Rmed[1];
515  r2 = Rmed[2];
516 #pragma omp parallel default(none) \
517  shared (nr,ns,Rmed,rho,Trr,Trp,Tpp,divergence,qplus,r0,r1,r2,EnergyMed,\
518  dt,SigmaMed,CoolingTimeMed,energy,QplusMed,energynew,\
519  ParametricCooling,ADIABIND) \
520  private (i,j,l,viscosity,qplus1,qplus2,tmp1,tmp2)
521  {
522 #pragma omp for
523  /* As Trp component of the viscosity tensor is defined from i=1,
524  * we first calculate the source term caused by viscous heating
525  * starting from i=1 */
526  for (i=1; i<nr; i++) {
527  viscosity = FViscosity (Rmed[i]);
528  for (j=0; j<ns; j++) {
529  l = j + i*ns;
530  if (viscosity != 0.0) { /* !!! FACTOR 2.0 NEXT TO THE rp TERM BELOW (this was wrong in ADSG version) */
531  qplus[l] = 0.5/viscosity/rho[l]*(Trr[l]*Trr[l] + \
532  2.0*Trp[l]*Trp[l] + \
533  Tpp[l]*Tpp[l]);
534  qplus[l] += (2.0/9.0)*viscosity*rho[l]*divergence[l]*divergence[l];
535  } else {
536  qplus[l] = 0.0;
537  }
538  }
539  }
540  /* Now we extrapolate to find the heating term for i=0 */
541  viscosity = FViscosity (Rmed[0]);
542 #pragma omp for
543  for (j=0; j<ns; j++) { /* we extrapolate towards the cells in the innermost ring ... */
544  qplus1 = qplus[j+ns]; /* ... using two adjacent rings of cells */
545  qplus2 = qplus[j+2*ns];
546  if (viscosity != 0.0) {
547  /* power-law extrapolation */
548  qplus[j] = qplus1*exp( log(qplus1/qplus2)*log(r0/r1)/log(r1/r2) );
549  } else {
550  qplus[j] = 0.0;
551  }
552  }
553  /* If the parametric cooling is used, update energy for all values of 'i' */
554  if (ParametricCooling) { // 2DO deprecated, could be removed
555 #pragma omp for
556  for (i=0; i<nr; i++) {
557  for (j=0; j<ns; j++) {
558  l = j + i*ns;
559  /* Note: QplusMed[] and CoolingTimeMed[] initialized through FillQplus()
560  * and FillCoolingTime() in InitGasVelocities(), see Pframeforce.c */
561  tmp1 = EnergyMed[i]*dt*rho[l]/SigmaMed[i] + CoolingTimeMed[i]*energy[l] + \
562  dt*CoolingTimeMed[i]*(qplus[l] - QplusMed[i]*rho[l]/SigmaMed[i]);
563  tmp2 = dt + CoolingTimeMed[i] + (ADIABIND-1.0)*dt*CoolingTimeMed[i]*divergence[l];
564  energynew[l] = tmp1/tmp2;
565  }
566  }
567  }
568  } // end of the omp parallel section
569  /* Use an implicit method if radiative terms are accounted for */
570  if (!ParametricCooling) ImplicitRadiativeDiffusion (Rho, EnergyInt, EnergyNew, dt);
571 }
572 /* <---- */
573 
574 int ConditionCFL (Vrad, Vtheta, deltaT) /* #THORIN */
575 PolarGrid *Vrad, *Vtheta;
576 real deltaT;
577 {
578  static real Vresidual[MAX1D], Vmoy[MAX1D];
579  int i, j, l, ns, nr, lip, ljp;
580  real invdt1, invdt2, invdt3, invdt4, invdt5=1e-30, cs, newdt, dt;
581  int ideb=0, jdeb=0;
582  real itdbg1=0., itdbg2=0., itdbg3=0., itdbg4=0., itdbg5=0., mdtdbg=0.;
583  /* debugging variables */
584  real *vt, *vr, dxrad, dxtheta, dvr, dvt, viscr=0., visct=0.;
585  real *soundspeed; /* #THORIN */
586  /* ----- */
587  ns = Vtheta->Nsec;
588  nr = Vtheta->Nrad;
589  vt = Vtheta->Field;
590  vr = Vrad->Field;
591  soundspeed = SoundSpeed->Field;
592  newdt = 1e30;
593  for (i = 0; i < nr; i++) {
594  dxrad = Rsup[i]-Rinf[i];
595  dxtheta=Rmed[i]*2.0*PI/(real)ns;
596  Vmoy[i] = 0.0;
597  for (j = 0; j < ns; j++) {
598  l = j+i*ns;
599  Vmoy[i] += vt[l];
600  }
601  Vmoy[i] /= (real)ns;
602  }
603  for (i = One_or_active; i < Max_or_active; i++) {
604  dxrad = Rsup[i]-Rinf[i];
605  dxtheta=Rmed[i]*2.0*PI/(real)ns;
606  for (j = 0; j < ns; j++) {
607  l = j+i*ns;
608  if (FastTransport == YES)
609  Vresidual[j] = vt[l]-Vmoy[i]; /* FARGO algorithm */
610  else
611  Vresidual[j] = vt[l]; /* Standard algorithm */
612  }
613  Vresidual[ns]=Vresidual[0];
614  for (j = 0; j < ns; j++) {
615  l = j+i*ns;
616  lip = l+ns;
617  ljp = l+1;
618  if (j == ns-1) ljp = i*ns;
619  cs = soundspeed[l];
620  invdt1 = cs/(min2(dxrad,dxtheta));
621  invdt2 = fabs(vr[l])/dxrad;
622  invdt3 = fabs(Vresidual[j])/dxtheta;
623  dvr = vr[lip]-vr[l];
624  dvt = vt[ljp]-vt[l];
625  if (dvr >= 0.0) dvr = 1e-10;
626  else dvr = -dvr;
627  if (dvt >= 0.0) dvt = 1e-10;
628  else dvt = -dvt;
629  invdt4 = max2(dvr/dxrad,dvt/dxtheta);
630  invdt4*= 4.0*CVNR*CVNR;
631  /*if ( ViscosityAlpha || (VISCOSITY != 0.0) )
632  invdt5 = FViscosity(Rmed[i])*4./pow(min2(dxrad,dxtheta),2);
633  dt = CFLSECURITY/sqrt(invdt1*invdt1+invdt2*invdt2+invdt3*invdt3+\
634  invdt4*invdt4+invdt5*invdt5); */
635  if (!Pebbles) {
636  dt = CFLSECURITY/sqrt(invdt1*invdt1+invdt2*invdt2+invdt3*invdt3+\
637  invdt4*invdt4+invdtreb_sq);
638  } else {
639  dt = CFLSECURITY/sqrt(invdt1*invdt1+invdt2*invdt2+invdt3*invdt3+invdt4*invdt4+invdtpeb_sq+invdtreb_sq);
640  }
641  if (dt < newdt) {
642  newdt = dt;
643  if (debug == YES) {
644  ideb = i;
645  jdeb = j;
646  itdbg1 = 1.0/invdt1; itdbg2=1.0/invdt2;
647  itdbg3=1.0/invdt3; itdbg4=1.0/invdt4;
648  itdbg5=1.0/invdt5;
649  mdtdbg = newdt;
650  viscr = dxrad/dvr/4.0/CVNR/CVNR;
651  visct = dxtheta/dvt/4.0/CVNR/CVNR;
652  }
653  }
654  }
655  }
656  for (i = Zero_or_active; i < MaxMO_or_active; i++) {
657  dt = 2.0*PI*CFLSECURITY/(real)NSEC/fabs(Vmoy[i]*InvRmed[i]-Vmoy[i+1]*InvRmed[i+1]);
658  if (dt < newdt) newdt = dt;
659  }
660  if (deltaT < newdt) newdt = deltaT;
661  if (debug == YES) {
662  printf ("Timestep control information for CPU %d: \n", CPU_Rank);
663  printf ("Most restrictive cell at i=%d and j=%d\n", ideb, jdeb);
664  printf ("located at radius Rmed : %g\n", Rmed[ideb]);
665  printf ("Sound speed limit : %g\n", itdbg1);
666  printf ("Radial motion limit : %g\n", itdbg2);
667  printf ("Residual circular motion limit : %g\n", itdbg3);
668  printf ("Artificial viscosity limit : %g\n", itdbg4);
669  printf (" Arise from r with limit : %g\n", viscr);
670  printf (" and from theta with limit : %g\n", visct);
671  printf ("Physical viscosity limit : %g\n", itdbg5);
672  printf ("Limit time step for this cell : %g\n", mdtdbg);
673  printf ("Limit time step adopted : %g\n", newdt);
674  if (newdt < mdtdbg) {
675  printf ("Discrepancy arise either from shear.\n");
676  printf ("or from the imposed DT interval.\n");
677  }
678  }
679  return (int)(ceil(deltaT/newdt));
680 }
681 
682 /** Updates the sound speed over the mesh. */
683 void ComputeSoundSpeed (Rho, Energy) /* #THORIN */
684 PolarGrid *Rho, *Energy;
685 {
686  int i, j, l, nr, ns;
687  real *rho, *energy, *cs;
688  /* ----- */
689  nr = Rho->Nrad;
690  ns = Rho->Nsec;
691  rho = Rho->Field;
692  energy = Energy->Field;
693  cs = SoundSpeed->Field;
694  for (i=0; i<nr; i++) {
695  for (j=0; j<ns; j++) {
696  l = j + i*ns;
697  if (!EnergyEq) {
698  cs[l] = AspectRatio(Rmed[i])*sqrt(G*1.0/Rmed[i])*pow(Rmed[i], FLARINGINDEX);
699  } else {
700  cs[l] = sqrt( ADIABIND*(ADIABIND-1.0)*energy[l]/rho[l] );
701  }
702  }
703  }
704  mpi_make1Dprofile (cs, globcsvec); /* this will update the radial field which holds the azimuth-averaged values of sound speed */
705 }
706 
707 /** Updates the pressure over the mesh. */
708 void ComputePressureField (Rho, Energy) /* #THORIN */
709 PolarGrid *Rho, *Energy;
710 {
711  int i, j, l, nr, ns;
712  real *rho, *energy, *pressure, *cs;
713  /* ---- */
714  nr = Rho->Nrad;
715  ns = Rho->Nsec;
716  rho = Rho->Field;
717  energy = Energy->Field;
718  pressure = Pressure->Field;
719  cs = SoundSpeed->Field;
720  for (i=0; i<nr; i++) {
721  for (j=0; j<ns; j++) {
722  l = j + i*ns;
723  if (!EnergyEq) {
724  pressure[l] = rho[l]*cs[l]*cs[l];
725  } else {
726  pressure[l] = (ADIABIND-1.0)*energy[l];
727  }
728  }
729  }
730 }
731 
732 /** Updates the temperature over the mesh. */
733 void ComputeTemperatureField (Rho, Energy) /* #THORIN */
734 PolarGrid *Rho, *Energy;
735 {
736  int i, j, l, nr, ns;
737  real tmp1, tmp2;
738  real *rho, *energy, *temperature, *pressure;
739  /* ----- */
740  nr = Rho->Nrad;
741  ns = Rho->Nsec;
742  rho = Rho->Field;
743  energy = Energy->Field;
744  temperature = Temperature->Field;
745  pressure = Pressure->Field;
746  for (i=0; i<nr; i++) {
747  for (j=0; j<ns; j++) {
748  l = j + i*ns;
749  tmp2 = 1.0/(rho[l]*GASCONST);
750  if (EnergyEq) {
751  tmp1 = energy[l]*MOLWEIGHT*(ADIABIND-1.0);
752  temperature[l] = tmp1*tmp2;
753  } else {
754  tmp1 = MOLWEIGHT*pressure[l];
755  temperature[l] = tmp1*tmp2;
756  }
757  }
758  }
759 }
void ImposeKeplerianEdges()
double real
Definition of the type 'real' used throughout the code.
Definition: types.h:20
real InvDiffRmed[MAX1D]
Definition: global.h:12
real MASSTAPER
Definition: param_noex.h:16
real SIGMASLOPE
Definition: param_noex.h:27
real Rsup[MAX1D]
Definition: global.h:11
void AlgoGas(PolarGrid *Rho, PolarGrid *Vrad, PolarGrid *Vtheta, PolarGrid *Energy, PolarGrid *Label, PlanetarySystem *sys, struct reb_simulation *rsim)
Definition: SourceEuler.c:199
int TimeStep
Definition: global.h:23
void ImplicitRadiativeDiffusion(PolarGrid *Rho, PolarGrid *EnergyInt, PolarGrid *EnergyNew, real dt)
The main numerical solver of the energy equation.
PolarGrid * Temperature
Definition: global.h:35
#define YES
Definition: types.h:46
PolarGrid * Qplus
Definition: global.h:35
void Transport()
int ConditionCFL(PolarGrid *Vrad, PolarGrid *Vtheta, real deltaT)
Definition: SourceEuler.c:574
real EnergyMed[MAX1D]
Definition: global.h:16
real DT
Definition: param_noex.h:7
boolean PrescribedAccretion
Definition: global.h:27
PolarGrid * TAURR
Definition: global.h:36
real max2(real a, real b)
Definition: SourceEuler.c:173
void SynchronizeFargoRebound()
PolarGrid * PebbleGravAccelTheta
Definition: global.h:39
real invdtreb_sq
Definition: global.h:19
#define MARK
Definition: types.h:59
char OUTPUTDIR[512]
Definition: param_noex.h:11
#define GET
Definition: types.h:58
PolarGrid * TAURP
Definition: global.h:36
PolarGrid * CreatePolarGrid(int Nr, int Ns, char *name)
Definition: LowTasks.c:73
PolarGrid * SoundSpeed
Definition: global.h:35
void SetWaveKillingZones()
Sets the wave-killing factors within the damping zones; inspired by de Val-Borro et al...
Definition: SideEuler.c:242
real InvRinf[MAX1D]
Definition: global.h:13
static PolarGrid * VthetaInt
Definition: SourceEuler.c:25
PolarGrid * DragForceTheta
Definition: global.h:37
static PolarGrid * VthetaNew
Definition: SourceEuler.c:25
int Zero_or_active
Definition: global.h:6
real OmegaInv[MAX1D]
Definition: global.h:15
A structure used to store any scalar fied on the computational domain.
Definition: types.h:37
real GlobalRmed[MAX1D]
Definition: global.h:13
real min2(real a, real b)
Definition: SourceEuler.c:166
boolean DampInit
Definition: global.h:24
real Rmed2[MAX1D]
Definition: global.h:15
real * Field
Definition: types.h:40
static real timeCRASH
Definition: SourceEuler.c:26
real InvSurf[MAX1D]
Definition: global.h:12
static PolarGrid * VradNew
Definition: SourceEuler.c:24
int Nrad
Radial size of the grid, in number of zones.
Definition: types.h:38
real GetPsysInfoFromRsim()
int One_or_active
Definition: global.h:8
real Rmed[MAX1D]
Definition: global.h:11
void SubStep1Pebbles(PolarGrid *Vrad, PolarGrid *Vtheta, real dt)
Applies a semi-implicit method to evolve the pebbles dynamically.
Definition: Pebbles.c:590
Contains all the information about a planetary system at a given instant in time. ...
Definition: types.h:96
static PolarGrid * VradInt
Definition: SourceEuler.c:24
void ComputeSoundSpeed(PolarGrid *Rho, PolarGrid *Energy)
Updates the sound speed over the mesh.
Definition: SourceEuler.c:683
void InitGasVelocity(PolarGrid *Vr, PolarGrid *Vt)
Definition: Pframeforce.c:568
PolarGrid * TAUPP
Definition: global.h:36
int MaxMO_or_active
Definition: global.h:9
real invdtpeb_sq
Definition: global.h:19
real Radii[MAX1D]
Definition: global.h:13
static int GasTimeStepsCFL
Definition: SourceEuler.c:31
real SQRT_ADIABIND_INV
Definition: global.h:19
static real a[7]
Definition: EnergySources.c:27
real CoolingTimeMed[MAX1D]
Definition: global.h:17
boolean EnergyEq
Definition: global.h:24
real PhysicalTime
Definition: global.h:20
real AspectRatio()
boolean Corotating
Definition: Interpret.c:30
boolean Damping
Definition: global.h:24
void FillSigma()
Definition: Theo.c:25
void CorrectPebblesVtheta(real domega)
Corrects the azimuthal flow velocity of pebbles to keep up with the frame rotation.
Definition: Pebbles.c:516
int IMIN
Definition: global.h:4
boolean FastTransport
Definition: Interpret.c:29
void PebbleStokesNumbers(PolarGrid *Rho)
Calculates the local Stokes number using the dominant pebble size, local gas density and parametric p...
Definition: Pebbles.c:240
void InitViscosity()
Definition: Viscosity.c:67
real InvDiffRsup[MAX1D]
Definition: global.h:13
real globcsvec[MAX1D]
Definition: global.h:16
void AccreteOntoPlanets(PolarGrid *Rho, PolarGrid *Vrad, PolarGrid *Vtheta, real dt, PlanetarySystem *sys)
Definition: Planet.c:15
#define MPI_MAX
Definition: mpi_dummy.h:16
int GLOBALNRAD
Definition: global.h:10
void RotatePsys()
PolarGrid * GravAccelRad
Definition: global.h:39
void ComputePressureField(PolarGrid *Rho, PolarGrid *Energy)
Updates the pressure over the mesh.
Definition: SourceEuler.c:708
real FLARINGINDEX
Definition: param_noex.h:39
#define G
Definition: fondam.h:11
boolean TorqueDensity
Definition: global.h:28
void UpdateDivVelocAndStressTensor()
void AccretePebblesOntoPlanets(PlanetarySystem *sys, PolarGrid *Rho, PolarGrid *Energy, PolarGrid *Vtheta, real dt)
Finds the amount of pebbles to be transfered from the pebble disk onto the planets.
Definition: Pebbles.c:312
#define CFLSECURITY
Definition: SourceEuler.c:16
real FViscosity()
void InitEuler(PolarGrid *Rho, PolarGrid *Vr, PolarGrid *Vt, PolarGrid *En)
Definition: SourceEuler.c:122
PolarGrid * PebbleGravAccelRad
Definition: global.h:39
boolean DetectCrashPebbles()
Safety check for negative pebble densities.
Definition: Pebbles.c:753
void MinStepForRebound()
PolarGrid * DragForceRad
Definition: global.h:37
boolean SloppyCFL
Definition: global.h:31
real OmegaFrame
Definition: global.h:20
void MPI_Allreduce(void *ptr, void *ptr2, int count, int type, int foo3, int foo4)
Definition: mpi_dummy.c:72
void ComputeTemperatureField(PolarGrid *Rho, PolarGrid *Energy)
Updates the temperature over the mesh.
Definition: SourceEuler.c:733
void FillEnergy()
Definition: Theo.c:76
void FillForcesArrays(PolarGrid *Rho, PlanetarySystem *sys)
Using the vertical averaging procedure of Muller & Kley (2012), calculates the acceleration in planet...
Definition: Pframeforce.c:38
void InitGasDensityEnergy(PolarGrid *Rho, PolarGrid *Energy)
Part of the initialisation.
Definition: Pframeforce.c:544
int NSEC
Definition: param_noex.h:19
void ParametricAccretion(PlanetarySystem *sys, real dt)
Writes filtering factors if needed.
Definition: Pebbles.c:779
#define MPI_INT
Definition: mpi_dummy.h:14
#define GASCONST
Definition: fondam.h:17
int Max_or_active
Definition: global.h:7
void SubStep3(PolarGrid *Rho, real dt)
Numerical step reponsible for the energy update.
Definition: SourceEuler.c:493
void SubStep2(PolarGrid *Rho, PolarGrid *Energy, real dt)
Definition: SourceEuler.c:402
#define MOLWEIGHT
Definition: fondam.h:18
real RMIN
Definition: param_noex.h:20
PolarGrid * Pressure
Definition: global.h:35
real Surf[MAX1D]
Definition: global.h:11
void SubStep1(PolarGrid *Vrad, PolarGrid *Vtheta, PolarGrid *Rho, real dt)
Definition: SourceEuler.c:304
#define NO
Definition: types.h:47
void UpdateVelocityWithViscousTerms()
PolarGrid * RhoStar
Definition: global.h:34
real RMAX
Definition: param_noex.h:21
static PolarGrid * TemperInt
Definition: SourceEuler.c:23
void mastererr(const char *template,...)
Definition: LowTasks.c:49
real ADIABIND
Definition: param_noex.h:54
int NRAD
Definition: param_noex.h:18
PolarGrid * GravAccelTheta
Definition: global.h:39
real QplusMed[MAX1D]
Definition: global.h:17
PolarGrid * DivergenceVelocity
Definition: global.h:36
static real domega
Definition: EnergySources.c:40
void InitRadiatDiffusionFields()
Initialises the polar arrays associated with the heating/cooling processes.
Definition: EnergySources.c:46
void FillVtheta()
void ParticleDiffusion(PolarGrid *Rho)
Applies the particle diffusion term acting on pebbles.
Definition: Pebbles.c:629
boolean DetectCrash(PolarGrid *array)
Definition: SourceEuler.c:37
void FillPolar1DArrays()
Definition: SourceEuler.c:57
Contains all the include directives requested by the code.
real Rinf[MAX1D]
Definition: global.h:11
real IMPOSEDDISKDRIFT
Definition: param_noex.h:38
int Nsec
Azimuthal size of the grid, in number of zones.
Definition: types.h:39
real SigmaMed[MAX1D]
Definition: global.h:14
void InitTransport()
boolean IsDisk
Definition: Interpret.c:30
void CriticalCharTime(PolarGrid *Vrad, PolarGrid *Vtheta)
Restricts the time step using the CFL condition for the pebble fluid.
Definition: Pebbles.c:702
static real b[7]
Definition: EnergySources.c:28
void AdvanceSystemFromDisk(PolarGrid *Rho, PlanetarySystem *sys, real dt)
Updates the planet velocities due to disk forces.
Definition: Pframeforce.c:448
boolean LogGrid
Definition: global.h:41
int CPU_Rank
Definition: global.h:1
static PolarGrid * EnergyNew
Definition: SourceEuler.c:29
boolean BackReaction
Definition: global.h:27
real Energy()
static PolarGrid * EnergyInt
Definition: SourceEuler.c:29
void EvolvePebbleDisk(real dt)
Calls the transport routines and applies the boundary conditions for pebbles.
Definition: Pebbles.c:675
void ActualiseGas(PolarGrid *array, PolarGrid *newarray)
Definition: SourceEuler.c:181
boolean ParametricCooling
Definition: global.h:24
void AdvanceSystemRebound()
void CommunicateBoundaries(PolarGrid *Density, PolarGrid *Vrad, PolarGrid *Vtheta, PolarGrid *Energy, PolarGrid *Label)
Definition: commbound.c:47
PolarGrid * GasAccelrad
Definition: global.h:37
boolean debug
Definition: global.h:29
real GetPsysInfo()
real InvRmed[MAX1D]
Definition: global.h:12
static int AlreadyCrashed
Definition: SourceEuler.c:31
PolarGrid * GasAcceltheta
Definition: global.h:37
PolarGrid * Torque
Definition: global.h:40
void WriteDiskPolar(PolarGrid *array, int number)
Definition: Output.c:153
void SourceTermsPebbles(PolarGrid *Vrad, PolarGrid *Vtheta, real dt)
Calculates the source terms acting on pebbles.
Definition: Pebbles.c:523
void InitPebbleArrays()
Initialise polar arrays associated with the pebble disk.
Definition: Pebbles.c:37
void CorrectVtheta()
#define CVNR
Definition: SourceEuler.c:19
void ApplyBoundaryCondition()
void mpi_make1Dprofile(real *gridfield, real *axifield)
Definition: mpiTasks.c:12
boolean CPU_Master
Definition: global.h:3
PolarGrid * RhoInt
Definition: global.h:34
#define PI
Definition: fondam.h:12
void SynchronizePebbleDisc()
Synchronises pebble fluid hydrodynamic quantities among the overlapping grid zones.
Definition: Pebbles.c:685
#define MPI_COMM_WORLD
Definition: mpi_dummy.h:10
void prs_exit(int numb)
Definition: LowTasks.c:33
#define MAX1D
Definition: types.h:65
real MassTaper
Definition: global.h:14
void masterprint(const char *template,...)
Definition: LowTasks.c:40
boolean Pebbles
Definition: global.h:27
boolean DiffusiveParticles
Definition: global.h:27