.. _program_listing_file_src_hydro.cpp: Program Listing for File hydro.cpp ================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/hydro.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "hydro.h" #include "EOSlist.h" #include int derivs_m(double x, const double y[], double dydx[], void * params) // derivative equations // two equations, x is mass, y0 is radius, y1 is P. // Using mass instead of radius as variable because the integral upper limit is given with mass. // Only works for constant room temperature and no gas. { struct double_params *p=(struct double_params *) params; double rho= p->x[0]; double MC = p->x[1]; double MM = p->x[2]; double MW = p->x[3]; double Teq = p->x[4]; if (y[1] < 0) { p->x[0] = -1; dydx[0] = -1/(4*pi*sq(y[0])); dydx[1] = -G*x/(4*pi*pow(y[0],4)); return GSL_SUCCESS; } EOS* Phase=find_phase(x,MC,MM,MW,0,y[1],Teq); if (!Phase) { if (verbose) cout<<"Warning: Can't find the phase when conducting constant temperature integral at location: r="<density(y[1],Teq,rho); if (!gsl_finite(rho)) { if (verbose) cout<<"Warning: Can't find the density for "<getEOS()<<" when performing constant temperature integral at location: r="<x[0] = rho; if (!gsl_finite(dydx[0]) || !gsl_finite(dydx[1])) { if (verbose) cout<<"Warning: Derivative not finite. Can't find the density for "<getEOS()<<" when performing constant temperature integral at location: r="< x[0]; int thermal = (int)p -> x[1]; vector M = p -> M; EOS *Phase = p -> Phase; double Mtot = accumulate(M.begin(), M.end(), 0.0) * ME; rho = Phase -> density(y[1], y[2], rho); if (!gsl_finite(rho)) { if (verbose) { cout<<"Warning: Can't find density for "<getEOS()<<" when performing outside-in integral against m at location: r="< M.size()); i++) cout<

M[i]<<' '; cout<dTdP_S(y[1], y[2], rho)*dydx[1]; else dydx[2] = -Phase->dTdm(Mtot-x, y[0], rho, y[1], y[2]); p -> x[0] = rho; if (!gsl_finite(dydx[0]) || !gsl_finite(dydx[1]) || !gsl_finite(dydx[2])) { if (verbose) { cout<<"Warning: Derivative not finite. Can't find density for "<getEOS()<<" when performing outside-in integral against m at location: r="< M.size()); i++) cout<

M[i]<<' '; cout< x[0]; int thermal = (int)p -> x[1]; vector M = p -> M; EOS *Phase = p -> Phase; rho = Phase -> density(y[1], y[2], rho); if (!gsl_finite(rho)) { if (verbose) { cout<<"Warning: Can't find density for "<getEOS()<<" when performing inside-out integral against m at location: r="< M.size()); i++) cout<

M[i]<<' '; cout<dTdP_S(y[1], y[2], rho)*dydx[1]; else dydx[2] = Phase->dTdm(x, y[0], rho, y[1], y[2]); p -> x[0] = rho; if (!gsl_finite(dydx[0]) || !gsl_finite(dydx[1])) { if (verbose) { cout<<"Warning: Derivative not finite. Can't find density for "<getEOS()<<" when performing inside-out integral against m at location: r="< M.size()); i++) cout<

