.. _program_listing_file_src_EOS.cpp: Program Listing for File EOS.cpp ================================ |exhale_lsh| :ref:`Return to documentation for file ` (``src/EOS.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "EOS.h" EOS::EOS():phasetype(""),eqntype(0), V0(numeric_limits::quiet_NaN()), K0(numeric_limits::quiet_NaN()), K0p(numeric_limits::quiet_NaN()), K0pp(numeric_limits::quiet_NaN()), mmol(numeric_limits::quiet_NaN()), P0(0), Theta0(numeric_limits::quiet_NaN()), gamma0(numeric_limits::quiet_NaN()), beta(numeric_limits::quiet_NaN()), gammainf(numeric_limits::quiet_NaN()), gamma0p(numeric_limits::quiet_NaN()), e0(numeric_limits::quiet_NaN()), g(numeric_limits::quiet_NaN()), T0(300), alpha0(numeric_limits::quiet_NaN()), alpha1(0), xi(0), cp_a(numeric_limits::quiet_NaN()), cp_b(0), cp_c(0), at1(numeric_limits::quiet_NaN()), at2(numeric_limits::quiet_NaN()), at3(numeric_limits::quiet_NaN()), at4(numeric_limits::quiet_NaN()), ap1(numeric_limits::quiet_NaN()), ap2(numeric_limits::quiet_NaN()), ap3(numeric_limits::quiet_NaN()), ap4(numeric_limits::quiet_NaN()), n(-1), Z(-1), a_vdW(numeric_limits::quiet_NaN()),b_vdW(numeric_limits::quiet_NaN()),Debye_approx(false), thermal_type(0), rhotable(NULL), Ptable(NULL), temptable(NULL), adiabattable(NULL), bn(0), accP(NULL), accT(NULL), spline(NULL), spline2drho(NULL), spline2dadi(NULL), nline(0), tlen(0) { density_extern=NULL; entropy_extern=NULL; dTdP = NULL; } EOS::EOS(string phaseinput, double params[][2], int length):phasetype(phaseinput),eqntype(0), V0(numeric_limits::quiet_NaN()), K0(numeric_limits::quiet_NaN()), K0p(numeric_limits::quiet_NaN()), K0pp(numeric_limits::quiet_NaN()), mmol(numeric_limits::quiet_NaN()), P0(0), Theta0(numeric_limits::quiet_NaN()), gamma0(numeric_limits::quiet_NaN()), beta(numeric_limits::quiet_NaN()), gammainf(numeric_limits::quiet_NaN()), gamma0p(numeric_limits::quiet_NaN()), e0(numeric_limits::quiet_NaN()), g(numeric_limits::quiet_NaN()), T0(300), alpha0(numeric_limits::quiet_NaN()), alpha1(0), xi(0), cp_a(numeric_limits::quiet_NaN()), cp_b(0), cp_c(0), at1(numeric_limits::quiet_NaN()), at2(numeric_limits::quiet_NaN()), at3(numeric_limits::quiet_NaN()), at4(numeric_limits::quiet_NaN()), ap1(numeric_limits::quiet_NaN()), ap2(numeric_limits::quiet_NaN()), ap3(numeric_limits::quiet_NaN()), ap4(numeric_limits::quiet_NaN()), n(-1), Z(-1), a_vdW(numeric_limits::quiet_NaN()),b_vdW(numeric_limits::quiet_NaN()),Debye_approx(false), thermal_type(0), rhotable(NULL), Ptable(NULL), temptable(NULL), adiabattable(NULL), bn(0), accP(NULL), accT(NULL), spline(NULL), spline2drho(NULL), spline2dadi(NULL), nline(0), tlen(0) { density_extern=NULL; entropy_extern=NULL; dTdP = NULL; for(int i=0;i0 ? true : false; break; case 24: thermal_type = params[i][1]; break; case 25: at1 = params[i][1]; break; case 26: at2 = params[i][1]; break; case 27: at3 = params[i][1]; break; case 28: at4 = params[i][1]; break; case 29: ap1 = params[i][1]; break; case 30: ap2 = params[i][1]; break; case 31: ap3 = params[i][1]; break; case 32: ap4 = params[i][1]; break; case 33: a_vdW = params[i][1]; break; case 34: b_vdW = params[i][1]; break; default: cout<<"Error: Incorrect index "<= 8) // RTpress EOS style { cout<<"Error: Incorrect equation type. Type "<::quiet_NaN()), K0(numeric_limits::quiet_NaN()), K0p(numeric_limits::quiet_NaN()), K0pp(numeric_limits::quiet_NaN()), mmol(numeric_limits::quiet_NaN()), P0(0), Theta0(numeric_limits::quiet_NaN()), gamma0(numeric_limits::quiet_NaN()), beta(numeric_limits::quiet_NaN()), gammainf(numeric_limits::quiet_NaN()), gamma0p(numeric_limits::quiet_NaN()), e0(numeric_limits::quiet_NaN()), g(numeric_limits::quiet_NaN()), T0(300), alpha0(numeric_limits::quiet_NaN()), alpha1(0), xi(0), cp_a(numeric_limits::quiet_NaN()), cp_b(0), cp_c(0), at1(numeric_limits::quiet_NaN()), at2(numeric_limits::quiet_NaN()), at3(numeric_limits::quiet_NaN()), at4(numeric_limits::quiet_NaN()), ap1(numeric_limits::quiet_NaN()), ap2(numeric_limits::quiet_NaN()), ap3(numeric_limits::quiet_NaN()), ap4(numeric_limits::quiet_NaN()), n(-1), Z(-1), a_vdW(numeric_limits::quiet_NaN()),b_vdW(numeric_limits::quiet_NaN()),Debye_approx(false), thermal_type(0), rhotable(NULL), Ptable(NULL), temptable(NULL), adiabattable(NULL), bn(0), accP(NULL), accT(NULL), spline(NULL), spline2drho(NULL), spline2dadi(NULL), nline(0), tlen(0) { ifstream fin; string sline; fin.open(filename.c_str()); density_extern=NULL; entropy_extern=NULL; dTdP = NULL; if(!fin) { if (verbose) cout<<"Warning: Failed to open input EOS file "<>ptemp1>>ttemp>>rhotemp>>adiabattemp; if(ptemp1!=ptemp && tlen>0) //Table must be ordered by (i, j) for i in P for j in T break; else{ tlen++; ptemp=ptemp1; } } fin.clear(); fin.seekg(beginpos); if(nline % tlen != 0) { if (verbose) cout<<"Warning: The input EOS file "<>Ptable[0]>>temptable[0]>>rhotable[0]>>adiabattable[0]; fin.seekg(beginpos); for(int i=0; i>ptemp>>ttemp>>rhotable[i]>>adiabattable[i]; if(i>rhotable[0]>>Ptable[0]; fin>>rhotable[1]>>Ptable[1]; fin.seekg(beginpos); if(Ptable[0]>Ptable[1]) for(int i=nline-1; i>=0; i--) fin>>rhotable[i]>>Ptable[i]; else for(int i=0; i>rhotable[i]>>Ptable[i]; fin.close(); accP = gsl_interp_accel_alloc (); spline = gsl_spline_alloc (gsl_interp_steffen, nline); gsl_spline_init (spline, Ptable, rhotable, nline); } else { if (verbose) cout<<"Warning: Requires 2 column (P, rho) or 4 column (P,T,rho,dT/dP_S) table in input EOS file "<::quiet_NaN()), K0(numeric_limits::quiet_NaN()), K0p(numeric_limits::quiet_NaN()), K0pp(numeric_limits::quiet_NaN()), mmol(numeric_limits::quiet_NaN()), P0(0), Theta0(numeric_limits::quiet_NaN()), gamma0(numeric_limits::quiet_NaN()), beta(numeric_limits::quiet_NaN()), gammainf(numeric_limits::quiet_NaN()), gamma0p(numeric_limits::quiet_NaN()), e0(numeric_limits::quiet_NaN()), g(numeric_limits::quiet_NaN()), T0(300), alpha0(numeric_limits::quiet_NaN()), alpha1(0), xi(0), cp_a(numeric_limits::quiet_NaN()), cp_b(0), cp_c(0), at1(numeric_limits::quiet_NaN()), at2(numeric_limits::quiet_NaN()), at3(numeric_limits::quiet_NaN()), at4(numeric_limits::quiet_NaN()), ap1(numeric_limits::quiet_NaN()), ap2(numeric_limits::quiet_NaN()), ap3(numeric_limits::quiet_NaN()), ap4(numeric_limits::quiet_NaN()), n(-1), Z(-1), a_vdW(numeric_limits::quiet_NaN()),b_vdW(numeric_limits::quiet_NaN()),Debye_approx(false), rhotable(NULL), Ptable(NULL), bn(0), accP(NULL), accT(NULL), spline(NULL), spline2drho(NULL), spline2dadi(NULL), nline(0), tlen(0) { density_extern=f; entropy_extern=g; dTdP = NULL; temptable = NULL; adiabattable = NULL; if (entropy_extern) thermal_type = 1; else thermal_type = 0; } EOS::EOS(string phaseinput, double *Plist, double *rholist, int len_list):phasetype(phaseinput),eqntype(7), V0(numeric_limits::quiet_NaN()), K0(numeric_limits::quiet_NaN()), K0p(numeric_limits::quiet_NaN()), K0pp(numeric_limits::quiet_NaN()), mmol(numeric_limits::quiet_NaN()), P0(0), Theta0(numeric_limits::quiet_NaN()), gamma0(numeric_limits::quiet_NaN()), beta(numeric_limits::quiet_NaN()), gammainf(numeric_limits::quiet_NaN()), gamma0p(numeric_limits::quiet_NaN()), e0(numeric_limits::quiet_NaN()), g(numeric_limits::quiet_NaN()), T0(300), alpha0(numeric_limits::quiet_NaN()), alpha1(0), xi(0), cp_a(numeric_limits::quiet_NaN()), cp_b(0), cp_c(0), at1(numeric_limits::quiet_NaN()), at2(numeric_limits::quiet_NaN()), at3(numeric_limits::quiet_NaN()), at4(numeric_limits::quiet_NaN()), ap1(numeric_limits::quiet_NaN()), ap2(numeric_limits::quiet_NaN()), ap3(numeric_limits::quiet_NaN()), ap4(numeric_limits::quiet_NaN()), n(-1), Z(-1), a_vdW(numeric_limits::quiet_NaN()),b_vdW(numeric_limits::quiet_NaN()),Debye_approx(false), thermal_type(0), bn(0), accT(NULL), spline2drho(NULL), spline2dadi(NULL), nline(len_list), tlen(0) { density_extern=NULL; entropy_extern=NULL; dTdP = NULL; temptable = NULL; adiabattable = NULL; rhotable=new double[nline]; Ptable=new double[nline]; // grl interpolation require strictly increasing x. Reverse the array order if x is decreasing. if(Plist[0]>Plist[1]) for(int i=0; i::quiet_NaN()), K0(numeric_limits::quiet_NaN()), K0p(numeric_limits::quiet_NaN()), K0pp(numeric_limits::quiet_NaN()), mmol(numeric_limits::quiet_NaN()), P0(0), Theta0(numeric_limits::quiet_NaN()), gamma0(numeric_limits::quiet_NaN()), beta(numeric_limits::quiet_NaN()), gammainf(numeric_limits::quiet_NaN()), gamma0p(numeric_limits::quiet_NaN()), e0(numeric_limits::quiet_NaN()), g(numeric_limits::quiet_NaN()), T0(300), alpha0(numeric_limits::quiet_NaN()), alpha1(0), xi(0), cp_a(numeric_limits::quiet_NaN()), cp_b(0), cp_c(0), at1(numeric_limits::quiet_NaN()), at2(numeric_limits::quiet_NaN()), at3(numeric_limits::quiet_NaN()), at4(numeric_limits::quiet_NaN()), ap1(numeric_limits::quiet_NaN()), ap2(numeric_limits::quiet_NaN()), ap3(numeric_limits::quiet_NaN()), ap4(numeric_limits::quiet_NaN()), n(-1), Z(-1), a_vdW(numeric_limits::quiet_NaN()),b_vdW(numeric_limits::quiet_NaN()),Debye_approx(false), thermal_type(8), rhotable(NULL), Ptable(NULL), bn(blength), accP(NULL), accT(NULL), spline(NULL), spline2drho(NULL), spline2dadi(NULL), nline(0), tlen(0) { // construction EOS for RTpress density_extern=NULL; entropy_extern=NULL; dTdP = NULL; temptable = NULL; adiabattable = NULL; for(int i=0;i0 ? true : false; break; case 24: break; // thermal_type has to be 8 case 25: at1 = params[i][1]; break; case 26: at2 = params[i][1]; break; case 27: at3 = params[i][1]; break; case 28: at4 = params[i][1]; break; case 29: ap1 = params[i][1]; break; case 30: ap2 = params[i][1]; break; case 31: ap3 = params[i][1]; break; case 32: ap4 = params[i][1]; break; case 33: a_vdW = params[i][1]; break; case 34: b_vdW = params[i][1]; break; default: cout<<"Error: Incorrect index "<0) delete[] b; } void EOS::modifyEOS(double params[][2], int length) // modify the constructed EOS parameters { if (density_extern) // Warning for EOS modification can't overwrite external EOS functions. { if (verbose) cout<<"Warning: External EOS function is set for state "<=8) // RTpress style { cout<<"Error: The number of atoms of a RTpress style EOS is not allowed to be modified."<0 ? true : false; break; case 24: thermal_type = params[i][1]; break; case 25: at1 = params[i][1]; break; case 26: at2 = params[i][1]; break; case 27: at3 = params[i][1]; break; case 28: at4 = params[i][1]; break; case 29: ap1 = params[i][1]; break; case 30: ap2 = params[i][1]; break; case 31: ap3 = params[i][1]; break; case 32: ap4 = params[i][1]; break; case 33: a_vdW = params[i][1]; break; case 34: b_vdW = params[i][1]; break; default: cout<<"Error: Incorrect index "<= 8) // RTpress EOS style thermal_type = 8; } void EOS::modifyEOS(int index, double value) // modify one value of the EOS { if (density_extern) // Warning for EOS modification can't overwrite external EOS functions. { if (verbose) cout<<"Warning: External EOS function is set for state "<=8) // RTpress style { cout<<"Error: The number of atoms of a RTpress style EOS is not allowed to be modified."<0 ? true : false; break; case 24: thermal_type = value; break; case 25: at1 = value; break; case 26: at2 = value; break; case 27: at3 = value; break; case 28: at4 = value; break; case 29: ap1 = value; break; case 30: ap2 = value; break; case 31: ap3 = value; break; case 32: ap4 = value; break; case 33: a_vdW = value; break; case 34: b_vdW = value; break; default: cout<<"Error: Incorrect index "<= 8) // RTpress EOS style { if (!gsl_finite(beta)) beta = 0.6; thermal_type = 8; } } double EOS::BM3(double rho) // input rho in g/cm^3, return pressure in GPa { if (!gsl_finite(V0) || !gsl_finite(K0)) { cout<<"Error: "<phasetype<<" missing input parameter(s) for BM3 EOS."<phasetype<<" missing input parameter(s) for BM4 EOS."<phasetype<<" missing input parameter(s) for Vinet EOS."<phasetype<<" missing input parameter(s) for Holzapfel EOS."<phasetype<<" missing input parameter(s) for Keane EOS."<::quiet_NaN(); Theta = numeric_limits::quiet_NaN(); return; } // set up default parameters for gammainf, beta, and n if not provided by the input data. if (!gsl_finite(gammainf)) // According to Al'tshuler et al. 1987, gammainf = 2/3 for all elements except alkali elments, for which gammainf = 0.5 gammainf = 2./3.; if (!gsl_finite(beta)) // Altshuler form. beta = gamma0 / (gamma0-gammainf); gamma = gammainf + (gamma0-gammainf)*pow(x,beta); if (thermal_type == 5 || thermal_type == 4) Theta = pow(x,-gammainf) * exp((gamma0-gammainf)/beta*(1-pow(x,beta))); else Theta = Theta0 * pow(x,-gammainf) * exp((gamma0-gammainf)/beta*(1-pow(x,beta))); // gammma = - d ln(Theta) / d ln(V) } double EOS::Pth(double V, double T) // calculate the thermal pressure in GPa, ref. Bouchet et al. 2013 PRB 87, 094102, Shim & Duffy, 2000, American Mineralogist // Or RTpress style Pth. { if (!gsl_finite(V0) || thermal_type < 2 || thermal_type == 3) // don't have thermal pressure data return 0; if ((thermal_type == 2 && !gsl_finite(gamma0)) || thermal_type == 9) return 0; if (gsl_finite(T) && getthermal() == 8) // RTpress style { double cf = 1E-10; // conversion factor from erg/cm^3 to GPa double T_OS = TOS(V); double fTp_OS = fTp(T_OS); double bVpV = bVp(V); // in erg/cm^3 (microbar) return - cf*bVpV*fT(T) + cf*gamma0S(V)*(T-T0)/V*Cv(V,T_OS) + cf*bVpV/(beta-1)*(T*(fTp(T)-fTp_OS) - T0*(fTp(T0)-fTp_OS)); } // set up default parameters for n if not provided by the input data. if (n<0) n = 1; double gamma, Theta, x = V/V0; DebyeT(x, gamma, Theta); if (thermal_type == 4 || thermal_type == 5 || (thermal_type == 2 && !gsl_finite(Theta0))) // Only gamma available // ref. Ono & Oganov 2005, Eq. 3, Earth Planet. Sci. Lett. 236, 914 return 3E-10*gamma*n*R*(T-T0)/V; double Eth = 3*n*R*T*gsl_sf_debye_3(Theta/T); double Eth0 = 3*n*R*T0*gsl_sf_debye_3(Theta/T0); if (thermal_type == 7 || (thermal_type==2 && gsl_finite(e0) && gsl_finite(g))) return 1E-10*gamma*(Eth-Eth0)/V + 1.5E-16*n*R*e0*pow(x,g)*g/V*(sq(T)-sq(T0)); // 1E-16 = 1E-10 * 1E-6. GPa -> microbar and e0 in 10^-6 K^-1 else // don't have enough information to get Pe return 1E-10 * gamma*(Eth-Eth0)/V; // convert to GPa } double EOS::adiabatic_index() // get the adiabatic index for ideal gas. Vibrational freedom is always ignored. // Note: For van der Waals gas, the adiabatic index C_p/C_v is no longer a constant. // But Cv is still relates to the internal degrees of freedom of the gas molecules (translational, rotational, vibrational). // So we still have Cv = R / (gamma-1) { if (eqntype != 6 || n<0) { cout<<"Error: "<::quiet_NaN(); } if (n == 1) // monatomic ideal gas return 5./3.; else if (n == 2) // diatomic gas and collinear molecules e.g. carbon dioxide return 1.4; else if (n == 0) // isothermal atmosphere return 1.; else // polyatomic gas return 4./3.; } double EOS::density(double P, double T, double rho_guess) // input P in cgs (microbar), return density in g/cm^3 { if(!gsl_finite(P) || !gsl_finite(T)) // Check if P or T is infinite or nan due to some error. Stop code to avoid further error. { if (verbose) cout<<"Warning: Request density for "<phasetype<<". The density at the end point is returned"<Ptable[nline/tlen-1] && T>temptable[tlen-1]) return rhotable[nline-1]; else if(PPtable[nline/tlen-1]) { gsl_spline2d_eval_e(spline2drho, T, Ptable[nline/tlen-1], accT, accP, &rho); return rho; } else if(T < temptable[0]) { gsl_spline2d_eval_e(spline2drho, temptable[0], P, accT, accP, &rho); return rho; } else { gsl_spline2d_eval_e(spline2drho, temptable[tlen-1], P, accT, accP, &rho); return rho; } } else return rho; } else { status = gsl_spline_eval_e(spline, P, accP, &rho); if(status == GSL_EDOM) { if (verbose) cout<<"Warning: Pressure "<phasetype<<". The density at the end point is returned"<R*T/b_vdW) // the volume is too close to b that the root solver would fail return 1E-3*mmol/b_vdW; P /= 1E10; // convert pressure from microbar to GPa // if no temperature information, T should be numeric_limits::quiet_NaN() struct EOS_params params = {{P, T}, this}; if(rho_guess < 1E-5 || !gsl_finite(rho_guess) || dP_EOS(rho_guess, ¶ms) < 0) // rho_guess will be set to negative if it is unknown. rho_guess = 1E-3*mmol/b_vdW - 1E-4; // slightly less than the maximum density. // the 1E-4 guarantee the density would fall in the larger side, which would crash the code int iter = 0, max_iter = 200; const gsl_root_fdfsolver_type *TPL = gsl_root_fdfsolver_newton; gsl_root_fdfsolver *s = gsl_root_fdfsolver_alloc (TPL); gsl_function_fdf FDF; double rho = rho_guess, rho0; FDF.f = &P_EOS; FDF.df = &dP_EOS; FDF.fdf = &PdP_EOS; FDF.params = ¶ms; gsl_root_fdfsolver_set (s, &FDF, rho); do { iter++; status = gsl_root_fdfsolver_iterate (s); rho0 = rho; rho = gsl_root_fdfsolver_root (s); if (rho<0.8*rho0)// limit the step size of each iteration to increase stability. { rho = 0.8*rho0; gsl_root_fdfsolver_set (s, &FDF, rho); } else if (rho>1.2*rho0) { rho = 1.2*rho0; gsl_root_fdfsolver_set (s, &FDF, rho); } status = gsl_root_test_delta (rho, rho0, 1E-16, rho_eps_rel); } while (status == GSL_CONTINUE && gsl_finite(rho) && iter < max_iter); if (!gsl_finite(rho)) { if (verbose) cout<<"Warning: Can't find the density for "<::quiet_NaN(); } else if (status == GSL_CONTINUE) { if (verbose) cout<<"Warning: Can't find the density for "<::quiet_NaN(); } gsl_root_fdfsolver_free (s); return rho; } } else { if (!gsl_finite(mmol)) { cout<<"Error: The mean molecular weight of "<= 8 && (!gsl_finite(n)||!gsl_finite(gamma0)||!gsl_finite(gamma0p)||!gsl_finite(V0)||!gsl_finite(beta)||!gsl_finite(T0)||!(bn>0))) cout<<"Error: Don't have enough input parameters to calculate the density of "<::quiet_NaN() struct EOS_params params = {{P, T}, this}; if(rho_guess < 0.5 || !gsl_finite(rho_guess) || dP_EOS(rho_guess, ¶ms) < 0) // rho_guess will be set to negative if it is unknown. Ideal gas doesn't need a rho_guess. // if rho_guess is too small, dP/drho can be negative, and the solver may be tricked to the unphysical branch of the solution. { rho_guess = density(V0) + P/1E3; } int iter = 0, max_iter = 100; const gsl_root_fdfsolver_type *TPL = gsl_root_fdfsolver_newton; gsl_root_fdfsolver *s = gsl_root_fdfsolver_alloc (TPL); gsl_function_fdf FDF; double rho = rho_guess, rho0; FDF.f = &P_EOS; FDF.df = &dP_EOS; FDF.fdf = &PdP_EOS; FDF.params = ¶ms; gsl_root_fdfsolver_set (s, &FDF, rho); do { iter++; status = gsl_root_fdfsolver_iterate (s); rho0 = rho; rho = gsl_root_fdfsolver_root (s); if (rho<0.95*rho0)// limit the step size of each iteration to increase stability. { rho = 0.95*rho0; gsl_root_fdfsolver_set (s, &FDF, rho); } else if (rho>1.05*rho0) { rho = 1.05*rho0; gsl_root_fdfsolver_set (s, &FDF, rho); } status = gsl_root_test_delta (rho, rho0, 1E-16, rho_eps_rel); } while (status == GSL_CONTINUE && gsl_finite(rho) && iter < max_iter); if (!gsl_finite(rho)) { if (verbose) cout<<"Warning: Can't find the density for "<::quiet_NaN(); } else if (status == GSL_CONTINUE) { if (verbose) cout<<"Warning: Can't find the density for "<::quiet_NaN(); } gsl_root_fdfsolver_free (s); if(thermal_type == 9 && T>T0) // thermal expansion // convert alpha to K^-1 return rho*exp(-1E-6*pow(1+K0p*P/K0, -xi)*(alpha0*(T-T0)+0.5*alpha1*(sq(T)-sq(T0)))); else return rho; } } void EOS::printEOS() // Create a tabulated EOS at ./tabulated/phasename.txt // The table from a pressure of 0.1GPa (or P0 if it is larger) to 2000 GPa at the temperature T0 (default 300 K) of the EOS. { string filename = "./tabulated/" + phasetype + ".txt"; ofstream fout(filename.c_str(),ofstream::trunc); if(!fout) { cout<<"Error: Failed to open file "< 2000*1e10) break; rho_guess = density(P,T0,rho_guess); fout << P/1e10 << "\t " << rho_guess << endl; P *= 1.1; } } double EOS::entropy(double rho, double T) // Given the density and temperature, calculate the entropy over n*R, or P V^{7/5} / R = T V^0.4 for ideal gas. // At either constant P or constant V, entropy always increases with temperature (y decreases with temperature). { double gamma; if (eqntype == 6) // ideal gas, S ~ nR log(T^(1/(gamma-1))*V) + const. For better performance and more concise code, here returns T rho^{1-gamma}. // van der Waals, returns T (V-b)^{gamma-1}. { gamma = this->adiabatic_index(); if (!gsl_finite(a_vdW) || !gsl_finite(b_vdW)) return T*pow(rho,1-gamma); else { double V = volume(rho); return T*pow(V-1E3*b_vdW, gamma-1); } } if (entropy_extern) return entropy_extern(rho, T); double Theta, V = mmol/rho, x = V/V0; if (!gsl_finite(V0) || thermal_type < 5 || thermal_type == 8 || thermal_type == 9) // don't have thermal pressure data, or outside the range of the EOS, return negative number return -1; DebyeT(x, gamma, Theta); double y = Theta/T; double Sth; if (Debye_approx == true) // Debye model { Sth = 4*gsl_sf_debye_3(y) - 3*log(1-exp(-y)); } else // Einstein model { Sth = 3 * (y/(exp(y)-1) - log(1-exp(-y))); } if (!gsl_finite(e0) || !gsl_finite(g)) // don't have enough information to get Pe return Sth; else { return Sth + 3E-6*e0*pow(x,g)*T; } } double P_EOS(double rho, void *params) // function of EOS, given a rho (in cgs), return the difference between pressure from EOS and target P (in GPa). Let this function equals 0 to solve for the correct rho. { struct EOS_params *p = (struct EOS_params *) params; double P = p->x[0]; double T = p->x[1]; EOS* Phase = p->Phase; if (P < Phase->getP0()) { if (verbose) cout<<"Warning: Incorrect phase diagram. "<getEOS()<<" doesn't exist at pressure "<getP0()<<" GPa. The transition pressure applied."<getP0() - P; } return Phase->Press(rho, T) - P; } double dP_EOS(double rho, void *params) { gsl_function F; double result, abserr; F.function = &P_EOS; F.params = params; gsl_deriv_central(&F, rho, 1E-4, &result, &abserr); return result; } void PdP_EOS(double rho, void *params, double *P, double *dP) { *P=P_EOS(rho,params); *dP=dP_EOS(rho,params); } double EOS::gamma0S(double V) // Grueneisen parameter along the reference adiabat (eq A.3), take volume in cm^3 / mol { double a1 = 6*gamma0; double a2 = -12*gamma0 + 36*sq(gamma0) -18*gamma0p; double f = fV(V); return (2*f + 1) * (a1 + a2*f) / 6 / (1 + a1*f + 0.5*a2*sq(f)); } double EOS::bV(double V) // thermal coefficients b(V) in erg/mol (Eq.10), take volume in cm^3 / mol { double sum=0; double Vdev = (V/V0-1); for (int i=0; i::quiet_NaN(); } double alphaP0; // alpha at P=0 if (!gsl_finite(alpha0)) { alphaP0 = alpha1*T; } else { if (T>T0) alphaP0 = alpha0 + alpha1*T; else // avoid alpha becomes negative at low temperature alphaP0 = alpha0 + alpha1*T0; } return 1E-6 * alphaP0 * pow(1+K0p*P/K0, -xi); } double EOS::Press(double rho, double T) // pressure in GPa (Eq. 6, 13, 14) in Wolf&Bower 2018, take density in g/cm^3. For thermal expansion representation, this return the pressure at T0. { double P; double V = volume(rho); // volume in cm^3/mol switch(geteqntype()) { case 0: // BM3 case 8: P = BM3(rho); break; case 1: // BM4 case 9: P = BM4(rho); break; case 2: // Vinet case 10: P = Vinet(rho); break; case 3: // Holzapfel case 11: P = Holzapfel(rho); break; case 4: // Keane case 12: P = Keane(rho); break; case 6: // van der Waals gas P = vdW_gas(rho, T); return P; default: cout<<"Error: No such EOS type "< 4 || getthermal() == 2)) // Pth = 0 if getthermal == 9 // thermal pressure P+= Pth(V,T); return P; } double S_T(double V, void *params) // entropy at constant T, volume in cm^3/mol { struct EOS_params *p = (struct EOS_params *) params; EOS* Phase = p->Phase; double rho = Phase->density(V); double T = p->x[0]; return Phase->entropy(rho, T); } double S_V(double T, void *params) // entropy at constant V, volume in cm^3/mol { struct EOS_params *p = (struct EOS_params *) params; EOS* Phase = p->Phase; double rho = Phase->density(p->x[0]); return Phase->entropy(rho, T); } double EOS::pSpV_T(double V, double T) // partial S (entropy) partial V at constant T { gsl_function F; double result, abserr; struct EOS_params params = {{T}, this}; F.function = &S_T; F.params = ¶ms; gsl_deriv_central(&F, V, 1E-4, &result, &abserr); return result; } double EOS::pSpT_V(double V, double T) // partial S (entropy) partial T at constant V { gsl_function F; double result, abserr; struct EOS_params params = {{V}, this}; F.function = &S_V; F.params = ¶ms; gsl_deriv_central(&F, T, 1E-2, &result, &abserr); return result; } double EOS::dTdV_S(double V, double P, double T) // adiabatic temperature gradient in K mol/cm^3, take volume in cm^3 / mol, P in GPa { if (thermal_type == 1) // has external entropy // dT/dV_S = - (dS/dV_T) / (dS/dT_V) { return - pSpV_T(V,T) / pSpT_V(V,T); } if (eqntype == 6) // ideal gas { if (!gsl_finite(mmol)) { cout<<"Error: The mean molecular weight of "<Phase; double T = p->x[0]; return Phase->Press(rho, T); } double P_rho(double T, void *params) // pressure at constant rho in GPa, density in g/cm^3 { struct EOS_params *p = (struct EOS_params *) params; EOS* Phase = p->Phase; double rho = p->x[0]; return Phase->Press(rho, T); } double EOS::pPprho_T(double rho, double T) // partial P partial rho at constant T in GPa / g/cm^3 { gsl_function F; double result, abserr; struct EOS_params params = {{T}, this}; F.function = &P_T; F.params = ¶ms; gsl_deriv_central(&F, rho, 1E-4, &result, &abserr); return result; } double EOS::pPpT_rho(double rho, double T) // partial P partial T at constant rho in GPa / K { gsl_function F; double result, abserr; struct EOS_params params = {{rho}, this}; F.function = &P_rho; F.params = ¶ms; gsl_deriv_central(&F, T, 1E-2, &result, &abserr); // 1E-2 is the step size in conducting derivative. A step size too small will increase the error due to machine error. A value too large may also increase the error and may even crash the code. return result; } double EOS::dTdm(double m, double r, double rho, double P, double T) // adiabatic temperature gradient in K/g, P in cgs { if (eqntype == 6) // gas { if (!gsl_finite(mmol)) { cout<<"Error: The mean molecular weight of "<400 && verbose) cout<<"Warning: At the center of of the planet when conducting the first step ODE integration, the material density is "<Ptable[nline/tlen-1] && T>temptable[tlen-1]) return adiabattable[nline-1]; else if(PPtable[nline/tlen-1]) { gsl_spline2d_eval_e(spline2dadi, T, Ptable[nline/tlen-1], accT, accP, &adiabat); return adiabat/1E10; } else if(T < temptable[0]) { gsl_spline2d_eval_e(spline2dadi, temptable[0], P, accT, accP, &adiabat); return adiabat/1E10; } else { gsl_spline2d_eval_e(spline2dadi, temptable[tlen-1], P, accT, accP, &adiabat); return adiabat/1E10; } } else return adiabat/1E10; } rho_guess = density(P*1E10, T, rho_guess); double V = volume(rho_guess); double dTdV = dTdV_S(V, P, T); return -V*dTdV/(rho_guess*pPprho_T(rho_guess,T)); } double P_EOS_S(double rho, void *params) // function used to solve volume and temperature along adiabatic temperature profile with known temperature gradient. Read in rho, return the difference between pressure from EOS and target P (in GPa). Let this function equals 0 to solve for the correct rho. { struct EOS_params *p = (struct EOS_params *) params; double P2 = p->x[0]; double T1 = p->x[1]; double rho1 = p->x[2]; double dTdV = p->x[3]; EOS* Phase = p->Phase; double V = Phase->volume(rho); double V1 = Phase->volume(rho1); return Phase->Press(rho, T1+(V-V1)*dTdV) - P2; } double dP_EOS_S(double rho, void *params) { gsl_function F; double result, abserr; F.function = &P_EOS_S; F.params = params; gsl_deriv_central(&F, rho, 1E-4, &result, &abserr); return result; } void PdP_EOS_S(double rho, void *params, double *P, double *dP) { *P=P_EOS_S(rho,params); *dP=dP_EOS_S(rho,params); } double EOS::density(double P1, double T1, double rho, double P2, double &T2) // Given the pressure (cgs), temperature, density of the previous step, the pressure of the next step, return the temperature and density at the new pressure. This solver doesn't conserve the entropy well enough. Only used as an approximation in the first integration step from the core of the planet where dTdm has 0/0 limit. { if( !gsl_finite(P1) || !gsl_finite(P2) || !gsl_finite(T1) || !gsl_finite(rho)) // Check if P, the guess of T and rho is infinite or nan due to some error. Stop code to avoid further error. { if (verbose) cout<<"Warning: Request density for "<= 8 && (!gsl_finite(n)||!gsl_finite(gamma0)||!gsl_finite(gamma0p)||!gsl_finite(V0)||!gsl_finite(beta)||!gsl_finite(T0)||!(bn>0))) { cout<<"Error: Don't have enough input parameters to calculate the density of "<::quiet_NaN(); return numeric_limits::quiet_NaN(); } else if (thermal_type >=4 && thermal_type <8 && (!gsl_finite(V0) || !gsl_finite(mmol)||!gsl_finite(gamma0))) { cout<<"Error: Don't have enough input parameters to calculate the density of "<::quiet_NaN(); return numeric_limits::quiet_NaN(); } else if (thermal_type ==1 && !entropy_extern) { cout<<"Error: Don't have user defined external entropy function to calculate the density of "<::quiet_NaN(); return numeric_limits::quiet_NaN(); } else if (thermal_type == 9 && (!gsl_finite(cp_a) || !gsl_finite(alpha0) || !gsl_finite(T0) || !gsl_finite(mmol))) { cout<<"Error: Information of phase "<::quiet_NaN(); return numeric_limits::quiet_NaN(); } // Use dTdP_S for adiabatic temperature gradient calculation if(rho < 0.01 || !gsl_finite(rho)) // rho will be set to negative if it is unknown. rho = density(V0) + P2/1E13; T2 = T1 + dTdP_S(P1/1E10, T1, rho) * (P2/1E10 - P1/1E10); return density(P2, T2, rho); } } // Define a structure to hold the parameters typedef struct { double P; double T; double (*pressure_func)(double, double); } density_params; // Define the function to solve for the root double fP_EOS(double rho, void *params) { density_params *p = (density_params *)params; return p->pressure_func(rho, p->T) - p->P; // f(rho) = Pressure(rho, T) - P } // Define the derivative of the function using gsl_deriv_central double fdP_EOS(double rho, void *params) { // Use gsl_deriv_central to compute the derivative double result, abserr; gsl_function F; F.function = &fP_EOS; // Directly use f as the function F.params = params; gsl_deriv_central(&F, rho, 1e-4, &result, &abserr); return result; } // Define the function and its derivative together void fdfP_EOS(double rho, void *params, double *f, double *df) { density_params *p = (density_params *)params; *f = p->pressure_func(rho, p->T) - p->P; // f(rho) = Pressure(rho, T) - P *df = fdP_EOS(rho, params); // Use the df function defined above } double density_solver(double P, double T, double (*pressure_func)(double rho, double T), double rho_guess) // Given the function pressure_func, which calculates pressure at (rho, T), use a root solver to find the density (rho) at the given pressure (P) in microbar and temperature (T). { if(!gsl_finite(P) || !gsl_finite(T)) // Check if P or T is infinite or nan due to some error. Stop code to avoid further error. { if (verbose) cout<<"Warning: Request density at infinite/nan value. P="<

::quiet_NaN(); } else if (status == GSL_CONTINUE) { if (verbose) cout<<"Warning: Can't find the density at pressure "<::quiet_NaN(); } gsl_root_fdfsolver_free (s); return rho; }