PLUTO
thermal_eos.c
Go to the documentation of this file.
1 /* ///////////////////////////////////////////////////////////////////// */
2 /*!
3  \file
4  \brief Thermodynamic relations for the thermal EoS, \c p=nkT and
5  <tt> T=p/(nK) </tt>.
6 
7  Collect miscellaneous functions involving computations with the
8  thermal equation of state,
9  \f[
10  p = nk_BT \quad {\rm(in\; cgs)}
11  \qquad \Longrightarrow\qquad
12  p = \frac{\rho T}{K\mu(\vec{X})} \quad {\rm(in\; code\quad units)}
13  \f]
14  where, in the second expression, \c rho and \c p are density and
15  pressure (in code units), \c T is the temperature (in Kelvin), \c K
16  is the \c ::KELVIN dimensionalization constant, \c mu(\b X) is the mean
17  molecular weight and \b X is an array containing the ionization fractions
18  of different elements.
19  The previous relation is typically used to compute
20  - <tt> p=p(T,rho,\b X) </tt> by calling Pressure();
21  - <tt> T=T(p,rho,\b X) </tt> by calling GetPV_Temperature().
22  This relation can be either:
23  - \e Explicit: ion fractions are evolved independently using a
24  chemical reaction network (e.g. when \c H2_COOL or \c SNEq is enabled).
25  Then one has \f$ T=(p/\rho) K\mu(\vec{X})\f$.
26  - \e Implicit: ions are not evolved explicitly but are computed
27  from equilibrium considerations (e.g. Saha of collisional ionization
28  equilibrium) so that \c X=X(T,rho) and the mean molecular
29  weight now depends on temperature.
30  Then one has \f$T = (p/\rho) K \mu(T,\rho)\f$.
31 
32  Nonlinear equation can be solved with a root finder (typically Brent's
33  method) or a tabulated approach (default).
34  In both case, a 2D table (::Ttab) of the temperature \c T(i,j) is
35  pre-calculated for fixed values of \c p/rho and \c rho by
36  MakePV_TemperatureTable() and stored into memory.
37  The table is then used to bracket the root (in the case of a root
38  finder) or to replace the runtime computation with a simpler
39  array indexing operation followed by a combination of lookup table
40  and bilinear (direct or inverse) interpolation.
41 
42  \author A. Mignone (mignone@ph.unito.it)\n
43  B. Vaidya
44  \date 31 Aug, 2014
45 */
46 /* /////////////////////////////////////////////////////////////////// */
47 #include "pluto.h"
48 
49 #ifndef PV_TEMPERATURE_TABLE_NX
50  #define PV_TEMPERATURE_TABLE_NX 768
51 #endif
52 #ifndef PV_TEMPERATURE_TABLE_NY
53  #define PV_TEMPERATURE_TABLE_NY 768
54 #endif
55 
56 static Table2D Ttab; /**< A 2D table containing pre-computed values of
57  temperature stored at equally spaced node values
58  of <tt> Log(p/rho) </tt> and \c Log(rho) */
59 #if NIONS == 0
60 static double TFunc(double T, void *par);
61 
62 /* ********************************************************************* */
64 /*!
65  * Pre-calculate a 2D table of temperature <tt>T(p,rho)</tt> by
66  * inverting, at specified values of \c p and \c rho, the nonlinear
67  * equation (only in LTE or CIE)
68  * \f[
69  * f(T) = T - \frac{p}{\rho} K\mu(T,\rho) = 0
70  * \qquad\Longrightarrow\qquad
71  * T_{ij} = T(x_i, y_j) \qquad
72  * \left(x=\log_{10}\frac{p}{\rho}\,,\quad y=\log_{10}\rho\right)
73  * \f]
74  * For convenience, the table is constructed using equally
75  * spaced bins of <tt>x=log10(p/rho)</tt> and <tt>y=log10(rho)</tt>
76  * where \c p and \c rho are in code units.
77  * This function must be called only once to initialize the 2D table
78  * \c ::Ttab.
79  *
80  *********************************************************************** */
81 {
82  int i,j, status;
83  double prs, rho, T1, Tlo, Thi, T, logK = log10(KELVIN);
84  double mu_lo, mu_hi;
85  struct func_param par;
86 
87  print1 ("> MakePV_TemperatureTable: Generating table...\n");
88 
89 /* --------------------------------------------------------------
90  Initialize table. The two table axis are given by
91  ln(x) = log(p/rho) and ln(y) = log(rho).
92  The lower and upper x-bounds correspond to T/mu = 1 K and
93  T/mu = 10^7 K, respectively.
94  -------------------------------------------------------------- */
95 
97  1.e-7 , 1.e7, PV_TEMPERATURE_TABLE_NY);
98 
99 /* -----------------------------------------------------------------
100  Guess the smallest and largest value of \mu.
101  This is likely to happen at very low and very high temperatures
102  ----------------------------------------------------------------- */
103 
104  GetMu(1.0, 1.0, &mu_lo);
105  GetMu(1.e12, 1.0, &mu_hi);
106 
107  for (j = 0; j < Ttab.ny; j++){
108  for (i = 0; i < Ttab.nx; i++){
109 
110  T1 = Ttab.x[i]*KELVIN; /* = p/rho*KELVIN */
111  rho = Ttab.y[j];
112 
113  par.T1 = T1;
114  par.v[RHO] = rho;
115 
116  /* -- set interval endpoints for root finder -- */
117 
118  Tlo = par.T1*mu_lo; /* T > T1*mu_\min */
119  Thi = par.T1*mu_hi; /* T < T1*mu_\max */
120 
121 /* status = Ridder(TFunc, &par, Tlo, Thi, -1, 1.0e-12, &T); */
122  status = Brent(TFunc, &par, Tlo, Thi, -1, 1.0e-12, &T);
123  if (status != 0) {
124  print1 ("! MakePV_TemperatureTable: ");
125  print1 ("root could not be found [%d]\n",status);
126  QUIT_PLUTO(1);
127  }
128  Ttab.f[j][i] = T;
129  }}
130 
131  FinalizeTable2D(&Ttab);
132  if (prank == 0) WriteBinaryTable2D("T_tab.bin",&Ttab);
133 
134 #if 0 /* Table-to-Table conversion: attempt to interpolate from another table */
135 {
136  int status;
137  double T, p, mu = 1.2;
138  Table2D rhotab; /* test table rho = rho(T,p); */
139 
140  InitializeTable2D(&rhotab, 1.e2, 1.e7, 64, 1.e-2, 1.e4, 80);
141 
142  for (j = 0; j < rhotab.ny; j++){
143  for (i = 0; i < rhotab.nx; i++){
144  T = rhotab.x[i];
145  p = rhotab.y[j];
146 mu = (T/5.e3);
147 mu = 1.2 + 0.0/cosh(mu*mu);
148  rhotab.f[j][i] = p*KELVIN*mu/T; /* rho */
149  }}
150 
151  FinalizeTable2D(&rhotab);
152  if (prank == 0) WriteBinaryTable2D("rhotab.bin",&rhotab);
153 
154 /* -- Build T(p/rho, rho) table -- */
155 
156  status = 0;
157  for (j = 0; j < Ttab.ny; j++){
158  for (i = 0; i < Ttab.nx; i++){
159  rho = Ttab.y[j];
160  p = Ttab.x[i]*rho;
161  status = InverseLookupTable2D(&rhotab, p, rho, &T);
162  if (status != 0){
163  printf ("! Error in inverting table\n");
164  T = -1.0;
165  }
166  Ttab.f[j][i] = T;
167  }}
168 
169  if (prank == 0) WriteBinaryTable2D("T_tab2.bin",&Ttab);
170 
171  printf ("KELVIN = %12.6e\n",KELVIN);
172  exit(1);
173 }
174 #endif /* 0 */
175 }
176 #endif /* NIONS == 0 */
177 
178 /* ********************************************************************* */
179 double Pressure(double *v, double T)
180 /*!
181  * Return pressure as a function of temperature, density and fractions.
182  * If <tt>T(p/rho,rho)</tt> has been tabulated, this requires
183  * inverting the table by calling InverseLookupTable2D().
184  *
185  * \param [in] v a pointer to a vector of primitive quantities
186  * (only density and fractions will be actually used).
187  * \param [in] T The temperature (in Kelvin);
188  *
189  * \return Pressure in code units.
190  *********************************************************************** */
191 {
192 #if PV_TEMPERATURE_TABLE == YES
193 
194  int status;
195  double rho, T1;
196 
197  rho = v[RHO];
198  status = InverseLookupTable2D(&Ttab, rho, T, &T1);
199  if (status != 0){
200  print ("! Pressure: table interpolation failure [rho = %12.6e]\n", rho);
201  QUIT_PLUTO(1);
202  }
203 
204  return T1*rho;
205 
206 #else
207 
208  double mu;
209 
210  #if NIONS == 0
211  GetMu(T, v[RHO], &mu);
212  #else
213  mu = MeanMolecularWeight(v);
214  #endif
215  return v[RHO]*T/(KELVIN*mu);
216 
217 #endif
218 }
219 
220 /* ********************************************************************* */
221 int GetPV_Temperature (double *v, double *T)
222 /*!
223  * Compute gas temperature (in Kelvin) from density, pressure
224  * and fractions:
225  * \f[
226  * T = \left\{\begin{array}{ll}
227  * \DS \frac{p}{\rho} K \mu(\vec{X}) &\quad{(\rm with\; chemistry)}
228  * \\ \noalign{\medskip}
229  * \DS \frac{p}{\rho}K\mu(\vec{X}(T,\rho)) & \quad{(\rm without\; chemistry)}
230  * \end{array}\right.
231  * \f]
232  * where \c K is the \c ::KELVIN macro.
233  *
234  * The first relation is used when ion fractions are explicitly evolved
235  * by the code (\c NIONS>0) while the second one is inverted numerically
236  * using root finder or lookup table when either LTE or CIE is assumed.
237  *
238  * \param [in] v 1D array of primitive quantities.
239  * \param [out] T Temperature in K
240  *
241  * \return 0 on success, 1 on failure.
242  *********************************************************************** */
243 {
244  int status;
245 
246 #if NIONS == 0 && PV_TEMPERATURE_TABLE == YES
247 
248  double rho, T1;
249 
250  if (v[PRS] < 0.0) return 1;
251 
252  rho = v[RHO];
253  T1 = v[PRS]/rho;
254  status = Table2DInterpolate(&Ttab, T1, rho, T);
255 
256  if (status != 0){
257  print ("! GetPV_Temperature: table interpolation failure ");
258  print ("[T1 = %8.3e, rho = %8.3e]\n",T1,rho);
259 
260  if (status == -1) return 1; /* hit lower x range (easy fix) */
261  else QUIT_PLUTO(1); /* not so easy to fix */
262  }
263 
264  if (*T < T_CUT_RHOE) return 1;
265  else return 0;
266 
267 #elif NIONS == 0 && PV_TEMPERATURE_TABLE == NO
268 
269  int i,j;
270  double Tmin, Tmax, lnx, lny, rho, prs;
271  struct func_param par;
272 
273  if (v[PRS] < 0.0) return 1;
274 
275  par.v[RHO] = v[RHO];
276  par.T1 = v[PRS]/v[RHO]*KELVIN;
277 
278 /* -- Locate indices (i,j) in the table -- */
279 
280  rho = v[RHO];
281  prs = v[PRS];
282  lnx = log10(prs/rho);
283  lny = log10(rho);
284  i = INT_FLOOR((lnx - Ttab.lnxmin)*Ttab.dlnx_1);
285  j = INT_FLOOR((lny - Ttab.lnymin)*Ttab.dlny_1);
286 
287 /* -------------------------------------------------------------------
288  Since Ttab[j][i] increases with i and j, the min and max between
289  four adjacent node values are the two opposite points on the main
290  diagonal given, respectively, by (i,j) and (i+1,j+1).
291  ------------------------------------------------------------------- */
292 
293  Tmin = MIN(Ttab.f[j][i], Ttab.f[j+1][i+1]);
294  Tmax = MAX(Ttab.f[j][i], Ttab.f[j+1][i+1]);
295 
296 /* status = Ridder(TFunc, &p, Tmin, Tmax, -1, 1.0e-7, &T); */
297  status = Brent(TFunc, &par, Tmin, Tmax, -1, 1.0e-7, T);
298  if (status != 0){
299  print ("! PrimitiveTemperature: could not find root, ");
300  print ("! [Tmin, Tmax] = [%12.6e, %12.6e]\n",Tmin, Tmax);
301  QUIT_PLUTO(1);
302  }
303 
304  if (*T < T_CUT_RHOE || status != 0) return 1;
305  return 0;
306 
307 #else
308 
309  *T = (v[PRS]/v[RHO])*KELVIN*MeanMolecularWeight(v);
310  if (*T < T_CUT_RHOE) return 1;
311  return 0;
312 
313 #endif
314 
315 }
316 
317 #if NIONS == 0
318 /* ********************************************************************* */
319 double TFunc(double T, void *par)
320 /*!
321  * Return the nonlinear function <tt> f(T) = T - T1 mu(T,rho)</tt> used
322  * by the root finder to find temperature \c T.
323  *
324  * \param [in] T The temperature in Kelvin
325  * \param [in] par A pointer to a \c func_param structure containing
326  * density required for computing internal energy.
327  * \return f(T)
328  *********************************************************************** */
329 {
330  double T1, rho, mu;
331  struct func_param *p = (struct func_param *) par;
332 
333  T1 = p->T1;
334  rho = p->v[RHO];
335  GetMu(T, rho, &mu);
336  return T - T1*mu;
337 }
338 #endif
339 
#define MAX(a, b)
Definition: macros.h:101
void GetMu(double T, double rho, double *mu)
Definition: pvte_law.c:115
tuple T
Definition: Sph_disk.py:33
double * y
array of y-values (not uniform)
Definition: structs.h:181
#define T_CUT_RHOE
Sets the lowest cut-off temperature used in the PVTE_LAW equation of state.
Definition: eos.h:69
#define PV_TEMPERATURE_TABLE_NY
Definition: thermal_eos.c:53
void print1(const char *fmt,...)
Definition: amrPluto.cpp:511
int nx
Number of columns or points in the x direction.
Definition: structs.h:174
#define RHO
Definition: mod_defs.h:19
double T1
Definition: eos.h:108
double dlny_1
Definition: structs.h:205
double v[NVAR]
Definition: eos.h:106
#define PV_TEMPERATURE_TABLE_NX
Definition: thermal_eos.c:50
double ** f
Definition: structs.h:186
#define KELVIN
Definition: pluto.h:401
int prank
Processor rank.
Definition: globals.h:33
void WriteBinaryTable2D(char *fname, Table2D *tab)
Definition: math_table2D.c:528
#define MIN(a, b)
Definition: macros.h:104
int Brent(double(*Func)(double, void *), void *param, double x1, double x2, double abs_acc, double rel_acc, double *xroot)
double dlnx_1
Definition: structs.h:204
int InverseLookupTable2D(Table2D *tab, double y, double f, double *x)
Definition: math_table2D.c:196
#define INT_FLOOR(z)
Definition: macros.h:98
int j
Definition: analysis.c:2
int ny
Number of rows or points in the y direction.
Definition: structs.h:175
int Table2DInterpolate(Table2D *tab, double x, double y, double *f)
Definition: math_table2D.c:430
void print(const char *fmt,...)
Definition: amrPluto.cpp:497
static double TFunc(double T, void *par)
Definition: thermal_eos.c:319
PLUTO main header file.
void InitializeTable2D(Table2D *tab, double xmin, double xmax, int nx, double ymin, double ymax, int ny)
Definition: math_table2D.c:15
int i
Definition: analysis.c:2
double MeanMolecularWeight(double *V)
double Pressure(double *v, double T)
Definition: thermal_eos.c:179
static Table2D Ttab
A 2D table containing pre-computed values of temperature stored at equally spaced node values of Log...
Definition: thermal_eos.c:56
double lnxmin
lower limit (in log10) in the x-direction
Definition: structs.h:198
void FinalizeTable2D(Table2D *tab)
Definition: math_table2D.c:121
double lnymin
lower limit (in log10) in the y-direction
Definition: structs.h:200
int GetPV_Temperature(double *v, double *T)
Definition: thermal_eos.c:221
void MakePV_TemperatureTable()
Definition: thermal_eos.c:63
#define QUIT_PLUTO(e_code)
Definition: macros.h:125
double * x
array of x-values (not uniform)
Definition: structs.h:180