M[i]<<' '; cout< &Comp_in, vector Mass_Comp, vector Tgap, double ode_tol, double P0, bool isothermal):ode_status(2) // Given the test planet radius, a vector of components, their masses, and the temperature discontinuity between each gaps (Temperature at the interior boundary minus exterior boundary. The last temperature in the list is the planet equilibrium temperature). Bool isothermal determines whether use isothermal temperature profile or self-consistent calculation. The function integrates hydrostatic equation outside in. The integration end when either the target mass reached or radius approaches 0. // Integration inside out need to fine tune the center pressure. The center pressure is way larger than the surface pressure. The required accuracy for the center pressure and the integration process is higher than double. // Integration outside-in will cause the same problem for the center mass. It's hard to shoot the exact mass at the center. // Integration outside in is more straight forward for temperature because only the temperature at outer boundary is set. // Because the density has discontinuity at the shift between layers, the integration is operated within each layers. And it's natural to choose mass as variable. { if (Comp_in.size() != Mass_Comp.size() || Mass_Comp.size() != Tgap.size()) { cout<<"Error. The length of input vectors doesn't match."<getEOS()<<" at outer boundary in the first round of mode 0 integration with initial radius Rp="< density(P[0], T[0], -1); if (!gsl_finite(rhot)) //Failed to find the density { if (verbose) cout<<"Warning: Can't find density for "<getEOS()<<" at outer boundary in the first round of mode 0 integration with initial radius Rp="<= 0; i--) { Phase = Comp[i].find_phase(P[0], T[0]); params.Phase = Phase; thermal = isothermal ? 0 : Phase->getthermal(); thermal = Phase->getthermal()==4 ? 4 : thermal; //for type 4, the isentrope temperature profile is enforced to the phase with this option even the isothermal option is chosen for the planet solver. params.x[1] = (double)thermal; if (!Phase) { if (verbose) { cout<<"Warning: Can't find the phase for component: "< density(y[1], y[2], -1); params.x[0] = rhot; rb.insert(rb.begin(), y[0]); P.insert(P.begin(), y[1]); M.insert(M.begin(), Mtot - m); T.insert(T.begin(), y[2]); rho.insert(rho.begin(), rhot); Phaselist.insert(Phaselist.begin(), Phase); mmax = accumulate(M_Comp.begin()+i, M_Comp.end(), 0.0) * ME; while (m < mmax && y[1] < 1E15) // integrate to center. If the mass is larger than 0 at the center, the gravity will diverge at the center. Therefore cut the integration at 1E5 GPa. { status = gsl_odeiv2_evolve_apply (e, c, s, &sys, &m, mmax, &h, y); if (status != GSL_SUCCESS) { if (status == GSL_FAILURE && y[0] > 1E-4 * Rp) { if (verbose) cout<<"Warning: In the first round integration of mode 0, at radius "<1E-13) { h/=2; gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); continue; } } else if (verbose) cout<<"Warning: In the first round integration of mode 0, at radius "< density(P[0], y[2], rhot); if (!new_Phase) { if (verbose) { cout<<"Warning: Can't find the phase for component: "<hmax) // Last step size too large { h = M[0]-Mtot+m; do { if (h>hmax || status == GSL_FAILURE) h/=2; if (new_Phase != Phase || status == GSL_FAILURE) { gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); params.x[0] = rho[0]; y[0] = rb[0]; y[1] = P[0]; y[2] = T[0]; m = Mtot - M[0]; } else { rb.insert(rb.begin(), y[0]); P.insert(P.begin(), y[1]); M.insert(M.begin(), Mtot - m); Phaselist.insert(Phaselist.begin(), new_Phase); T.insert(T.begin(), y[2]); rho.insert(rho.begin(), rhot); } if (h density(y[1], y[2], rhot); } } while (h>hmax); } thermal = isothermal ? 0 : new_Phase->getthermal(); thermal = new_Phase->getthermal()==4 ? 4 : thermal; //for type 4, the isentrope temperature profile is enforced to the phase with this option even the isothermal option is chosen for the planet solver. params.Phase = new_Phase; Phase = new_Phase; params.x[1] = (double)thermal; } rb.insert(rb.begin(), y[0]); P.insert(P.begin(), y[1]); M.insert(M.begin(), Mtot - m); T.insert(T.begin(), y[2]); rho.insert(rho.begin(), rhot); Phaselist.insert(Phaselist.begin(), new_Phase); params.x[0] = rhot; h = min(h, 2*pow(y[0],3)*rhot); //cout<getEOS()<<" thermal_type "<getthermal()<<" params thermal "<1 && M_Comp[i-1]*ME<1) //Massless layer, skipped { i--; R_Comp[i-1] = y[0]; y[2] += Tgap[i-1]; } if ( y[1] >= 1E15 || status == GSL_FAILURE || (i==1 && M_Comp[0]*ME<1)) // Reaches the pressure limit, or code diverges almost at the center. Exit iteration no matter which component current at. { for(int j = i-2; j >= 0; j-- ) R_Comp[j] = R_Comp[j+1]; break; } if (i) { gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); h = pow(rb[0], 4) * P[0] / (10 * G * M[0]); } } count_shoot++; count_step+=rb.size(); gsl_odeiv2_evolve_free (e); gsl_odeiv2_control_free (c); gsl_odeiv2_step_free (s); } hydro::hydro(double Pc, double MCin, double MMin, double MWin, double P0, double Teq):ode_status(2) // Given the center pressure, Core (iron) mass, mantle (Si) mass, water mass, integrate hydrostatic equation inside out against mass. The integration end when either the target mass reached or pressure smaller than P0. { // set up the first grid double rhot; EOS *Phase; Comp.clear(); Comp.push_back(core); Comp.push_back(mant); Comp.push_back(water); Comp.push_back(atm); M_Comp = {MCin, MMin, MWin, 0}; double Mtot = accumulate(M_Comp.begin(), M_Comp.end(), 0.0) * ME; fitM = 0; rb.resize(1,0); P.resize(1,Pc); M.resize(1,0); T.resize(1,Teq); Phase = find_phase(10,Comp, M_Comp ,Pc,Teq); if (!Phase) { if (verbose) cout<<"Warning: Can't find the phase at center with mode 1 with Pc="<density(Pc,Teq,-1)); if (!gsl_finite(rho[0])) //Failed to find the density { if (verbose) cout<<"Warning: Can't find density for "<getEOS()<<" at center with mode 1 with Pc="<getEOS()<<" after first step with mode 1 with Pc="< P0 && m1E-13) { h/=2; gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); continue; } } else { if (verbose) cout<<"Warning: In the mode 1 integration, at radius "<= MCin*ME) // Get the size of each composition layer R_Comp[0] = cbrt( pow(y[0],3.) - 3*(m-MCin*ME)/params.x[0]/4/pi ); if(R_Comp[1]<0 && m >= (MCin+MMin)*ME) R_Comp[1] = cbrt( pow(y[0],3.) - 3*(m-(MCin+MMin)*ME)/params.x[0]/4/pi ); if(R_Comp[2]<0 && m >= (MCin+MMin+MWin)*ME) R_Comp[2] = cbrt( pow(y[0],3.) - 3*(m-(MCin+MMin+MWin)*ME)/params.x[0]/4/pi ); } if(R_Comp[0]<0) R_Comp[0] = rb.back(); if(R_Comp[1]<0) R_Comp[1] = rb.back(); if(R_Comp[2]<0) R_Comp[2] = rb.back(); count_shoot++; count_step+=rb.size(); gsl_odeiv2_evolve_free (e); gsl_odeiv2_control_free (c); gsl_odeiv2_step_free (s); } hydro::hydro(double Rp, double Pc, double Tc, vector &Comp_in, vector Mass_Comp, vector Tgap, bool isothermal, double Mfit, double ode_tol, double P0, double &Ri, double &Pi, double &Ti, double &Ro, double &Po, double &To):ode_status(2) // After finishing a round of iteration of outside-in integration and receive a reasonable value of Rp, Pc, and Tc, using these values as the initial guess to integrate equations from both the surface, and the center, and meet two branches at the fitting mass Mfit (in g). ode_tol is the ode relative tolerance // The R, P, and T value at inner edge (from inside-out integration with subscript i) and outer edge (from outside-in integration with subscript o) at the connection point is passed out. { fit_iter++; if (Comp_in.size() != Mass_Comp.size() || Mass_Comp.size() != Tgap.size()) { if (verbose) cout<<"Warning. The length of input vectors doesn't match."<getEOS()<<" at the center in the second round of mode 0 integration with initial Pc="< density(y[1], Tt, -1); params.x[0] = rhot; rb.push_back(y[0]); P.push_back(y[1]); M.push_back(m); T.push_back(y[2]); rho.push_back(rhot); Phaselist.push_back(Phase); mmax = min(Mfit, accumulate(M_Comp.begin(), M_Comp.begin()+i+1, 0.0) * ME); while (m < mmax && y[1]>P0) // integrate from center to Mfit { status = gsl_odeiv2_evolve_apply (e, c, s, &sys, &m, mmax, &h, y); if (status != GSL_SUCCESS) { if (status == GSL_FAILURE) { if (verbose) cout<<"Warning: In the second round of mode 0 inside-out integration, at radius "<1E-13) { h/=2; gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); continue; } } else { if (verbose) cout<<"Warning: In the second round of mode 0 inside-out integration, at radius "< density(y[1], y[2], rhot); if (!new_Phase) { if (verbose) { cout<<"Warning: Can't find the phase for component: "<hmax) // the last step size is two large. { h = m-M.back(); do { if (h>hmax || status == GSL_FAILURE) h/=2; if (new_Phase != Phase || status == GSL_FAILURE) { gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); params.x[0] = rho.back(); y[0] = rb.back(); y[1] = P.back(); y[2] = T.back(); m = M.back(); } else { rb.push_back(y[0]); P.push_back(y[1]); M.push_back(m); Phaselist.push_back(new_Phase); T.push_back(y[2]); rho.push_back(rhot); } if (h density(y[1], y[2], rhot); } } while (h>hmax); } thermal = isothermal ? 0 : new_Phase->getthermal(); thermal = new_Phase->getthermal()==4 ? 4 : thermal; //for type 4, the isentrope temperature profile is enforced to the phase with this option even the isothermal option is chosen for the planet solver. params.Phase = new_Phase; Phase = new_Phase; params.x[1] = (double)thermal; } rb.push_back(y[0]); P.push_back(y[1]); M.push_back(m); Phaselist.push_back(new_Phase); T.push_back(y[2]); rho.push_back(rhot); params.x[0] = rhot; } if (m > (1-1E-15) * Mfit || y[1] <= P0) // reaches the fitting point or the pressure approaches 0 before the fitting mass, need correction for the fitting values. break; if (i < n_Comp-1) { R_Comp[i] = y[0]; y[2] -= Tgap[i]; if (y[2] < 0) { Tcor = Tgap[n_Comp-1]-y[2]; y[2] = Tgap[n_Comp-1]; } } while (M_Comp[i+1]*ME<1 && i density(y[1], y[2], -1); params.x[0] = rhot; rb.insert(rb.begin()+ninner, y[0]); P.insert(P.begin()+ninner, y[1]); M.insert(M.begin()+ninner, Mtot - m); T.insert(T.begin()+ninner, y[2]); rho.insert(rho.begin()+ninner, rhot); Phaselist.insert(Phaselist.begin()+ninner,Phase); mmax = min(Mtot - Mfit, accumulate(M_Comp.begin()+i, M_Comp.end(), 0.0) * ME); while (m < mmax && y[1] < 1E15) // integrate from surface to Mfit { status = gsl_odeiv2_evolve_apply (e, c, s, &sys, &m, mmax, &h, y); if (status != GSL_SUCCESS) { if (status == GSL_FAILURE) { if (verbose) cout<<"Warning: In the second round of mode 0 outside-in integration, at radius "<1E-13) { h/=2; gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); continue; } } else { if (verbose) cout<<"Warning: In the second round of mode 0 outside-in integration, at radius "< density(y[1], y[2], rhot); if (!new_Phase) { if (verbose) { cout<<"Warning: Can't find the phase for component: "<hmax) //the last step size is two large. { h = M[ninner]-Mtot+m; do { if (h>hmax || status == GSL_FAILURE) h/=2; if (new_Phase != Phase || status == GSL_FAILURE) { gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); params.x[0] = rho[ninner]; y[0] = rb[ninner]; y[1] = P[ninner]; y[2] = T[ninner]; m = Mtot - M[ninner]; } else { rb.insert(rb.begin()+ninner, y[0]); P.insert(P.begin()+ninner, y[1]); M.insert(M.begin()+ninner, Mtot - m); Phaselist.insert(Phaselist.begin()+ninner, new_Phase); T.insert(T.begin()+ninner, y[2]); rho.insert(rho.begin()+ninner, rhot); } if (h density(y[1], y[2], rhot); } } while (h>hmax); } thermal = isothermal ? 0 : new_Phase->getthermal(); thermal = new_Phase->getthermal()==4 ? 4 : thermal; //for type 4, the isentrope temperature profile is enforced to the phase with this option even the isothermal option is chosen for the planet solver. params.Phase = new_Phase; Phase = new_Phase; params.x[1] = (double)thermal; } rb.insert(rb.begin()+ninner, y[0]); P.insert(P.begin()+ninner, y[1]); M.insert(M.begin()+ninner, Mtot - m); Phaselist.insert(Phaselist.begin()+ninner, new_Phase); T.insert(T.begin()+ninner, y[2]); rho.insert(rho.begin()+ninner, rhot); params.x[0] = rhot; } if (m > (1-1E-15) * (Mtot - Mfit) || y[1] >= 1E15) // reaches the fit point break; if (i) { R_Comp[i-1] = y[0]; y[2] += Tgap[i-1]; } while (M_Comp[i-1]*ME<1 && i>1) //Massless layer, skipped { i--; R_Comp[i-1] = y[0]; y[2] += Tgap[i-1]; } if (i) { gsl_odeiv2_step_reset(s); gsl_odeiv2_evolve_reset(e); h = pow(rb[ninner], 4) * P[ninner] / (1000 * G * M[ninner]); } } for (int j=0; j < n_Comp-1; j++) // Just in case some weird things occur near the fitting point if (R_Comp[j] < 0) R_Comp[j] = Ri; Ro = rb[ninner]; Po = P[ninner]; To = T[ninner]; if ( m < 0.999*(Mtot-Mfit) && y[1] > 9E14) // The radius approachs 0 before the fitting mass, need correction for the fitting values. { if (verbose) { cout<<"Warning: Starting radius "< 1E-4*rb.back() || M[i] - Mtemp > 1E-4 *M.back() || i==int(rb.size())-1 || newPhase!=Phase) { if (newPhase!=Phase && j!=i-1 && Phase!=NULL && !debug) // make sure the previous step of the phase transition also get printed. Debug mode should have already output this { fout< getEOS()<entropy(rho[i],T[i])<<"\t "< getEOS()< getEOS()<entropy(rho[i],T[i])<<"\t "< getEOS()<rb.back()) return rb.size(); else if(rl+1) { m=(l+t)>>1; if(rb[m]<=r) l=m; else t=m; } return l; } int hydro::getLayer_from_m(double m) const // return the layer index (from 0, count from bottom) by given the mass in MEarth. M(l)<=m*MEarthM.back()) return M.size(); else if(ml+1) { mid=(l+t)>>1; if(M[mid]<=m) l=mid; else t=mid; } return l; } vector hydro::getRs() // return the radii of core, mantle, and water layer, and the total radius in the unit of earth radii. { if (!rb.empty()) { vector Rs(R_Comp); double scale = 1/RE; for(int i=0; i < int(Rs.size()); i++) Rs[i] *= scale; Rs.push_back(rb.back()*scale); return Rs; } else { cout<<"Error: Invalid planet model. Can't get boundary radius for "; for (int i=0; i < int(M_Comp.size()); i++) cout< hydro::getTs() // return the temperatures at the outer side of each component interfaces as well as planet surface { if (!rb.empty()) { vector Rs = getRs(); vector Ts; int layer; for(int i=0; i < int(Rs.size()); i++) { layer = getLayer_from_r(Rs[i]); Ts.push_back(getT(layer)); } return Ts; } else { cout<<"Error: Invalid planet model. Can't get temperature for "; for (int i=0; i < int(M_Comp.size()); i++) cout<x[2] > 0 means find a function value. p->x[2] < 0 means planet structure integration was failed. { struct loop_params *p = (struct loop_params *) params; double P0 = p -> x[2]; vector M = p -> M; vector Tgap = p -> Tgap; bool isothermal = p -> iso; double Mtot = accumulate(M.begin(), M.end(), 0.0) * ME; if (Rp < 0) return Rp - Mtot; hydro temp(Rp, p -> Comp, M, Tgap, ode_eps_rel0, P0, isothermal); if (temp.getsize() == 0) // can't find a solution { p -> x[1] = -1; p -> x[0] = numeric_limits::quiet_NaN(); return numeric_limits::quiet_NaN(); } if (temp.getM(0) < 1) // The integration reaches the target mass before r reaches 0 { p -> x[0] = temp.getR(0); p -> x[1] = temp.getP(0); return p -> x[0]; } else // the integration reaches the center before reaches the target mass. The integration is stopped at 1E5 GPa to prevent pressure diverge. Assuming the remaining matter fixed at density 1 g cm^-3. After the correction, the value should always negative because no material will have a density less than 1 g cm^3 at 1E5 GPa. The smaller the remaining mass is, the closer to zero the value gets. { p -> x[0] = -cbrt( 3 * temp.getM(0) / (4 * pi * 1) - pow(temp.getR(0), 3)); p -> x[1] = temp.getP(0); if (p -> x[0] >= 0) { if (verbose) cout<<"Warning: The density of "< Comp, M, p->x[1], temp.getT(0))->getEOS()<<" at pressure "<x[1]/1E10<<"GPa and temperature "< x[0] = -cbrt( 3 * temp.getM(0) / (4 * pi * temp.getrho(0)) - pow(temp.getR(0), 3)); } return p -> x[0]; } } void hydro::setstatus(int s) { if (s>3 || s<0) { cout<<"Error: Incorrect planet model status "< &Comp, vector M_Comp, vector ave_rho, vector Tgap, double P0, bool isothermal, double &Rp, double &Pc, double &Tc) // First round of iteration. Input: list of componsations, masses and typical densities of each layers, the temperature discontinuity between each gaps (the last temperature in the list is the planet equilibrium temperature), boolean isothermal determines whether use isothermal temperature profile or self-consistent calculation. The function iterates planet radius in order to get zero mass at the center. The suggested planet radius, central pressure and temperature are also returned. { int n_Comp = int(Comp.size()); if (int(M_Comp.size()) != n_Comp || int(ave_rho.size()) != n_Comp || int(Tgap.size()) != n_Comp) { cout<<"Error: The array lengthes provided to Rloop (first round integration in mode 0) do not match."< 0 means find a function value. success < 0 means planet structure integration was failed. struct loop_params params = {{R_rsd, success, P0}, Comp, M_Comp, Tgap, isothermal, NULL}; const gsl_root_fsolver_type *EQN = gsl_root_fsolver_brent; gsl_root_fsolver *s = gsl_root_fsolver_alloc (EQN); gsl_function F; F.function = &R_hydro; F.params = ¶ms; status = gsl_root_fsolver_set (s, &F, R_lo, R_hi); if (status == GSL_EINVAL && R_hi > R_lo)// Both shoots completed successfully and root is not straddled by the endpoints. { if (params.x[0] <= 0) // R_hydro(R_hi, ¶ms) called later. params can still reach its result. // Initial upper radius was too small { do { R_lo = R_hi; R_hi *= 1.4; iter++; if (params.x[1]<0) // planet structure integration was failed. { cout<<"Error: Failed to conduct the first round outside-in integration for "; for (int i=0; i < n_Comp; i++) cout<= max_iter || R_hi > 100*RE) { cout<<"Error: Can't find planet radius upper limit for the first round outside-in iteration."<= max_iter) { cout<<"Error: Can't find planet radius lower limit within limited steps for the first round outside-in iteration."<= 0); } gsl_root_fsolver_set (s, &F, R_lo, R_hi); // update correct endpoints that straddle target radius } else if (status == GSL_EINVAL) { cout<<"Error: The lower radius bound "< getP(0) * sqrt(1 + temp -> getR(0) / Rp); // Make a correction to the pressure Tc = temp -> getT(0); return temp; } int fitting_error(const gsl_vector *x, void *params, gsl_vector *f) // return the fitting error of R, P, and T given by the integrations from both end at the fitting point. { struct loop_params *p = (struct loop_params *) params; double Mfit = p -> x[0]; double ode_tol = p -> x[1]; double P0 = p -> x[2]; vector M = p -> M; vector Tgap = p -> Tgap; bool isothermal = p -> iso; if (p->model != NULL) delete p->model; const double Rp = gsl_vector_get(x, 0); const double Pc = gsl_vector_get(x, 1); const double Tc = gsl_vector_get(x, 2); double Ri, Pi, Ti, Ro, Po, To; p->model = new hydro(Rp, Pc, Tc, p->Comp, M, Tgap, isothermal, Mfit, ode_tol, P0, Ri, Pi, Ti, Ro, Po, To); if (p->model->getsize() == 0) // can't find a solution return GSL_FAILURE; gsl_vector_set (f, 0, (Ri-Ro) / (Ri+Ro)); // set the mass different at the fitting point and try to normalize the difference gsl_vector_set (f, 1, (Pi-Po) / (Pi+Po)); gsl_vector_set (f, 2, (Ti-To) / (Ti+To)); return GSL_SUCCESS; } hydro* fitting_method(vector &Comp, vector M_Comp, vector Tgap, vector ave_rho, double P0, bool isothermal) // Using the fitting method to integrate the model from both the center and the surface, and meet at an intermediate mass. The precedure requires an accurate initial condition, run a Rloop first to determine a good initial condition. { int n_Comp = int(M_Comp.size()); if (int(M_Comp.size()) != n_Comp || int(ave_rho.size()) != n_Comp || int(Tgap.size()) != n_Comp) { cout<<"Error: The array lengthes provided to two branch fitting (second round integration in mode 0) do not match."< 100 && verbose) cout<<"Warning: "< 5E-4*Mtot) { Mfit += accumulate(M_Comp.begin()+i+1,M_Comp.begin()+j,0)*ME + 2E-4*Mtot; break; } if (j == n_Comp-1 && verbose) cout<<"Warning: "< Mfit) break; } if (Mtot < 1E-14 * ME && verbose) cout<<"Warning: The total planet mass "<x, 0); Pc = gsl_vector_get (s->x, 1); Tc = gsl_vector_get (s->x, 2); if (status == GSL_ENOPROG || status == GSL_ENOPROGJ || status == GSL_EBADFUNC) //Iteration is not making progress towards solution. Exit the current iteration, decrease the integral tolerance and try again. { gsl_multiroot_fsolver_free (s); gsl_vector_free (x); break; } else { cout<<"Error: Multiroot solver failed to iterate. The solution failed at Rp="<f, fit_eps_rel); } while (status == GSL_CONTINUE && iter < max_iter); if (status == GSL_SUCCESS) { Rp = gsl_vector_get (s->x, 0); Pc = gsl_vector_get (s->x, 1); Tc = gsl_vector_get (s->x, 2); gsl_multiroot_fsolver_free (s); gsl_vector_free (x); if (params.model->checkdummy() != "") { cout<<"Warning: P-T of the solution pass through a dummy EOS "<checkdummy()<<". Result is not reliable. Component masses "; for (int i=0; i < n_Comp; i++) cout<setstatus(1); } else params.model->setstatus(0); return params.model; } else if (status == GSL_CONTINUE && m == 3) { cout<<"Error: Can't find planet model using two side fitting method within maximum iterations "<setstatus(2); return params.model; } else if (status == GSL_ENOPROG || status == GSL_ENOPROGJ || status == GSL_EBADFUNC || status == GSL_CONTINUE) //Iteration is not making progress towards solution { ode_tol /= 3.; } else { Rp = gsl_vector_get (s->x, 0); Pc = gsl_vector_get (s->x, 1); Tc = gsl_vector_get (s->x, 2); cout<<"Error: Multiroot solver failed to iterate. The solution failed at Rp="<x[0]; double MM = p->x[1]; double MW = p->x[2]; double Mtot = (MC+MM+MW)*ME; double P0 = p->x[4]; int nlayer; // number of layers if (Pc < 0) return Pc*1E10; hydro temp(Pc,MC,MM,MW,P0); nlayer = temp.getsize(); if (nlayer == 0) // can't find a solution { p -> x[4] = -1; return 0; } if (temp.totalM() >= Mtot) // The integration reaches the target mass before P0. The center pressure is too large. p -> x[3] = temp.getP(nlayer-1) - P0; else // the integration reaches P0 with less mass. The center pressure is too small. p -> x[3] = temp.getP(nlayer-1) - P0 - (Mtot-temp.totalM())*G*temp.totalM()/(4*pi*pow(temp.totalR(),4)); p -> x[5] = 1; return p -> x[3]; } hydro* getmass(double MC, double MM, double MW, double P0) // Given the mass of core, mantle, and water, iterate center pressure to shot for a correct water layer mass. { double Mtot = MC+MM+MW; double Pc = Mtot*1E12; if (Mtot < 1E-14 && verbose) cout<<"Warning: The total planet mass "< 0 means find a function value. success < 0 means planet structure integration was failed. struct double_params params = {{MC, MM, MW, P_rsd, P0, success}}; const gsl_root_fsolver_type *EQN = gsl_root_fsolver_brent; gsl_root_fsolver *s = gsl_root_fsolver_alloc (EQN); gsl_function F; F.function = &P_hydro; F.params = ¶ms; status = gsl_root_fsolver_set (s, &F, P_lo, P_hi); if (status == GSL_EINVAL && P_hi > P_lo)// root is not straddled by the endpoints. { if (params.x[3] <= 0) // P_hydro(P_hi, ¶ms) called later. params can still reach its result. // Initial upper central pressure was too small { do { P_lo = P_hi; P_hi *= 3; iter++; if (params.x[5]<0) // planet structure integration was failed. { cout<<"Error: Failed to conduct the mode 1 inside-out integration for (MC/MM/MW in ME) "<= max_iter) { cout<<"Error: Can't find central pressure upper limit within limited steps for mode 1 inside-out iteration for (MC/MM/MW in ME) "<= max_iter) { cout<<"Error: Can't find central pressure lower limit within limited steps for mode 1 inside-out iteration for (MC/MM/MW in ME) "<= 0); } gsl_root_fsolver_set (s, &F, P_lo, P_hi); // update correct endpoints that straddle target central pressure. } else if (status == GSL_EINVAL) { cout<<"Error: The lower pressure bound "<= P0*0.02) { temp->setstatus(3); if(verbose) cout<<"Warning: The integral accuracy is not ideal for component masses "<setstatus(0); return temp; } string hydro::checkdummy() // Check if there is any Dummy EOS used in the profile { for (int i=0; igetEOS().find("Dummy") != std::string::npos) return Phaselist[i]->getEOS(); return ""; }