/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.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 . * ******************************************************************************/ #ifndef SWIFT_RT_UNPHYSICAL_GEAR_H #define SWIFT_RT_UNPHYSICAL_GEAR_H /** * @file src/rt/GEAR/rt_unphysical.h * @brief Routines for checking for and correcting unphysical scenarios */ /** * @brief check for and correct if needed unphysical * values for a radiation state. * * @param energy_density pointer to the radiation energy density * @param flux pointer to radiation flux (3 dimensional) * @param e_old energy density before change to check. Set = 0 if not available * @param callloc integer indentifier where this function was called from */ __attribute__((always_inline)) INLINE static void rt_check_unphysical_state( float* energy_density, float* flux, const float e_old, int callloc) { /* Check for negative energies */ /* Note to self for printouts: Maximal allowable F = E * c. * In some cases, e.g. while cooling, we don't modify the fluxes, * so you can get an estimate of what the photon energy used to be * by dividing the printed out fluxes by the speed of light in * code units */ #ifdef SWIFT_DEBUG_CHECKS float ratio = -1.f; char print = 0; if (e_old == 0.) { if (*energy_density < -1.e-4f) print = 1; } else { if (fabsf(*energy_density) > 1.e-30) { if (*energy_density < -1.e-4f * fabsf(e_old)) print = 1; ratio = fabsf(*energy_density / e_old); } } /* callloc = 1 is gradient extrapolation. Don't print out those. */ if (callloc == 1) print = 0; if (print) message("Fixing unphysical energy case %d | %.6e | %.6e %.6e %.6e | %.6e", callloc, *energy_density, flux[0], flux[1], flux[2], ratio); #endif if (isinf(*energy_density) || isnan(*energy_density)) error("Got inf/nan radiation energy case %d | %.6e | %.6e %.6e %.6e", callloc, *energy_density, flux[0], flux[1], flux[2]); if (*energy_density <= 0.f) { *energy_density = 0.f; flux[0] = 0.f; flux[1] = 0.f; flux[2] = 0.f; return; } /* Check for too high fluxes */ const double flux2 = flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * flux[2]; const double flux_norm = sqrt(flux2); const double flux_max = rt_params.reduced_speed_of_light * *energy_density; if (flux_norm > flux_max) { const double correct = flux_max / flux_norm; flux[0] *= correct; flux[1] *= correct; flux[2] *= correct; } } /** * @brief Do additional checks after reading in initial conditions, and exit on * error. * * @param p particle we're checking * @param group current photon group we're checking * @param energy_density pointer to the radiation energy density * @param flux pointer to radiation flux (3 dimensional) * @param c the speed of light (in internal units). NOT the reduced speed of * light. */ __attribute__((always_inline)) INLINE static void rt_check_unphysical_state_ICs( const struct part* restrict p, int group, float* energy_density, float* flux, const double c) { /* Nothing to do here. The other unphysical check will catch other problems. */ if (*energy_density == 0.f) return; /* Check for negative energies */ if (*energy_density < 0.f) error( "Found particle with negative energy density after reading in ICs: " "pid= %lld group=%d E=%.6g", p->id, group, *energy_density); if (*energy_density > FLT_MAX || isnan(*energy_density)) error("Got inf/nan energy_density: %g", *energy_density); /* Check for too high fluxes */ const float flux2 = flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * flux[2]; const float flux_norm = sqrtf(flux2); const float flux_max = c * *energy_density; if (flux_max > FLT_MAX || isnan(flux_max)) error("Got inf/nan flux_max: %g", flux_max); if (flux_norm > FLT_MAX || isnan(flux_norm)) error("Got inf/nan flux_norm: %g", flux_norm); if (flux_norm > flux_max * 1.0001) { error( "Found too high radiation flux for a particle: pid=%lld, group=%d, " "have=%.6g, max=%.6g", p->id, group, flux_norm, flux_max); } } /** * @brief check for and correct if needed unphysical * values for a flux in the sense of hyperbolic conservation laws * * @param flux hyperbolic flux: 4 components (photon energy + * photon flux) x 3 dimensions each */ __attribute__((always_inline)) INLINE static void rt_check_unphysical_hyperbolic_flux(float flux[4][3]) { #ifdef SWIFT_DEBUG_CHECKS int nans = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (isnan(flux[i][j])) { nans += 1; break; } } } if (nans) { message( "Fixing unphysical hyperbolic flux:" " %.3e %.3e %.3e | %.3e %.3e %.3e |" " %.3e %.3e %.3e | %.3e %.3e %.3e", flux[0][0], flux[0][1], flux[0][2], flux[1][0], flux[1][1], flux[1][2], flux[2][0], flux[2][1], flux[2][2], flux[3][0], flux[3][1], flux[3][2]); } #endif for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (isnan(flux[i][j])) { flux[i][j] = 0.f; } } } } /** * @brief check whether gas species mass fractions have physical * values and correct small errors if necessary. * * @param p particle to work on */ __attribute__((always_inline)) INLINE static void rt_check_unphysical_mass_fractions(struct part* restrict p) { /* For now, catch either mass or rho being zero. At the moment, they are not * necessarily both zero. For example, an unphysical check may zero out both * mass and density when it becomes negative in a hydro step. Once that * happens, particles may gain mass through flux exchanges with other active * particles, while they themselves remain inactive. The density of such * inactive particles however remains zero until the particle is active * again. See issue #833. */ if (hydro_get_mass(p) <= 0.f || p->rho <= 0.f) { /* Deal with unphysical situations and vacuum. */ p->rt_data.tchem.mass_fraction_HI = RT_GEAR_TINY_MASS_FRACTION; p->rt_data.tchem.mass_fraction_HII = RT_GEAR_TINY_MASS_FRACTION; p->rt_data.tchem.mass_fraction_HeI = RT_GEAR_TINY_MASS_FRACTION; p->rt_data.tchem.mass_fraction_HeII = RT_GEAR_TINY_MASS_FRACTION; p->rt_data.tchem.mass_fraction_HeIII = RT_GEAR_TINY_MASS_FRACTION; return; } #ifdef SWIFT_RT_DEBUG_CHECKS if (p->rt_data.tchem.mass_fraction_HI < -1e4) message("WARNING: Got negative HI mass fraction?"); if (p->rt_data.tchem.mass_fraction_HII < -1e4) message("WARNING: Got negative HII mass fraction?"); if (p->rt_data.tchem.mass_fraction_HeI < -1e4) message("WARNING: Got negative HeI mass fraction?"); if (p->rt_data.tchem.mass_fraction_HeII < -1e4) message("WARNING: Got negative HeII mass fraction?"); if (p->rt_data.tchem.mass_fraction_HeIII < -1e4) message("WARNING: Got negative HeIII mass fraction?"); #endif /* TODO: this should be a for loop with mass fractions being enums. */ p->rt_data.tchem.mass_fraction_HI = max(p->rt_data.tchem.mass_fraction_HI, RT_GEAR_TINY_MASS_FRACTION); p->rt_data.tchem.mass_fraction_HII = max(p->rt_data.tchem.mass_fraction_HII, RT_GEAR_TINY_MASS_FRACTION); p->rt_data.tchem.mass_fraction_HeI = max(p->rt_data.tchem.mass_fraction_HeI, RT_GEAR_TINY_MASS_FRACTION); p->rt_data.tchem.mass_fraction_HeII = max(p->rt_data.tchem.mass_fraction_HeII, RT_GEAR_TINY_MASS_FRACTION); p->rt_data.tchem.mass_fraction_HeIII = max(p->rt_data.tchem.mass_fraction_HeIII, RT_GEAR_TINY_MASS_FRACTION); const float XHI = p->rt_data.tchem.mass_fraction_HI; const float XHII = p->rt_data.tchem.mass_fraction_HII; const float XHeI = p->rt_data.tchem.mass_fraction_HeI; const float XHeII = p->rt_data.tchem.mass_fraction_HeII; const float XHeIII = p->rt_data.tchem.mass_fraction_HeIII; const float Xtot = XHI + XHII + XHeI + XHeII + XHeIII; /* Make sure we sum up to 1. TODO: Assuming we have no metals. */ if (fabsf(Xtot - 1.f) > 1e-3) error("Got total mass fraction of gas = %.6g", Xtot); } #endif /* SWIFT_RT_UNPHYSICAL_GEAR_H */