The FARGO_THORIN code developer's guide
 All Data Structures Files Functions Variables Typedefs Macros
Pebbles.c
Go to the documentation of this file.
1 /**
2  * @file Pebbles.c
3  *
4  * @brief Contains functions reponsible for the pebble disk initialisation,
5  * evolution due to source terms and pebble accretion. Also controls several outputs.
6  *
7  * @author Ondřej Chrenko <chrenko@sirrah.troja.mff.cuni.cz>
8  *
9  * @details The pebble disk equilibrium model is inspired by
10  * Lambrechts & Johansen (2014). The evolution follows a standard
11  * two-fluid approximation with linear drag coupling and particle diffusion.
12  *
13  * @section LICENSE
14  * Copyright (c) 2017 Ondřej Chrenko. See the LICENSE file of the
15  * distribution.
16  *
17  */
18 
19 #include "fargo.h"
20 
21 #define TRAPEZMAX 35
22 #define TRAPEZEPS 1.0e-7
23 
24 extern boolean Restart, FastTransport;
25 
30 
32 // static real filtering[50];
33 
34 // 2DO check everything here if it can work in isothermal case
35 
36 /** Initialise polar arrays associated with the pebble disk */
38 {
39  PebbleDens = CreatePolarGrid (NRAD, NSEC, "pebbledens");
40  PebbleVrad = CreatePolarGrid (NRAD, NSEC, "pebblevrad");
41  PebbleVtheta = CreatePolarGrid (NRAD, NSEC, "pebblevtheta");
42  PebbleSize = CreatePolarGrid (NRAD, NSEC, "pebblesize");
43  StokesNumber = CreatePolarGrid (NRAD, NSEC, "pebblestokes");
44  PebbleDensTemp= CreatePolarGrid (NRAD, NSEC, "pebbledensitytemp");
45  PebbleAccelrad= CreatePolarGrid (NRAD, NSEC, "pebbleaccelrad");
46  PebbleAcceltheta= CreatePolarGrid (NRAD, NSEC, "pebbleacceltheta");
47  GasAccelrad = CreatePolarGrid (NRAD, NSEC, "gasaccelrad");
48  GasAcceltheta = CreatePolarGrid (NRAD, NSEC, "gasacceltheta");
49  EtaFaceCentered=CreatePolarGrid (NRAD, NSEC, "etafacecentered");
50  EtaCellCentered=CreatePolarGrid (NRAD, NSEC, "eta");
51  AccretedMass = CreatePolarGrid (NRAD, NSEC, "accretedmass");
52  DragForceRad = CreatePolarGrid (NRAD, NSEC, "dragforcerad");
53  DragForceTheta= CreatePolarGrid (NRAD, NSEC, "createpolargrid");
55 }
56 
57 /** Sets up a pebble disk in a coagulation-drift equilibrium */
58 void EquilPebbleDisk (Rho, Vrad, Vtheta)
59 PolarGrid *Rho, *Vrad, *Vtheta;
60 {
61  EtaPressureSupport (Vtheta);
62  InitPebblesViaFlux (Rho, Vrad);
63 }
64 
65 /** Reads arrays necessary to restart the pebble disk */
66 void RestartPebbleDisk (Rho, index)
67 PolarGrid *Rho;
68 int index;
69 {
70  ReadfromFile (PebbleDens, "gaspebbledens", index);
71  ReadfromFile (PebbleSize, "gaspebblesize", 0); // sidenote: stokes number will be calculated in AlgoGas()
72  ReadfromFile (PebbleVrad, "gaspebblevrad", index);
73  ReadfromFile (PebbleVtheta, "gaspebblevtheta", index);
74  BckpFieldsForBC ();
75 }
76 
77 /** Imposing the radial mass flux of pebbles in a
78  * coagulation-drift equilibrium, initialises their
79  * surface density, flow velocity and local Stokes numbers */
80 void InitPebblesViaFlux (Rho, Vrad)
81 PolarGrid *Rho, *Vrad;
82 {
83  static real pebbleflux=0.0;
84  int i,j,l,nr,ns;
85  real *rho, *vr, *etafc, *etacc, *psize, *tau, *pdens, *pvr, *pvt;
86  real rho1, vr1, etafc1, etacc1; // index 1 is for azimuthally averaged vars
87  real pdens1, psize1, tau1, pvr1, pvt1;
88  real vtcorr, vkepl, fac, etaterm, pvrtemp;
89  char command[1024];
90  if (pebbleflux==0.0) pebbleflux = PEBBLEFLUX*FLUX2CU;
91  pdens = PebbleDens->Field;
92  tau = StokesNumber->Field;
93  psize = PebbleSize->Field;
94  pvr = PebbleVrad->Field;
95  pvt = PebbleVtheta->Field;
96  rho = Rho->Field;
97  vr = Vrad->Field;
98  etafc = EtaFaceCentered->Field;
99  etacc = EtaCellCentered->Field;
100  nr = Rho->Nrad;
101  ns = Rho->Nsec;
102  for (i=0; i<nr; i++) {
103  // prepare azimuthally averaged quantities for constructing 1D radial profiles
104  rho1 = 0.0;
105  vr1 = 0.0;
106  etafc1 = 0.0;
107  etacc1 = 0.0;
108  for (j=0; j<ns; j++) {
109  l = j + i*ns;
110  rho1 += rho[l];
111  vr1 += vr[l];
112  etafc1 += etafc[l];
113  etacc1 += etacc[l];
114  }
115  rho1 /= (real)ns;
116  vr1 /= (real)ns;
117  etafc1 /= (real)ns;
118  etacc1 /= (real)ns;
119  // 1D steady-state profiles
120  tau1 = sqrt(3.0)*PEBBLECOAGULATION*pebbleflux/(32.0*PI*pow(Rmed[i],-1.5)*rho1); // dominant Stokes number from 2 parameters + hydro quantities
121  tau1 = sqrt(tau1)/(Rmed[i]*etacc1);
122  psize1 = sqrt(ADIABIND)*tau1*rho1/(2.0*pebbulkdens); // pebble size corresponding to the dominant Stokes from Epstein law
123  vtcorr = Rmed[i]*OmegaFrame; // steady-state drift velocity part, serves also as the initialization |--------->
124  vkepl = pow (Rmed[i], -0.5);
125  fac = 1.0/(tau1*tau1 + 1.0);
126  etaterm = etafc1*vkepl;
127  pvr1 = -2.0*tau1*fac*(etaterm - 0.5/tau1*vr1); // initial radial velocity (face centered)
128  etaterm = etacc1*vkepl;
129  pvrtemp = -2.0*tau1*fac*(etaterm - 0.5/tau1*vr1); // radial velocity in the cell centre (will be used for the flux estimate)
130  pvt1 = -fac*(etaterm - 0.5*tau1*vr1); // initial azim. velocity
131  pvt1 += (vkepl - vtcorr); // transform to corot. <---------|
132  pdens1 = pebbleflux/(2.0*PI*Rmed[i]*fabs(pvrtemp));// having the drift velocity, get the density from flux conservation
133  // fill HD pebble arrays with 1D profiles
134  for (j=0; j<ns; j++) {
135  l = j + i*ns;
136  psize[l] = psize1;
137  tau[l] = tau1;
138  pdens[l] = pdens1;
139  pvr[l] = pvr1;
140  pvt[l] = pvt1;
141  }
142  // bckp for boundary conditions
143  PebDensInit[i] = pdens1;
144  PebVradInit[i] = pvr1;
145  PebVthetaInit[i] = pvt1 + Rmed[i]*OmegaFrame; // transform back to intertial value
146  }
148  WriteDiskPolar (PebbleSize, 0);
149  if (Merge && (CPU_Number > 1)) {
150  if (!CPU_Master) return;
151  for (i=1; i<CPU_Number; i++) {
152  sprintf (command, "cd %s; cat gaspebblestokes0.dat.%05d >> gaspebblestokes0.dat",\
153  OUTPUTDIR, i);
154  system (command);
155  sprintf (command, "cd %s; cat gaspebblesize0.dat.%05d >> gaspebblesize0.dat",\
156  OUTPUTDIR, i);
157  system (command);
158  }
159  sprintf (command, "cd %s; rm -f gaspebblestokes0.dat.0*", OUTPUTDIR);
160  system (command);
161  sprintf (command, "cd %s; rm -f gaspebblesize0.dat.0*", OUTPUTDIR);
162  system (command);
163  }
164 }
165 
166 /** Backs up the initial state of the pebble disk
167  * to impose damping boundary conditions later. */
169 {
170  real *pdens, *pvr, *pvt;
171  int nr, ns, i, j, l;
172  pdens = PebbleDens->Field;
173  pvr = PebbleVrad->Field;
174  pvt = PebbleVtheta->Field;
175  nr = PebbleVrad->Nrad;
176  ns = PebbleVtheta->Nsec;
177  for (i=0; i<nr; i++) {
178  PebDensInit[i] = 0.0;
179  PebVradInit[i] = 0.0;
180  PebVthetaInit[i] = 0.0;
181  for (j=0; j<ns; j++) {
182  l = j + i*ns;
183  PebDensInit[i] += pdens[l];
184  PebVradInit[i] += pvr[l];
185  PebVthetaInit[i] += pvt[l];
186  }
187  PebDensInit[i] /= (real)ns;
188  PebVradInit[i] /= (real)ns;
189  PebVthetaInit[i] /= (real)ns;
190  PebVthetaInit[i] += Rmed[i]*OmegaFrame;
191  }
192 }
193 
194 /** Calculates tha gas rotation parameter eta */
195 void EtaPressureSupport (Vtheta)
196 PolarGrid *Vtheta;
197 {
198  int i, j, l, lim, ljp, nr, ns;
199  real vtcorr, vkinv, vtcc;
200  real *vt, *etafc, *etacc;
201  /* ----- */
202  nr = Vtheta->Nrad;
203  ns = Vtheta->Nsec;
204  vt = Vtheta->Field;
205  etafc = EtaFaceCentered->Field;
206  etacc = EtaCellCentered->Field;
207 #pragma omp parallel default(none) \
208  shared (nr,ns,Rmed,OmegaFrame,vt,etacc,etafc) \
209  private (i,j,l,vtcorr,vkinv,ljp,lim,vtcc)
210  {
211 #pragma omp for
212  for (i=0; i<nr; i++) {
213  vtcorr = Rmed[i]*OmegaFrame;
214  vkinv = sqrt(Rmed[i]);
215  for (j=0; j<ns; j++) {
216  l = j + i*ns;
217  ljp = l+1;
218  if (j == ns-1) ljp = i*ns;
219  vtcc = 0.5*(vt[l]+vt[ljp]);
220  etacc[l] = 1.0 - (vtcc + vtcorr)*vkinv;
221  }
222  }
223 #pragma omp for
224  for (i=1; i<nr; i++) {
225  for (j=0; j<ns; j++) {
226  l = j + i*ns;
227  lim = l - ns;
228  etafc[l] = 0.5*(etacc[l]+etacc[lim]);
229  }
230  }
231  } // #end of omp parallel section
232  for (j=0; j<ns; j++) { // adopt nearest values when missing
233  etafc[j] = etafc[j+ns];
234  }
235 }
236 
237 /** Calculates the local Stokes number using the
238  * dominant pebble size, local gas density and parametric
239  * pebble material density. */
241 PolarGrid *Rho;
242 {
243  int nr, ns, i, j, l;
244  real *rho, *tau, *psize;
245  /* ----- */
246  nr = Rho->Nrad;
247  ns = Rho->Nsec;
248  rho = Rho->Field;
249  tau = StokesNumber->Field;
250  psize = PebbleSize->Field;
251 #pragma omp parallel for default(none) \
252  shared (nr,ns,tau,pebbulkdens,psize,rho,SQRT_ADIABIND_INV) \
253  private (i,j,l)
254  for (i=0; i<nr; i++) {
255  for (j=0; j<ns; j++) {
256  l = j + i*ns;
257  tau[l] = 2.0*pebbulkdens*psize[l]/rho[l]*SQRT_ADIABIND_INV; // given the pebble size, recalculate tau from local density (Epstein)
258  }
259  }
260 }
261 
262 /** Trapezoidal rule to integrate the column mass
263  * of pebbles located within the accretion radius.
264  * Adopted from Numerical Recipes. */
265 real Trapzd (n, a, b, H2)
266 int n;
267 real a, b, H2;
268 {
269  static real integral;
270  real iter, spacing, x, sum;
271  int j;
272  if (n==1) {
273  integral = 0.5*(b-a)*(exp(-0.5*a*a/H2) + exp(-0.5*b*b/H2));
274  return integral;
275  } else {
276  iter = pow(2.0,(real)(n-2));
277  spacing = (b-a)/iter;
278  x = a + 0.5*spacing;
279  sum = 0.0;
280  for (j=1; j<=(int)iter; j++) {
281  sum += exp(-0.5*x*x/H2);
282  x += spacing;
283  }
284  integral = 0.5*(integral + (b-a)*sum/iter);
285  return integral;
286  }
287 }
288 
289 /** Primitive algorithm to find the mass in a
290  * vertical column of pebbles overlapping
291  * the accretion radius. */
293 real a, b, Hpeb;
294 {
295  int n;
296  real integral, iold, H2;
297  H2 = Hpeb*Hpeb;
298  for (n=1; n<=TRAPEZMAX; n++) {
299  integral = Trapzd (n,a,b,H2);
300  if (n > 5) { // avoid early convergence
301  if (fabs(integral-iold) < TRAPEZEPS*fabs(iold) || (integral == 0.0 && iold == 0.0)) return integral;
302  }
303  iold = integral;
304  }
305  masterprint ("Warning! Integration of the column mass did not converge. Terminating now...\n");
306  prs_exit(1);
307  return 0.0; // will never get here
308 }
309 
310 /** Finds the amount of pebbles to be transfered
311  * from the pebble disk onto the planets */
312 void AccretePebblesOntoPlanets (sys, Rho, Energy, Vtheta, dt)
313 PlanetarySystem *sys;
314 PolarGrid *Rho, *Energy, *Vtheta;
315 real dt;
316 {
317  real *pdens, *abs, *ord;
318  real *pvr, *pvt, *tau, *cs, *vt, *macc;
319  int nr,ns,k,i,j,l,imin,imax,ncell,tempint;
320  real dMplanet, dPXplanet, dPYplanet;
321  real Xplanet, Yplanet, Zplanet, VXplanet, VYplanet, VZplanet, Mplanet, PXplanet, PYplanet, Rplanet, Rplanet3D, Omegaplanet;
322  real vtcorr, xcell, ycell, vrcell, vtcell, vxcell, vycell, flow, flowx, flowy, vrel;
323  real ifrac, frac, Mt, Rbondi, tbondi, fac, Reff=0.0, Reff2, Rhill;
324  real R2, H, Hpeb, zmin, zmax, dM, mcell, voldenspeb, temp;
325  real ang, plradius, plrho, dE, L, tdelay, taper;
326  real tau_mean, dM_upper, Hpeb_mean, pdens_mean, veloc, dM_model, limiter;
327  /* ----- */
328  pdens = PebbleDens->Field;
329  nr = PebbleDens->Nrad;
330  ns = PebbleDens->Nsec;
331  abs = CellAbscissa->Field;
332  ord = CellOrdinate->Field;
333  pvr = PebbleVrad->Field;
334  pvt = PebbleVtheta->Field;
335  tau = StokesNumber->Field;
336  cs = SoundSpeed->Field;
337  macc = AccretedMass->Field;
338  vt = Vtheta->Field;
339  if (AccretHeating) heatsrc_max = sys->nb;
340  mpi_make1Dprofile (vt, vt1D);
341  for (k=0; k<sys->nb; k++) {
342  dMplanet = dPXplanet = dPYplanet = 0.0;
343  Xplanet = sys->x[k];
344  Yplanet = sys->y[k];
345  Zplanet = sys->z[k];
346  VXplanet = sys->vx[k];
347  VYplanet = sys->vy[k];
348  VZplanet = sys->vz[k];
349  Mplanet = sys->mass[k];
350  PXplanet = VXplanet*Mplanet;
351  PYplanet = VYplanet*Mplanet;
352  Rplanet = Xplanet*Xplanet + Yplanet*Yplanet;
353  Rplanet3D = sqrt(Rplanet + Zplanet*Zplanet);
354  Rplanet = sqrt(Rplanet);
355  Rhill = Rplanet3D*pow(1.0/3.0*Mplanet, 1.0/3.0);
356  Omegaplanet = pow(Rplanet, -1.5); // transition mass calculation on global grid --->
357  ifrac = GetGlobalIFrac (Rplanet); // get radial index w.respect to the global grid
358  frac = ifrac-floor(ifrac);
359  i = (int)ifrac;
360  flow = vt1D[i]*(1.0-frac) + vt1D[i+1]*frac + Rplanet*OmegaFrame; // flow of gas at planet's orbit (v_rad negleted, azim.average of v_theta, from corot. to inertial)
361  flowx = -flow*Yplanet/Rplanet; // conversion from theta to (x,y)
362  flowy = flow*Xplanet/Rplanet;
363  vrel = sqrt((VXplanet-flowx)*(VXplanet-flowx) + (VYplanet-flowy)*(VYplanet-flowy) + VZplanet*VZplanet); // headwind experienced by planet
364  Mt = sqrt(1.0/3.0)*pow(vrel,3.0)/Omegaplanet; // <---
365  imax = Max_or_active-1; // set range of cells on current CPU that should be checked for accretion --->
366  imin = Zero_or_active;
367  while ( Rinf[imax] > (Rplanet+Rhill) && imax >= imin) imax--; // if at the end (imin>imax), it means that current CPU does not contain the critical sphere
368  while ( Rsup[imin] < (Rplanet-Rhill) && imin <= imax) imin++; // and the loop below won't do anything <---
369  // STEP 1 - find average tau through the annulus spanned by the Hill sphere
370  ncell = 0;
371  tau_mean = 0.0;
372  for (i=imin; i<=imax; i++) {
373  for (j=0; j<ns; j++) {
374  l = j + i*ns;
375  tau_mean += tau[l];
376  ncell++;
377  }
378  }
379  MPI_Allreduce(&tau_mean, &temp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
380  tau_mean = temp;
381  MPI_Allreduce(&ncell, &tempint, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
382  ncell = tempint;
383  tau_mean /= (real)ncell;
384  // STEP 2 - using tau calculated above, find the effective accretion radius
385  if (Mplanet < Mt) { // Bondi regime
386  Rbondi = Mplanet/vrel/vrel; // insert vrel from the headwind experienced by planet
387  tbondi = Rbondi/vrel;
388  fac = sqrt(tau_mean*pow(Rplanet, 1.5)/tbondi); // tau/Omega_kepl = t_stopping
389  Reff = Rbondi*fac;
390  } else { // Hill regime
391  fac = pow(tau_mean/0.1, 1.0/3.0);
392  Reff = Rhill*fac;
393  }
394  if (Reff > Rhill) Reff = Rhill; // can't get bigger than Hill (just to be safe)
395  Reff2 = Reff*Reff;
396  // STEP 3 - find the cells below Reff, find the UPPER LIMIT for ACCRETED MASS
397  dM_upper = 0.0;
398  Hpeb_mean = 0.0;
399  pdens_mean = 0.0;
400  ncell = 0;
401  for (i=imin; i<=imax; i++) {
402  vtcorr = Rmed[i]*OmegaFrame;
403  for (j=0; j<ns; j++) {
404  l = j + i*ns;
405  xcell = abs[l];
406  ycell = ord[l];
407  R2 = (xcell-Xplanet)*(xcell-Xplanet) + (ycell-Yplanet)*(ycell-Yplanet); // midplane separation cell vs planet
408  if (R2 <= Reff2) {
409  H = cs[l]*OmegaInv[i]*SQRT_ADIABIND_INV;
410  Hpeb = H*sqrt(PEBBLEALPHA/tau[l]);
411  zmin = Zplanet - sqrt(Reff2 - R2); // intersections between the vertical column of pebbles and the hill sphere
412  zmax = Zplanet + sqrt(Reff2 - R2);
413  mcell = Surf[i]*pdens[l];
414  voldenspeb = pdens[l]/Hpeb*SQRT2PI_INV;
415  dM = voldenspeb*Surf[i]*IntegrateColumnMass (zmin,zmax,Hpeb);
416  if (dM > mcell) dM = 0.99999999*mcell;
417  macc[l] = dM; // this is the UPPER LIMIT for the mass transported from a specific cell onto the planet
418  dM_upper += dM;
419  Hpeb_mean += Hpeb;
420  pdens_mean += pdens[l];
421  ncell++;
422  }
423  }
424  }
425  MPI_Allreduce(&dM_upper, &temp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
426  dM_upper = temp;
427  MPI_Allreduce(&Hpeb_mean, &temp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
428  Hpeb_mean = temp;
429  MPI_Allreduce(&pdens_mean, &temp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
430  pdens_mean = temp;
431  MPI_Allreduce(&ncell, &tempint, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
432  ncell = tempint;
433  Hpeb_mean /= (real)ncell;
434  pdens_mean /= (real)ncell;
435  // STEP 4 - using average values, find the MODEL ACCRETION RATE to reduce the upper
436  // limit from STEP 3 (formulae from e.g. Morbidelli et al. 2015)
437  if (Mplanet < Mt) { // Bondi or Hill regime
438  veloc = vrel; // headwind as the relative velocity
439  } else {
440  veloc = Reff*Omegaplanet; // shear velocity
441  }
442  if (Hpeb_mean < Reff) { // 2D or 3D regime
443  dM_model = 2.0*Reff*veloc*pdens_mean;
444  } else {
445  dM_model = PI*Reff*Reff*veloc*pdens_mean/Hpeb_mean*SQRT2PI_INV;
446  }
447  dM_model *= dt; // !!! MUST convert the accretion rate to mass change
448  if (dM_upper > dM_model) { // limit the accretion rate not to exceed the model rate
449  limiter = dM_model/dM_upper;
450  } else {
451  limiter = 1.0;
452  }
453  // STEP 5 - apply the limiter and finish the accretion
454  for (i=imin; i<=imax; i++) {
455  vtcorr = Rmed[i]*OmegaFrame;
456  for (j=0; j<ns; j++) {
457  l = j + i*ns;
458  if (macc[l] > 0.0) { // cells below Rhill have non-zero 'macc'
459  xcell = abs[l];
460  ycell = ord[l];
461  vrcell = pvr[l];
462  vtcell = pvt[l] + vtcorr; // from corot. to inertial
463  vxcell = (vrcell*xcell - vtcell*ycell)*InvRmed[i];
464  vycell = (vrcell*ycell + vtcell*xcell)*InvRmed[i];
465  dM = macc[l]*limiter; // limiter applied here
466  macc[l] = 0.0; // !!! important so that the if-condition works properly next time
467  mcell = Surf[i]*pdens[l];
468  pdens[l] = (mcell - dM)/Surf[i]; // new surface density
469  if (pdens[l] < 0.0) pdens[l] = 1.e-20; // density floor
470  dMplanet += dM;
471  dPXplanet += dM*vxcell;
472  dPYplanet += dM*vycell;
473  }
474  }
475  }
476  MPI_Allreduce (&dMplanet, &temp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
477  dMplanet = temp;
478  MPI_Allreduce (&dPXplanet, &temp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
479  dPXplanet = temp;
480  MPI_Allreduce (&dPYplanet, &temp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
481  dPYplanet = temp;
482  // OPTIONAL STEP 6 - account for the accretion heating
483  if (AccretHeating) {
484  heatsrc_index[k] = -1;
485  if (Rplanet >= Rinf[0] && Rplanet <= Rsup[nr-1]) {
486  tdelay = 4.0*DT*(real)HEATINGDELAY;
487  taper = PhysicalTime/tdelay;
488  if (taper > 1.0) taper = 1.0;
489  plrho = PLANETARYDENSITY/RHO2CGS;
490  plradius = pow (3.0*Mplanet/(4.0*PI*plrho), 1.0/3.0);
491  L = Mplanet*dMplanet/dt/plradius; // see Benitez et al. (2015); G=1; and we work in terms of LUMINOSITY
492  ang = atan2(Yplanet,Xplanet);
493  if (ang < 0.0) ang += 2.0*PI;
494  i = 0;
495  while (Rsup[i] < Rplanet) i++;
496  j = floor((real)ns/2.0/PI*ang + 0.5);
497  if (j==ns) j=0;
498  l = j + i*ns;
499  heatsrc_index[k] = l;
500  dE = L*taper/Surf[i]; // final dimension: W/m^2 -> average power density in cells closest to the planet
501  heatsrc[k] = dE;
502  }
503  }
504  // STEP 7 - Update the planet
505  PXplanet += dPXplanet;
506  PYplanet += dPYplanet;
507  Mplanet += dMplanet;
508  sys->mass[k] = Mplanet;
509  sys->vx[k] = PXplanet/Mplanet;
510  sys->vy[k] = PYplanet/Mplanet;
511  }
512 }
513 
514 /** Corrects the azimuthal flow velocity
515  * of pebbles to keep up with the frame rotation. */
517 real domega;
518 {
519  CorrectVtheta (PebbleVtheta, domega);
520 }
521 
522 /** Calculates the source terms acting on pebbles */
523 void SourceTermsPebbles (Vrad,Vtheta,dt)
524 PolarGrid *Vrad, *Vtheta;
525 real dt;
526 {
527  real *pvr, *pvt, *tau, *paccr, *pacct, *vr, *vt, *pdens, *dragr, *dragt;
528  real *pagr, *pagt;
529  real Omega, vt2, drag;
530  int nr, ns, i, j, l, lim, ljm, ljp;
531  pdens = PebbleDens->Field;
532  pvr = PebbleVrad->Field;
533  pvt = PebbleVtheta->Field;
534  tau = StokesNumber->Field;
535  paccr = PebbleAccelrad->Field;
536  pacct = PebbleAcceltheta->Field;
537  nr = Vrad->Nrad;
538  ns = Vrad->Nsec;
539  vr = Vrad->Field;
540  vt = Vtheta->Field;
541  dragr = DragForceRad->Field;
542  dragt = DragForceTheta->Field;
543  pagr = PebbleGravAccelRad->Field;
544  pagt = PebbleGravAccelTheta->Field;
546 #pragma omp parallel private(i,j,l,lim,ljm,ljp,vt2,Omega,drag)
547  {
548 #pragma omp for
549  for (i = 1; i < nr; i++) {
550  Omega = 1.0/OmegaInv[i];
551  for (j = 0; j < ns; j++) {
552  l = j+i*ns;
553  lim = l-ns;
554  ljm = l-1;
555  if (j == 0) ljm = i*ns+ns-1;
556  ljp = l+1;
557  if (j == ns-1) ljp = i*ns;
558  vt2 = pvt[l]+pvt[ljp]+pvt[lim]+pvt[ljp-ns];
559  vt2 = vt2/4.0+Rinf[i]*OmegaFrame;
560  vt2 = vt2*vt2;
561  paccr[l] = vt2*InvRinf[i]+0.5*(pagr[l]+pagr[lim]);
562  if (BackReaction) {
563  drag = Omega*(pvr[l] - vr[l])/tau[l];
564  dragr[l] = 0.5*(pdens[l]+pdens[lim])*drag;
565  }
566  }
567  }
568 #pragma omp for
569  for (i = 0; i < nr; i++) {
570  Omega = 1.0/OmegaInv[i];
571  for (j = 0; j < ns; j++) {
572  l = j+i*ns;
573  lim = l-ns;
574  ljm = l-1;
575  if (j == 0) ljm = i*ns+ns-1;
576  ljp = l+1;
577  if (j == ns-1) ljp = i*ns;
578  pacct[l] = 0.5*(pagt[l]+pagt[ljm]);
579  if (BackReaction) {
580  drag = Omega*(pvt[l] - vt[l])/tau[l];
581  dragt[l] = 0.5*(pdens[l]+pdens[ljm])*drag;
582  }
583  }
584  }
585  }
586 }
587 
588 /** Applies a semi-implicit method to evolve the pebbles
589  * dynamically. See Appendix C in Chrenko et al. (2017) */
590 void SubStep1Pebbles (Vrad,Vtheta,dt)
591 PolarGrid *Vrad, *Vtheta;
592 real dt;
593 {
594  real *pvr, *pvt, *tau, *paccr, *pacct, *accr, *acct, *vr, *vt;
595  real Omega, efac;
596  int nr, ns, i, j, l;
597  pvr = PebbleVrad->Field;
598  pvt = PebbleVtheta->Field;
599  tau = StokesNumber->Field;
600  paccr = PebbleAccelrad->Field;
601  pacct = PebbleAcceltheta->Field;
602  accr = GasAccelrad->Field;
603  acct = GasAcceltheta->Field;
604  nr = Vrad->Nrad;
605  ns = Vrad->Nsec;
606  vr = Vrad->Field;
607  vt = Vtheta->Field;
608 #pragma omp parallel private(j,l,Omega,efac)
609  {
610 #pragma omp for
611  for (i = 0; i < nr; i++) {
612  Omega = 1.0/OmegaInv[i];
613  for (j = 0; j < ns; j++) {
614  l = j+i*ns;
615  efac = exp(-dt*Omega/tau[l]);
616  if (i > 0) {
617  pvr[l] = pvr[l]*efac + accr[l]*dt + \
618  (vr[l] + (paccr[l] - accr[l])*tau[l]/Omega)*(1.0 - efac);
619  }
620  pvt[l] = pvt[l]*efac + acct[l]*dt + \
621  (vt[l] + (pacct[l] - acct[l])*tau[l]/Omega)*(1.0 - efac);
622  }
623  }
624  }
625 }
626 
627 /** Applies the particle diffusion term acting on pebbles.
628  * See Eq. (35) in Chrenko et al. (2017) */
630 PolarGrid *Rho;
631 {
632  int i,j,l,lim,ljm,nr,ns;
633  real corr, dxtheta, invdxtheta;
634  real *rho, *pdens, *pvr, *pvt;
635  rho = Rho->Field;
636  nr = Rho->Nrad;
637  ns = Rho->Nsec;
638  pdens = PebbleDens->Field;
639  pvr = PebbleVrad->Field;
640  pvt = PebbleVtheta->Field;
641 #pragma omp parallel default(none) \
642  shared (nr,ns,Rinf,SCHMIDTNUMBER,rho,pdens,InvDiffRmed,pvr,Rmed,pvt) \
643  private (i,j,l,lim,ljm,corr,dxtheta,invdxtheta)
644  {
645 #pragma omp for
646  for (i=1; i<nr; i++) {
647  for (j=0; j<ns; j++) {
648  l = j + i*ns;
649  lim = l - ns;
650  corr = -FViscosity(Rinf[i])/SCHMIDTNUMBER;
651  corr *= (rho[l] + rho[lim])/(pdens[l] + pdens[lim]);
652  corr *= (pdens[l]/rho[l] - pdens[lim]/rho[lim])*InvDiffRmed[i];
653  pvr[l] += corr;
654  }
655  }
656 #pragma omp for
657  for (i=0; i<nr; i++) {
658  dxtheta = 2.0*PI/(real)ns*Rmed[i];
659  invdxtheta = 1.0/dxtheta;
660  for (j=0; j<ns; j++) {
661  l = j + i*ns;
662  ljm = l-1;
663  if (j == 0) ljm = i*ns+ns-1;
664  corr = -FViscosity(Rmed[i])/SCHMIDTNUMBER;
665  corr *= (rho[l] + rho[ljm])/(pdens[l] + pdens[ljm]);
666  corr *= (pdens[l]/rho[l] - pdens[ljm]/rho[ljm])*invdxtheta;
667  pvt[l] += corr;
668  }
669  }
670  } // #end of omp parallel section
671 }
672 
673 /** Calls the transport routines and applies the boundary conditions
674  * for pebbles */
676 real dt;
677 {
681 }
682 
683 /** Synchronises pebble fluid hydrodynamic quantities
684  * among the overlapping grid zones. */
686 {
687  int nr;
688  real *pdens, *pvr, *pvt;
689  nr = PebbleDens->Nrad;
690  pdens = PebbleDens->Field;
691  pvr = PebbleVrad->Field;
692  pvt = PebbleVtheta->Field;
693  if (CPU_Number > 1) {
694  SynchronizeOverlapFields (pdens, nr, CPUOVERLAP); /* we use the function from RadiativeDiffusion.c */
697  }
698 }
699 
700 /** Restricts the time step using the CFL
701  * condition for the pebble fluid. */
702 void CriticalCharTime (Vrad, Vtheta)
703 PolarGrid *Vrad, *Vtheta;
704 {
705  real dxrad, dxtheta, pvtmed=0.0, invdt1, invdt2, invdt3, invdt4, Vresidual, temp;
706  real *pvr, *pvt, *vr, *vt;
707  int i, j, l, ns;
708  pvr = PebbleVrad->Field;
709  ns = PebbleVrad->Nsec;
710  pvt = PebbleVtheta->Field;
711  vr = Vrad->Field;
712  vt = Vtheta->Field;
713  invdtpeb_sq = -1.e30;
714  for (i=One_or_active; i<Max_or_active; i++) {
715  dxrad = Rsup[i] - Rinf[i];
716  dxtheta = Rmed[i]*2.0*PI/(real)ns;
717  if (FastTransport) {
718  pvtmed = 0.0;
719  for (j=0; j<ns; j++) {
720  l = j + i*ns;
721  pvtmed += pvt[l];
722  }
723  pvtmed /= (real)ns;
724  }
725  for (j=0; j<ns; j++) {
726  l = j + i*ns;
727  invdt1 = fabs(pvr[l])/dxrad;
728  if (FastTransport) {
729  Vresidual = pvt[l] - pvtmed;
730  } else {
731  Vresidual = pvt[l];
732  }
733  invdt2 = fabs(Vresidual)/dxtheta;
734  invdt3 = fabs(pvr[l] - vr[l])/dxrad; // Rosotti et al. 2011
735  invdt4 = fabs(pvt[l] - vt[l])/dxtheta;
736  temp = invdt1*invdt1 + invdt2*invdt2 + invdt3*invdt3 + invdt4*invdt4;
737  invdtpeb_sq = max2(invdtpeb_sq,temp);
738  }
739  }
740 }
741 
742 /** Outputs the pebble fluid arrays. */
743 void WritePebbles (index)
744 int index;
745 {
746  WriteDiskPolar (PebbleDens, index);
747  WriteDiskPolar (PebbleVrad, index);
748  WriteDiskPolar (PebbleVtheta, index);
749  if (Write_Eta) WriteDiskPolar (EtaCellCentered, index);
750 }
751 
752 /** Safety check for negative pebble densities. */
754 {
755  boolean result;
756  result = DetectCrash (PebbleDens);
757  return result;
758 }
759 
760 /** Writes filtering factors if needed. */
761 /*void UpdateFilteringFile (sys)
762 PlanetarySystem *sys;
763 {
764  int k;
765  char name[256];
766  FILE *output;
767  if (!CPU_Master) return;
768  sprintf (name, "%spebble_filtering.dat", OUTPUTDIR);
769  output = fopenp (name, "a");
770  for (k=0; k<sys->nb; k++) {
771  fprintf (output, "%d\t%.12g\t%.12g\n", k, PhysicalTime, filtering[k]);
772  }
773  fclose (output);
774 }*/
775 
776 /** Mass accretion onto planets is provided
777  * by a parametric prescription using a given mass
778  * doubling time. */
779 void ParametricAccretion (sys, dt)
780 PlanetarySystem *sys;
781 real dt;
782 {
783  int k, i, j, l, nr, ns;
784  real Mplanet, Xplanet, Yplanet, dMplanet, Rplanet, tdelay, taper;
785  real plrho, plradius, L, ang, dE;
786  static real doubling=0.0;
787  if (doubling == 0.0) doubling = PARAMETRICACCRETION*1000.0*2.0*PI; // convert the doubling time from kyr to code units
788  nr = SoundSpeed->Nrad;
789  ns = SoundSpeed->Nsec;
790  if (AccretHeating) heatsrc_max = sys->nb;
791  for (k=0; k<sys->nb; k++) {
792  Mplanet = sys->mass[k];
793  Xplanet = sys->x[k];
794  Yplanet = sys->y[k];
795  dMplanet = Mplanet*dt/doubling;
796  Rplanet = sqrt(Xplanet*Xplanet + Yplanet*Yplanet);
797  if (AccretHeating) {
798  heatsrc_index[k] = -1;
799  if (Rplanet >= Rinf[0] && Rplanet <= Rsup[nr-1]) {
800  tdelay = 4.0*DT*(real)HEATINGDELAY;
801  taper = PhysicalTime/tdelay;
802  if (taper > 1.0) taper = 1.0;
803  plrho = PLANETARYDENSITY/RHO2CGS;
804  plradius = pow (3.0*Mplanet/(4.0*PI*plrho), 1.0/3.0);
805  L = Mplanet*Mplanet/doubling/plradius;
806  ang = atan2(Yplanet,Xplanet);
807  if (ang < 0.0) ang += 2.0*PI;
808  i = 0;
809  while (Rsup[i] < Rplanet) i++;
810  j = floor((real)ns/2.0/PI*ang + 0.5);
811  if (j==ns) j=0;
812  l = j + i*ns;
813  heatsrc_index[k] = l;
814  dE = L*taper/Surf[i]; // final dimension: W/m^2 -> average power density in cells closest to the planet
815  heatsrc[k] = dE;
816  }
817  }
818  Mplanet += dMplanet;
819  sys->mass[k] = Mplanet;
820  }
821 }
double real
Definition of the type 'real' used throughout the code.
Definition: types.h:20
real PEBBLEFLUX
Definition: param_noex.h:86
real InvDiffRmed[MAX1D]
Definition: global.h:12
int HEATINGDELAY
Definition: param_noex.h:92
real Rsup[MAX1D]
Definition: global.h:11
real SCHMIDTNUMBER
Definition: param_noex.h:90
int CPU_Number
Definition: global.h:2
real PLANETARYDENSITY
Definition: param_noex.h:72
void EtaPressureSupport(PolarGrid *Vtheta)
Calculates tha gas rotation parameter eta.
Definition: Pebbles.c:195
real DT
Definition: param_noex.h:7
#define MPI_DOUBLE
Definition: mpi_dummy.h:11
PolarGrid * PebbleGravAccelTheta
Definition: global.h:39
char OUTPUTDIR[512]
Definition: param_noex.h:11
void EquilPebbleDisk(PolarGrid *Rho, PolarGrid *Vrad, PolarGrid *Vtheta)
Sets up a pebble disk in a coagulation-drift equilibrium.
Definition: Pebbles.c:58
#define TRAPEZEPS
Definition: Pebbles.c:22
real Trapzd(int n, real a, real b, real H2)
Trapezoidal rule to integrate the column mass of pebbles located within the accretion radius...
Definition: Pebbles.c:265
real max2()
PolarGrid * CreatePolarGrid(int Nr, int Ns, char *name)
Definition: LowTasks.c:73
PolarGrid * SoundSpeed
Definition: global.h:35
real IntegrateColumnMass(real a, real b, real Hpeb)
Primitive algorithm to find the mass in a vertical column of pebbles overlapping the accretion radius...
Definition: Pebbles.c:292
boolean DetectCrash()
int heatsrc_max
Definition: global.h:22
real InvRinf[MAX1D]
Definition: global.h:13
PolarGrid * DragForceTheta
Definition: global.h:37
void RestartPebbleDisk(PolarGrid *Rho, int index)
Reads arrays necessary to restart the pebble disk.
Definition: Pebbles.c:66
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
boolean Restart
Definition: main.c:14
real * Field
Definition: types.h:40
static real VXplanet
Definition: Output.c:17
int Nrad
Radial size of the grid, in number of zones.
Definition: types.h:38
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 * PebbleAcceltheta
Definition: Pebbles.c:26
real invdtpeb_sq
Definition: global.h:19
real PebVthetaInit[MAX1D]
Definition: global.h:18
real vt1D[MAX1D]
Definition: global.h:19
PolarGrid * CellOrdinate
Definition: global.h:33
real SQRT_ADIABIND_INV
Definition: global.h:19
static real a[7]
Definition: EnergySources.c:27
real PhysicalTime
Definition: global.h:20
void SynchronizeOverlapFields(real *field, int nr, int nsync)
For a MPI-split grid, synchronizes the values in a requested number 'nsync' of the overlapping radial...
void CorrectPebblesVtheta(real domega)
Corrects the azimuthal flow velocity of pebbles to keep up with the frame rotation.
Definition: Pebbles.c:516
real PEBBLEBULKDENS
Definition: param_noex.h:89
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 TransportPebbles()
void WritePebbles(int index)
Outputs the pebble fluid arrays.
Definition: Pebbles.c:743
boolean heatsrc_index[MAXPLANETS]
Definition: global.h:28
static PolarGrid * EtaCellCentered
Definition: Pebbles.c:28
static real Yplanet
Definition: Output.c:17
static PolarGrid * PebbleDensTemp
Definition: Pebbles.c:26
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
static PolarGrid * AccretedMass
Definition: Pebbles.c:29
real FViscosity()
PolarGrid * PebbleGravAccelRad
Definition: global.h:39
#define CPUOVERLAP
Definition: fondam.h:13
boolean DetectCrashPebbles()
Safety check for negative pebble densities.
Definition: Pebbles.c:753
PolarGrid * DragForceRad
Definition: global.h:37
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
#define MPI_SUM
Definition: mpi_dummy.h:17
real PebDensInit[MAX1D]
Definition: global.h:18
#define RHO2CGS
Definition: fondam.h:38
boolean FastTransport
Definition: Interpret.c:29
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
int Max_or_active
Definition: global.h:7
real PebVradInit[MAX1D]
Definition: global.h:18
PolarGrid * StokesNumber
Definition: global.h:38
PolarGrid * CellAbscissa
Definition: global.h:33
static real pebbulkdens
Definition: Pebbles.c:31
real Surf[MAX1D]
Definition: global.h:11
void InitPebblesViaFlux(PolarGrid *Rho, PolarGrid *Vrad)
Imposing the radial mass flux of pebbles in a coagulation-drift equilibrium, initialises their surfac...
Definition: Pebbles.c:80
void BckpFieldsForBC()
Backs up the initial state of the pebble disk to impose damping boundary conditions later...
Definition: Pebbles.c:168
#define TRAPEZMAX
Definition: Pebbles.c:21
PolarGrid * PebbleDens
Definition: global.h:38
static real VYplanet
Definition: Output.c:17
real ADIABIND
Definition: param_noex.h:54
int NRAD
Definition: param_noex.h:18
static real domega
Definition: EnergySources.c:40
static PolarGrid * PebbleAccelrad
Definition: Pebbles.c:26
void ParticleDiffusion(PolarGrid *Rho)
Applies the particle diffusion term acting on pebbles.
Definition: Pebbles.c:629
Contains all the include directives requested by the code.
boolean Write_Eta
Definition: global.h:27
real Rinf[MAX1D]
Definition: global.h:11
int Nsec
Azimuthal size of the grid, in number of zones.
Definition: types.h:39
PolarGrid * PebbleVtheta
Definition: global.h:38
boolean Merge
Definition: global.h:29
boolean AccretHeating
Definition: global.h:27
void CriticalCharTime(PolarGrid *Vrad, PolarGrid *Vtheta)
Restricts the time step using the CFL condition for the pebble fluid.
Definition: Pebbles.c:702
real GetGlobalIFrac(real r)
Definition: LowTasks.c:21
void ReadfromFile(PolarGrid *array, char *fileprefix, int filenumber)
Definition: Init.c:23
static real b[7]
Definition: EnergySources.c:28
boolean BackReaction
Definition: global.h:27
#define FLUX2CU
Definition: fondam.h:54
real Energy()
void EvolvePebbleDisk(real dt)
Calls the transport routines and applies the boundary conditions for pebbles.
Definition: Pebbles.c:675
static real Xplanet
Definition: Output.c:17
PolarGrid * GasAccelrad
Definition: global.h:37
real InvRmed[MAX1D]
Definition: global.h:12
real heatsrc[MAXPLANETS]
Definition: global.h:21
void DampPebbles()
#define SQRT2PI_INV
Definition: fondam.h:20
PolarGrid * GasAcceltheta
Definition: global.h:37
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
real PARAMETRICACCRETION
Definition: param_noex.h:93
void CorrectVtheta()
void mpi_make1Dprofile(real *gridfield, real *axifield)
Definition: mpiTasks.c:12
boolean CPU_Master
Definition: global.h:3
real PEBBLECOAGULATION
Definition: param_noex.h:88
PolarGrid * PebbleVrad
Definition: global.h:38
#define PI
Definition: fondam.h:12
void SynchronizePebbleDisc()
Synchronises pebble fluid hydrodynamic quantities among the overlapping grid zones.
Definition: Pebbles.c:685
static PolarGrid * EtaFaceCentered
Definition: Pebbles.c:28
#define MPI_COMM_WORLD
Definition: mpi_dummy.h:10
static PolarGrid * PebbleSize
Definition: Pebbles.c:27
void prs_exit(int numb)
Definition: LowTasks.c:33
void masterprint(const char *template,...)
Definition: LowTasks.c:40
real PEBBLEALPHA
Definition: param_noex.h:87