/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Tsang Keung Chan (chantsangkeung@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * ******************************************************************************/ /** * @file src/rt/SPHM1RT/rt_rate_equations.c * @brief Thermo-chemistry rate equation for * SPHM1RT method described in Chan+21: 2102.08404 */ /* Local includes. */ #include "rt_cooling_rates.h" #include "rt_species_and_elements.h" /* Local includes. */ #include #include /* access to CVDls interface */ #include #include #include #include /** * @brief Defines the right-hand side of the system of differential equations * (dy/dt = ydot). * * Defines the system of differential equations that make * up the right-hand side function, which will be integrated * by CVode. * * @param t Current time. * @param y Vector containing the variables to be integrated. * @param ydot Vector containing the time derivatives of the variables. * @param user_data The #RTUserData struct containing the input data. */ int rt_frateeq(realtype t, N_Vector y, N_Vector ydot, void *user_data) { struct RTUserData *data; data = (struct RTUserData *)user_data; /* First, loop through the enum types of all * non-eq species. If they are included in * the network then their abundance is in * the vector y. */ int icount = 0; /* We use this to keep track of where we are in the vector y */ int aindex[3]; for (int i = 0; i < 3; i++) { aindex[i] = data->aindex[i]; } for (int i = 0; i < 3; i++) { data->abundances[aindex[i]] = (double)NV_Ith_S(y, icount); icount += 1; } /* Update the species not in the network */ double finish_abundances[rt_species_count]; rt_enforce_constraint_equations(data->abundances, data->metal_mass_fraction, finish_abundances); for (int spec = 0; spec < rt_species_count; spec++) { data->abundances[spec] = finish_abundances[spec]; } /* If Thermal Evolution is switched on, the element in the * vector y is the internal energy (per unit volume). Use this * to update the temperature, and also the rates that depend on T */ double u_cgs; if (data->coolingon == 1) { u_cgs = (double)NV_Ith_S(y, icount); if (data->u_min_cgs > u_cgs) { u_cgs = data->u_min_cgs; } icount += 1; } else { u_cgs = data->u_cgs; if (data->u_min_cgs > u_cgs) { u_cgs = data->u_min_cgs; } } /* the final element in the * vector y is the photon density. * */ double ngamma_cgs[3]; if (data->fixphotondensity == 0) { for (int i = 0; i < 3; i++) { ngamma_cgs[i] = (double)NV_Ith_S(y, icount); icount += 1; } } else { for (int i = 0; i < 3; i++) { ngamma_cgs[i] = data->ngamma_cgs[i]; } } double T_cgs = rt_convert_u_to_temp(data->k_B_cgs, data->m_H_cgs, data->metal_mass_fraction[rt_chemistry_element_H], u_cgs, data->abundances); const double T_min_cgs = rt_convert_u_to_temp(data->k_B_cgs, data->m_H_cgs, data->metal_mass_fraction[rt_chemistry_element_H], data->u_min_cgs, data->abundances); if (T_min_cgs > T_cgs) { T_cgs = T_min_cgs; } // Update rates double alphalist[rt_species_count], betalist[rt_species_count], Gammalist[rt_species_count], sigmalist[3][3], epsilonlist[3][3]; if (data->useparams == 1) { betalist[rt_sp_elec] = 0.0; betalist[rt_sp_HI] = data->beta_cgs_H; betalist[rt_sp_HII] = 0.0; betalist[rt_sp_HeI] = 0.0; betalist[rt_sp_HeII] = 0.0; betalist[rt_sp_HeIII] = 0.0; alphalist[rt_sp_elec] = 0.0; alphalist[rt_sp_HI] = 0.0; alphalist[rt_sp_HeI] = 0.0; if (data->onthespot == 1) { alphalist[rt_sp_HII] = data->alphaB_cgs_H; alphalist[rt_sp_HeII] = 0.0; alphalist[rt_sp_HeIII] = 0.0; } else { alphalist[rt_sp_HII] = data->alphaA_cgs_H; alphalist[rt_sp_HeII] = 0.0; alphalist[rt_sp_HeIII] = 0.0; } sigmalist[0][0] = data->sigma_cross_cgs_H[0]; sigmalist[1][0] = data->sigma_cross_cgs_H[1]; sigmalist[2][0] = data->sigma_cross_cgs_H[2]; sigmalist[0][1] = 0.0; sigmalist[1][1] = 0.0; sigmalist[2][1] = 0.0; sigmalist[0][2] = 0.0; sigmalist[1][2] = 0.0; sigmalist[2][2] = 0.0; for (int spec = 0; spec < rt_species_count; spec++) { Gammalist[spec] = 0.0; } for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { epsilonlist[i][j] = 0.0; } } } else { rt_compute_rate_coefficients(T_cgs, data->onthespot, alphalist, betalist, Gammalist, sigmalist, epsilonlist); } // Compute creation and destruction rates double absorption_rate[3], chemistry_rates[rt_species_count]; rt_compute_radiation_rate(data->n_H_cgs, data->cred_cgs, data->abundances, ngamma_cgs, sigmalist, aindex, absorption_rate); rt_compute_chemistry_rate(data->n_H_cgs, data->cred_cgs, data->abundances, ngamma_cgs, alphalist, betalist, sigmalist, aindex, chemistry_rates); double Lambda_net_cgs; Lambda_net_cgs = rt_compute_cooling_rate( data->n_H_cgs, data->cred_cgs, data->abundances, ngamma_cgs, Gammalist, sigmalist, epsilonlist, aindex); int jcount = 0; /* Now set the output ydot vector for the chemical abundances */ for (int i = 0; i < 3; i++) { NV_Ith_S(ydot, jcount) = (realtype)(chemistry_rates[aindex[i]] / data->n_H_cgs); jcount += 1; } /* Now set the output ydot vector for the internal energy */ if (data->coolingon == 1) { NV_Ith_S(ydot, jcount) = (realtype)(Lambda_net_cgs / data->rho_cgs); jcount += 1; } /* Now set the output ydot vector for the radiation density */ if (data->fixphotondensity == 0) { for (int i = 0; i < 3; i++) { NV_Ith_S(ydot, jcount) = (realtype)(-absorption_rate[i]); jcount += 1; } } return (0); }