diff --git a/compas_python_utils/preprocessing/compasConfigDefault.yaml b/compas_python_utils/preprocessing/compasConfigDefault.yaml index 82ba3ad85..e46a0e853 100644 --- a/compas_python_utils/preprocessing/compasConfigDefault.yaml +++ b/compas_python_utils/preprocessing/compasConfigDefault.yaml @@ -1,5 +1,5 @@ ##~!!~## COMPAS option values -##~!!~## File Created Sun Aug 10 13:16:13 2025 by COMPAS v03.23.00 +##~!!~## File Created Mon Aug 18 13:37:46 2025 by COMPAS v03.23.02 ##~!!~## ##~!!~## The default COMPAS YAML file (``compasConfigDefault.yaml``), as distributed, has ##~!!~## all COMPAS option entries commented so that the COMPAS default value for the @@ -283,6 +283,8 @@ stringChoices: # --fryer-supernova-engine: 'DELAYED' # Default: 'DELAYED' # Options: ['DELAYED','RAPID'] # --kick-magnitude-distribution: 'MULLERMANDEL' # Default: 'MULLERMANDEL' # Options: ['LOGNORMAL','MULLERMANDEL','MULLER2016MAXWELLIAN','MULLER2016','BRAYELDRIDGE','MAXWELLIAN','FLAT','FIXED','ZERO'] # --kick-direction-distribution: 'ISOTROPIC' # Default: 'ISOTROPIC' # Options: ['POLES','WEDGE','POWERLAW','PERPENDICULAR','INPLANE','ISOTROPIC'] +# --maltsev-fallback: 0.500000 # Default: 0.500000 +# --maltsev-mode: 'BALANCED' # Default: 'BALANCED' # Options: ['PESSIMISTIC','BALANCED','OPTIMISTIC'] # --neutron-star-accretion-in-ce: 'ZERO' # Default: 'ZERO' # Options: ['DISK','SURFACE','ZERO'] # --neutron-star-equation-of-state: 'SSE' # Default: 'SSE' # Options: ['ARP3','SSE'] # --neutrino-mass-loss-BH-formation: 'FIXED_MASS' # Default: 'FIXED_MASS' # Options: ['FIXED_MASS','FIXED_FRACTION'] diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst index 47ca673d4..18b7541b0 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst @@ -790,6 +790,38 @@ Binary Properties * - Header String: - SemiMajorAxis>MT +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR1_LUM** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→luminosity1 + * - Description: + - Luminosity (\ :math:`L_\odot`) of the primary immediately after RLOF. + * - Header String: + - Lum(1)>MT + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR2_LUM** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→luminosity2 + * - Description: + - Luminosity (\ :math:`L_\odot`) of the secondary immediately after RLOF. + * - Header String: + - Lum(2)>MT + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -854,6 +886,38 @@ Binary Properties * - Header String: - Radius(2)>MT +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR1_TEFF** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→temperature1 + * - Description: + - Effective temperature (\ :math:`K`) of the primary immediately after RLOF. + * - Header String: + - Teff(1)>MT + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR2_TEFF** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→temperature2 + * - Description: + - Effective temperature (\ :math:`K`) of the secondary immediately after RLOF. + * - Header String: + - Teff(2)>MT + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -1026,6 +1090,39 @@ but not both. If both are printed then the file will contain two columns with th * - Header String: - SemiMajorAxismass2; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR1_RADIUS: value = RLOFDetails().propsPostMT->radius1; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR2_RADIUS: value = RLOFDetails().propsPostMT->radius2; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR1_TEFF: value = RLOFDetails().propsPostMT->temperature1; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR2_TEFF: value = RLOFDetails().propsPostMT->temperature2; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR1_LUM: value = RLOFDetails().propsPostMT->luminosity1; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR2_LUM: value = RLOFDetails().propsPostMT->luminosity2; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR1_RLOF: value = RLOFDetails().propsPostMT->isRLOF1; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR2_RLOF: value = RLOFDetails().propsPostMT->isRLOF2; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR1_STELLAR_TYPE: value = RLOFDetails().propsPostMT->stellarType1; break; @@ -586,6 +590,10 @@ COMPAS_VARIABLE BaseBinaryStar::BinaryPropertyValue(const T_ANY_PROPERTY p_Prope case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_MASS: value = RLOFDetails().propsPreMT->mass2; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RADIUS: value = RLOFDetails().propsPreMT->radius1; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RADIUS: value = RLOFDetails().propsPreMT->radius2; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_TEFF: value = RLOFDetails().propsPreMT->temperature1; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_TEFF: value = RLOFDetails().propsPreMT->temperature2; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_LUM: value = RLOFDetails().propsPreMT->luminosity1; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_LUM: value = RLOFDetails().propsPreMT->luminosity2; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RLOF: value = RLOFDetails().propsPreMT->isRLOF1; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RLOF: value = RLOFDetails().propsPreMT->isRLOF2; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_STELLAR_TYPE: value = RLOFDetails().propsPreMT->stellarType1; break; @@ -906,6 +914,10 @@ void BaseBinaryStar::StashRLOFProperties(const MT_TIMING p_Which) { rlofPropertiesToReset->massLossRateFromDonor = m_MassLossRateInRLOF; rlofPropertiesToReset->accretionEfficiency = m_FractionAccreted; rlofPropertiesToReset->massTransferTimescale = m_MassTransferTimescale; + rlofPropertiesToReset->temperature1 = m_Star1->Temperature(); + rlofPropertiesToReset->temperature2 = m_Star2->Temperature(); + rlofPropertiesToReset->luminosity1 = m_Star1->Luminosity(); + rlofPropertiesToReset->luminosity2 = m_Star2->Luminosity(); } diff --git a/src/ErrorCatalog.h b/src/ErrorCatalog.h index 9c90dfd2c..fea6e5ce4 100644 --- a/src/ErrorCatalog.h +++ b/src/ErrorCatalog.h @@ -166,6 +166,7 @@ enum class ERROR: int { UNKNOWN_KICK_MAGNITUDE_DISTRIBUTION, // unknown kick magnitude distribution UNKNOWN_LOGFILE, // unknown log file UNKNOWN_LBV_MASS_LOSS_PRESCRIPTION, // unknown LBV mass loss prescription + UNKNOWN_MALTSEV_MODE, // unknown maltsev mode UNKNOWN_MT_ACCRETION_EFFICIENCY_PRESCRIPTION, // unknown mass transfer accretion efficiency prescription UNKNOWN_MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION, // unknown mass transfer angular momentum loss prescription UNKNOWN_MASS_LOSS_PRESCRIPTION, // unknown mass loss prescription @@ -342,6 +343,7 @@ const COMPASUnorderedMap> ERROR_CATA { ERROR::UNKNOWN_KICK_MAGNITUDE_DISTRIBUTION, { ERROR_SCOPE::ALWAYS, "Unknown kick magnitude distribution" }}, { ERROR::UNKNOWN_LBV_MASS_LOSS_PRESCRIPTION, { ERROR_SCOPE::ALWAYS, "Unknown LBV mass loss prescription" }}, { ERROR::UNKNOWN_LOGFILE, { ERROR_SCOPE::ALWAYS, "Unknown log file" }}, + { ERROR::UNKNOWN_MALTSEV_MODE, { ERROR_SCOPE::ALWAYS, "Unknown Maltsev remnant mass mode" }}, { ERROR::UNKNOWN_MT_CASE, { ERROR_SCOPE::ALWAYS, "Unknown mass transfer case" }}, { ERROR::UNKNOWN_MT_ACCRETION_EFFICIENCY_PRESCRIPTION, { ERROR_SCOPE::ALWAYS, "Unknown mass transfer accretion efficiency prescription" }}, { ERROR::UNKNOWN_MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION, { ERROR_SCOPE::ALWAYS, "Unknown mass transfer angular momentum loss prescription" }}, diff --git a/src/GiantBranch.cpp b/src/GiantBranch.cpp index 73dfcb7b8..9d25e1650 100644 --- a/src/GiantBranch.cpp +++ b/src/GiantBranch.cpp @@ -1297,76 +1297,113 @@ double GiantBranch::CalculateRemnantMassBySchneider2020(const double p_COCoreMas */ double GiantBranch::CalculateRemnantMassByMaltsev2024(const double p_COCoreMass, const double p_HeCoreMass) { - ST_VECTOR mtHist = MassTransferDonorHistory(); // mass transfer history vector - MT_CASE massTransferCase = MT_CASE::OTHER; - double log10Z = m_Log10Metallicity - LOG10_ZSOL_ASPLUND; // log_{10} (Z/Zsol), for convenience - double M1, M2, M3; - - if (utils::Compare(p_COCoreMass, MALTSEV2024_MMIN) < 0) // NS formation regardless of metallicity and MT history - return CalculateRemnantNSMassMullerMandel(p_COCoreMass, p_HeCoreMass); - - if (utils::Compare(p_COCoreMass, MALTSEV2024_MMAX) > 0) // BH formation regardless of metallicity and MT history - return p_HeCoreMass; - - // determine MT history - this will tell us which Schneider MT case prescription should be used - if (mtHist.size() == 0) { // no history of MT - effectively single star - massTransferCase = MT_CASE::NONE; + ST_VECTOR mtHist = MassTransferDonorHistory(); // mass transfer history vector + MT_CASE massTransferCase = MT_CASE::OTHER; + double log10Z = m_Log10Metallicity - LOG10_ZSOL_ASPLUND; // log_{10} (Z/Zsol), for convenience + constexpr double log10_1 = 0; // useful for the limits later + constexpr double log10_1_div_10 = -1; // useful for the limits later + constexpr double log10_1_div_50 = -1.69897; // useful for the limits later + double M1, M2, M3; // Z-dependent boundary values for SN outcomes (see Maltsev+ 2025) + double remnantMass; + + if (utils::Compare(p_COCoreMass, MALTSEV2024_MMIN) < 0) { // NS formation regardless of metallicity and MT history + m_SupernovaDetails.fallbackFraction = 0; + remnantMass = NEUTRON_STAR_MASS; } - else { // star was MT donor at least once - // determine MT_CASE of first MT event - STELLAR_TYPE mostRecentDonorType = mtHist[0]; // stellar type at first MT event (as donor) - BaseStar* newStar = stellarUtils::NewStar(mostRecentDonorType); // create new (empty) star of correct stellar type - massTransferCase = newStar->DetermineMassTransferTypeAsDonor(); // get MT type as donor - delete newStar; newStar = nullptr; // return the memory allocated for the new star + else if (utils::Compare(p_COCoreMass, MALTSEV2024_MMAX) > 0) { // BH formation regardless of metallicity and MT history + m_SupernovaDetails.fallbackFraction = 1; + remnantMass = p_HeCoreMass; } + else { // Determine MT history - this will tell us which Schneider MT case prescription should be used + + double log10Z_bounded; // This is really log10(Z/Zsol), so it is 0 for Z=Zsol, -1 for Z=Zsol/10 and log10(1/50) for ... + switch (OPTIONS->MaltsevMode()) { + + case MALTSEV_MODE::OPTIMISTIC: + log10Z_bounded = log10Z; + break; + case MALTSEV_MODE::BALANCED: + log10Z_bounded = std::min(std::max(log10Z, log10_1_div_50), log10_1); + break; + case MALTSEV_MODE::PESSIMISTIC: + log10Z_bounded = std::min(std::max(log10Z, log10_1_div_10), log10_1); + break; + default: + // Unrecognized MALTSEV_MODE. Only possible if a new one is added but an + // extra flag is not created here. + THROW_ERROR(ERROR::UNKNOWN_MALTSEV_MODE); // throw error + } - if (massTransferCase == MT_CASE::NONE && HydrogenAbundanceSurface() == 0.0) massTransferCase = MT_CASE::B; // if a star was stripped by winds, treat it as if it experienced Case B mass transfer + if (mtHist.size() == 0) { // no history of MT - effectively single star + massTransferCase = MT_CASE::NONE; + } + else { // star was MT donor at least once + // determine MT_CASE of first MT event + STELLAR_TYPE stellarTypeAtFirstDonation = mtHist[0]; // stellar type at first MT event (as donor) + BaseStar* newStar = stellarUtils::NewStar(stellarTypeAtFirstDonation); // create new (empty) star of correct stellar type + massTransferCase = newStar->DetermineMassTransferTypeAsDonor(); // get MT type as donor + delete newStar; newStar = nullptr; // return the memory allocated for the new star + } + + // If self-stripped, re-classify as Case B + if (massTransferCase == MT_CASE::NONE && HydrogenAbundanceSurface() == 0.0) massTransferCase = MT_CASE::B; // if a star was stripped by winds, treat it as if it experienced Case B mass transfer + + // apply the appropriate remnant mass prescription for the chosen MT case + switch (massTransferCase) { // which MT_CASE? + + case MT_CASE::NONE: // no history of MT + case MT_CASE::OTHER: // if MT happens from naked He stars, WDs, etc., assume that the core properties are not affected + M1 = MALTSEV2024_M1S + (MALTSEV2024_M1S - MALTSEV2024_M1SZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2S + (MALTSEV2024_M2S - MALTSEV2024_M2SZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3S + (MALTSEV2024_M3S - MALTSEV2024_M3SZ01) * log10Z_bounded; + break; + + case MT_CASE::A: // case A MT + M1 = MALTSEV2024_M1A + (MALTSEV2024_M1A - MALTSEV2024_M1AZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2A + (MALTSEV2024_M2A - MALTSEV2024_M2AZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3A + (MALTSEV2024_M3A - MALTSEV2024_M3AZ01) * log10Z_bounded; + break; + + case MT_CASE::B: // case B MT + M1 = MALTSEV2024_M1B + (MALTSEV2024_M1B - MALTSEV2024_M1BZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2B + (MALTSEV2024_M2B - MALTSEV2024_M2BZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3B + (MALTSEV2024_M3B - MALTSEV2024_M3BZ01) * log10Z_bounded; + break; + + case MT_CASE::C: // case C MT + M1 = MALTSEV2024_M1C + (MALTSEV2024_M1C - MALTSEV2024_M1CZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2C + (MALTSEV2024_M2C - MALTSEV2024_M2CZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3C + (MALTSEV2024_M3C - MALTSEV2024_M3CZ01) * log10Z_bounded; + break; + + default: // unknown MT_CASE + // the only way this can happen is if someone added an MT_CASE + // and it isn't accounted for in this code. We should not default here, with or without a warning. + // We are here because DetermineMassTransferTypeAsDonor() returned an MT_CASE this code doesn't + // account for, and that should be flagged as an error and result in termination of the evolution + // of the star or binary. + // The correct fix for this is to add code for the missing MT_CASE or, if the missing MT_CASE is + // incorrect/superfluous, remove it from the possible MT_CASE values. + + THROW_ERROR(ERROR::UNKNOWN_MT_CASE); // throw error + } - // apply the appropriate remnant mass prescription for the chosen MT case - switch (massTransferCase) { // which MT_CASE? - - case MT_CASE::NONE: // no history of MT - case MT_CASE::OTHER: // if MT happens from naked He stars, WDs, etc., assume that the core properties are not affected - M1 = MALTSEV2024_M1S + (MALTSEV2024_M1S - MALTSEV2024_M1SZ01) * log10Z; - M2 = MALTSEV2024_M2S + (MALTSEV2024_M2S - MALTSEV2024_M2SZ01) * log10Z; - M3 = MALTSEV2024_M3S + (MALTSEV2024_M3S - MALTSEV2024_M3SZ01) * log10Z; - break; - - case MT_CASE::A: // case A MT - M1 = MALTSEV2024_M1A + (MALTSEV2024_M1A - MALTSEV2024_M1AZ01) * log10Z; - M2 = MALTSEV2024_M2A + (MALTSEV2024_M2A - MALTSEV2024_M2AZ01) * log10Z; - M3 = MALTSEV2024_M3A + (MALTSEV2024_M3A - MALTSEV2024_M3AZ01) * log10Z; - break; - - case MT_CASE::B: // case B MT - M1 = MALTSEV2024_M1B + (MALTSEV2024_M1B - MALTSEV2024_M1BZ01) * log10Z; - M2 = MALTSEV2024_M2B + (MALTSEV2024_M2B - MALTSEV2024_M2BZ01) * log10Z; - M3 = MALTSEV2024_M3B + (MALTSEV2024_M3B - MALTSEV2024_M3BZ01) * log10Z; - break; - - case MT_CASE::C: // case C MT - M1 = MALTSEV2024_M1C + (MALTSEV2024_M1C - MALTSEV2024_M1CZ01) * log10Z; - M2 = MALTSEV2024_M2C + (MALTSEV2024_M2C - MALTSEV2024_M2CZ01) * log10Z; - M3 = MALTSEV2024_M3C + (MALTSEV2024_M3C - MALTSEV2024_M3CZ01) * log10Z; - break; - - default: // unknown MT_CASE - // the only way this can happen is if someone added an MT_CASE - // and it isn't accounted for in this code. We should not default here, with or without a warning. - // We are here because DetermineMassTransferTypeAsDonor() returned an MT_CASE this code doesn't - // account for, and that should be flagged as an error and result in termination of the evolution - // of the star or binary. - // The correct fix for this is to add code for the missing MT_CASE or, if the missing MT_CASE is - // incorrect/superfluous, remove it from the possible MT_CASE values. - THROW_ERROR(ERROR::UNKNOWN_MT_CASE); // throw error + if( utils::Compare(p_COCoreMass, M3) >=0 || (utils::Compare(p_COCoreMass, M1) >= 0 && utils::Compare(p_COCoreMass, M2) <= 0) ) { // Complete fallback into BH + m_SupernovaDetails.fallbackFraction = 1; + remnantMass = p_HeCoreMass; + } + else if ( utils::Compare(p_COCoreMass, M2) > 0 && utils::Compare(p_COCoreMass, M3) < 0 && utils::Compare(RAND->Random(0, 1), 0.1) <= 0 ) { // Partial fallback BH formation + // add fallback back on + m_SupernovaDetails.fallbackFraction = OPTIONS->MaltsevFallback(); + remnantMass = (p_HeCoreMass - NEUTRON_STAR_MASS)*m_SupernovaDetails.fallbackFraction + NEUTRON_STAR_MASS; + } + else { + m_SupernovaDetails.fallbackFraction = 0; + remnantMass = NEUTRON_STAR_MASS; + } } - - if( utils::Compare(p_COCoreMass, M3) >=0 || (utils::Compare(p_COCoreMass, M1) >= 0 && utils::Compare(p_COCoreMass, M2) <= 0) ) // Complete fallback into BH - return p_HeCoreMass; - else if ( utils::Compare(p_COCoreMass, M2) > 0 && utils::Compare(p_COCoreMass, M3) < 0 && utils::Compare(RAND->Random(0, 1), 0.1) <= 0 ) // Partial fallback BH formation - return CalculateFallbackBHMassMullerMandel(p_COCoreMass, p_HeCoreMass); - return CalculateRemnantNSMassMullerMandel(p_COCoreMass, p_HeCoreMass); + return remnantMass; } @@ -1916,19 +1953,18 @@ STELLAR_TYPE GiantBranch::ResolveCoreCollapseSN() { case REMNANT_MASS_PRESCRIPTION::SCHNEIDER2020: // Schneider 2020 m_Mass = CalculateRemnantMassBySchneider2020(m_COCoreMass); - m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // Fallback fraction of mass beyond proto-neutron-star for BH formation and kicks + m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // fallback fraction of mass beyond proto-neutron-star for BH formation and kicks break; case REMNANT_MASS_PRESCRIPTION::SCHNEIDER2020ALT: // Schneider 2020, alternative m_Mass = CalculateRemnantMassBySchneider2020Alt(m_COCoreMass); - m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // Fallback fraction of mass beyond proto-neutron-star for BH formation and kicks + m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // fallback fraction of mass beyond proto-neutron-star for BH formation and kicks break; case REMNANT_MASS_PRESCRIPTION::MALTSEV2024: // Maltsev+ 2024 - m_SupernovaDetails.fallbackFraction = 0.0; // no subsequent kick adjustment by fallback fraction needed; MULLERMANDEL kick prescription should be used - m_Mass = CalculateRemnantMassByMaltsev2024(m_COCoreMass, m_HeCoreMass); + m_Mass = CalculateRemnantMassByMaltsev2024(m_COCoreMass, m_HeCoreMass); // fallback fraction determined internally break; diff --git a/src/LogTypedefs.h b/src/LogTypedefs.h index f8f327423..0a088e72b 100644 --- a/src/LogTypedefs.h +++ b/src/LogTypedefs.h @@ -525,6 +525,10 @@ enum class BINARY_PROPERTY: int { RLOF_POST_MT_STAR2_MASS, RLOF_POST_MT_STAR1_RADIUS, RLOF_POST_MT_STAR2_RADIUS, + RLOF_POST_MT_STAR1_TEFF, + RLOF_POST_MT_STAR2_TEFF, + RLOF_POST_MT_STAR1_LUM, + RLOF_POST_MT_STAR2_LUM, RLOF_POST_MT_STAR1_RLOF, RLOF_POST_MT_STAR2_RLOF, RLOF_POST_MT_STAR1_STELLAR_TYPE, @@ -539,6 +543,10 @@ enum class BINARY_PROPERTY: int { RLOF_PRE_MT_STAR2_MASS, RLOF_PRE_MT_STAR1_RADIUS, RLOF_PRE_MT_STAR2_RADIUS, + RLOF_PRE_MT_STAR1_TEFF, + RLOF_PRE_MT_STAR2_TEFF, + RLOF_PRE_MT_STAR1_LUM, + RLOF_PRE_MT_STAR2_LUM, RLOF_PRE_MT_STAR1_RLOF, RLOF_PRE_MT_STAR2_RLOF, RLOF_PRE_MT_STAR1_STELLAR_TYPE, @@ -675,6 +683,10 @@ const COMPASUnorderedMap BINARY_PROPERTY_LABEL = { { BINARY_PROPERTY::RLOF_POST_MT_STAR2_MASS, "RLOF_POST_MT_STAR2_MASS" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RADIUS, "RLOF_POST_MT_STAR1_RADIUS" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RADIUS, "RLOF_POST_MT_STAR2_RADIUS" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_TEFF, "RLOF_POST_MT_STAR1_TEFF" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_TEFF, "RLOF_POST_MT_STAR2_TEFF" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_LUM, "RLOF_POST_MT_STAR1_LUM" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_LUM, "RLOF_POST_MT_STAR2_LUM" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RLOF, "RLOF_POST_MT_STAR1_RLOF" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RLOF, "RLOF_POST_MT_STAR2_RLOF" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_STELLAR_TYPE, "RLOF_POST_MT_STAR1_STELLAR_TYPE" }, @@ -689,6 +701,10 @@ const COMPASUnorderedMap BINARY_PROPERTY_LABEL = { { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_MASS, "RLOF_PRE_MT_STAR2_MASS" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RADIUS, "RLOF_PRE_MT_STAR1_RADIUS" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RADIUS, "RLOF_PRE_MT_STAR2_RADIUS" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_TEFF, "RLOF_PRE_MT_STAR1_TEFF" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_TEFF, "RLOF_PRE_MT_STAR2_TEFF" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_LUM, "RLOF_PRE_MT_STAR1_LUM" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_LUM, "RLOF_PRE_MT_STAR2_LUM" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RLOF, "RLOF_PRE_MT_STAR1_RLOF" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RLOF, "RLOF_PRE_MT_STAR2_RLOF" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_STELLAR_TYPE, "RLOF_PRE_MT_STAR1_STELLAR_TYPE" }, @@ -872,6 +888,10 @@ enum class PROGRAM_OPTION: int { LBV_FACTOR, LBV_MASS_LOSS_PRESCRIPTION, + + MALTSEV_FALLBACK, + MALTSEV_MODE, + MASS_LOSS_PRESCRIPTION, MASS_RATIO, @@ -1099,6 +1119,9 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::LBV_FACTOR, "LBV_FACTOR" }, { PROGRAM_OPTION::LBV_MASS_LOSS_PRESCRIPTION, "LBV_MASS_LOSS_PRESCRIPTION" }, + + { PROGRAM_OPTION::MALTSEV_FALLBACK, "MALTSEV_FALLBACK" }, + { PROGRAM_OPTION::MALTSEV_MODE, "MALTSEV_MODE" }, { PROGRAM_OPTION::MASS_LOSS_PRESCRIPTION, "MASS_LOSS_PRESCRIPTION" }, { PROGRAM_OPTION::MASS_RATIO, "MASS_RATIO" }, @@ -1483,6 +1506,10 @@ const std::map BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::RLOF_POST_MT_STAR2_MASS, { TYPENAME::DOUBLE, "Mass(2)>MT", "Msol", 24, 15}}, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RADIUS, { TYPENAME::DOUBLE, "Radius(1)>MT", "Rsol", 24, 15}}, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RADIUS, { TYPENAME::DOUBLE, "Radius(2)>MT", "Rsol", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_TEFF, { TYPENAME::DOUBLE, "Teff(1)>MT", "K", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_TEFF, { TYPENAME::DOUBLE, "Teff(2)>MT", "K", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_LUM, { TYPENAME::DOUBLE, "Lum(1)>MT", "Lsol", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_LUM, { TYPENAME::DOUBLE, "Lum(2)>MT", "Lsol", 24, 15}}, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RLOF, { TYPENAME::BOOL, "RLOF(1)>MT", "State", 0, 0 }}, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RLOF, { TYPENAME::BOOL, "RLOF(2)>MT", "State", 0, 0 }}, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_STELLAR_TYPE, { TYPENAME::STELLAR_TYPE, "Stellar_Type(1)>MT", "-", 4, 1 }}, @@ -1497,6 +1524,10 @@ const std::map BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_MASS, { TYPENAME::DOUBLE, "Mass(2) PROGRAM_OPTION_DETAIL = { { PROGRAM_OPTION::LBV_FACTOR, { TYPENAME::DOUBLE, "PO_LBV_Factor", "-", 24, 15}}, { PROGRAM_OPTION::LBV_MASS_LOSS_PRESCRIPTION, { TYPENAME::INT, "PO_LBV_Mass_Loss_Prscrptn", "-", 4, 1 }}, + { PROGRAM_OPTION::MALTSEV_FALLBACK, { TYPENAME::DOUBLE, "PO_Maltsev_Fallback", "-", 24, 15}}, + { PROGRAM_OPTION::MALTSEV_MODE, { TYPENAME::INT, "PO_Maltsev_Mode", "-", 4, 1 }}, { PROGRAM_OPTION::MASS_LOSS_PRESCRIPTION, { TYPENAME::INT, "PO_Mass_Loss_Prscrptn", "-", 4, 1 }}, { PROGRAM_OPTION::MASS_RATIO, { TYPENAME::DOUBLE, "PO_Mass_Ratio", "-", 24, 15}}, diff --git a/src/Options.cpp b/src/Options.cpp index da91cff4d..ef774c396 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -359,6 +359,10 @@ void Options::OptionValues::Initialise() { m_BlackHoleKicksMode.type = BLACK_HOLE_KICKS_MODE::FALLBACK; m_BlackHoleKicksMode.typeString = BLACK_HOLE_KICKS_MODE_LABEL.at(m_BlackHoleKicksMode.type); + m_MaltsevFallback = 0.5; + m_MaltsevMode.type = MALTSEV_MODE::BALANCED; + m_MaltsevMode.typeString = MALTSEV_MODE_LABEL.at(m_MaltsevMode.type); + // Rocket kicks m_RocketKickMagnitude1 = 0.0; m_RocketKickMagnitude2 = 0.0; @@ -1449,6 +1453,12 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Multiplicitive constant for LBV mass loss (default = " + std::to_string(p_Options->m_LuminousBlueVariableFactor) + ", use 10 for Mennekens & Vanbeveren 2014)").c_str() ) + ( + "maltsev-fallback", + po::value(&p_Options->m_MaltsevFallback)->default_value(p_Options->m_MaltsevFallback), + ("Fallback fraction for Maltsev black holes (ignored otherwise) (default = " + std::to_string(p_Options->m_MaltsevFallback) + ")").c_str() + ) + ( "mass-change-fraction", po::value(&p_Options->m_MassChangeFraction)->default_value(p_Options->m_MassChangeFraction), @@ -1952,6 +1962,11 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Main Sequence core mass prescription (" + AllowedOptionValuesFormatted("main-sequence-core-mass-prescription") + ", default = '" + p_Options->m_MainSequenceCoreMassPrescription.typeString + "')").c_str() ) + ( + "maltsev-mode", + po::value(&p_Options->m_MaltsevMode.typeString)->default_value(p_Options->m_MaltsevMode.typeString), + ("Maltsev mode (" + AllowedOptionValuesFormatted("maltsev-mode") + ", default = '" + p_Options->m_MaltsevMode.typeString + "')").c_str() + ) ( "mass-loss-prescription", po::value(&p_Options->m_MassLossPrescription.typeString)->default_value(p_Options->m_MassLossPrescription.typeString), @@ -2410,6 +2425,11 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(!found, "Unknown Main Sequence Core Mass Prescription"); } + if (!DEFAULTED("maltsev-mode")) { // mass loss prescription + std::tie(found, m_MaltsevMode.type) = utils::GetMapKey(m_MaltsevMode.typeString, MALTSEV_MODE_LABEL, m_MaltsevMode.type); + COMPLAIN_IF(!found, "Unknown Mass Loss Prescription"); + } + if (!DEFAULTED("mass-loss-prescription")) { // mass loss prescription std::tie(found, m_MassLossPrescription.type) = utils::GetMapKey(m_MassLossPrescription.typeString, MASS_LOSS_PRESCRIPTION_LABEL, m_MassLossPrescription.type); COMPLAIN_IF(!found, "Unknown Mass Loss Prescription"); @@ -2580,9 +2600,11 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(m_LogLevel < 0, "Logging level (--log-level) < 0"); COMPLAIN_IF(m_LuminousBlueVariableFactor < 0.0, "LBV multiplier (--luminous-blue-variable-multiplier) < 0"); + + COMPLAIN_IF(m_MaltsevFallback < 0.0 || m_MaltsevFallback > 1.0, "Maltsev fallback fraction (--maltsev-fallback) must be between 0 and 1, inclusive"); COMPLAIN_IF(m_MassChangeFraction <= 0.0, "Mass change fraction per timestep (--mass-change-fraction) <= 0"); - + COMPLAIN_IF(m_MassRatio <= 0.0 || m_MassRatio > 1.0, "Mass ratio (--mass-ratio) must be greater than 0 and less than or equal to 1"); COMPLAIN_IF(m_MassRatioDistributionMin <= 0.0 || m_MassRatioDistributionMin > 1.0, "Minimum mass ratio (--mass-ratio-min) must be greater than 0 and less than or equal to 1"); @@ -2804,6 +2826,7 @@ STR_VECTOR Options::AllowedOptionValues(const std::string p_OptionString) { case _("logfile-type") : POPULATE_RET(LOGFILETYPELabel); break; case _("LBV-mass-loss-prescription") : POPULATE_RET(LBV_MASS_LOSS_PRESCRIPTION_LABEL); break; case _("main-sequence-core-mass-prescription") : POPULATE_RET(CORE_MASS_PRESCRIPTION_LABEL); break; + case _("maltsev-mode") : POPULATE_RET(MALTSEV_MODE_LABEL); break; case _("mass-loss-prescription") : POPULATE_RET(MASS_LOSS_PRESCRIPTION_LABEL); break; case _("mass-ratio-distribution") : POPULATE_RET(MASS_RATIO_DISTRIBUTION_LABEL); break; case _("mass-transfer-accretion-efficiency-prescription") : POPULATE_RET(MT_ACCRETION_EFFICIENCY_PRESCRIPTION_LABEL); break; @@ -4974,7 +4997,10 @@ COMPAS_VARIABLE Options::OptionValue(const T_ANY_PROPERTY p_Property) const { case PROGRAM_OPTION::LBV_FACTOR : value = LuminousBlueVariableFactor(); break; case PROGRAM_OPTION::LBV_MASS_LOSS_PRESCRIPTION : value = static_cast(LBVMassLossPrescription()); break; - + + case PROGRAM_OPTION::MALTSEV_FALLBACK : value = MaltsevFallback(); break; + case PROGRAM_OPTION::MALTSEV_MODE : value = static_cast(MaltsevMode()); break; + case PROGRAM_OPTION::MASS_LOSS_PRESCRIPTION : value = static_cast(MassLossPrescription()); break; case PROGRAM_OPTION::MASS_RATIO : value = MassRatio(); break; diff --git a/src/Options.h b/src/Options.h index 12dd84f86..ea6c65a33 100755 --- a/src/Options.h +++ b/src/Options.h @@ -514,6 +514,7 @@ class Options { "logfile-rlof-parameters", "logfile-rlof-parameters-record-types", + "maltsev-fallback", "mass-ratio", "q", "mass-ratio-max", "mass-ratio-min", @@ -651,6 +652,7 @@ class Options { "logfile-type", "main-sequence-core-mass-prescription", + "maltsev-mode", "mass-change-fraction", "mass-loss-prescription", "mass-ratio-distribution", @@ -896,6 +898,10 @@ class Options { double m_InitialMassFunctionMax; // Maximum mass to generate in Msol double m_InitialMassFunctionPower; // single IMF power law set manually + // Maltsev remnant mass model + double m_MaltsevFallback; // fallback fraction for Maltsev fallback black holes + ENUM_OPT m_MaltsevMode; // Maltsev remnant mass mode (which variant of the prescription) + // Mass ratio double m_MassRatio; // Mass ratio for BSE ENUM_OPT m_MassRatioDistribution; // Which mass ratio distribution @@ -1580,6 +1586,9 @@ class Options { LBV_MASS_LOSS_PRESCRIPTION LBVMassLossPrescription() const { return OPT_VALUE("LBV-mass-loss-prescription", m_LBVMassLossPrescription.type, true); } CORE_MASS_PRESCRIPTION MainSequenceCoreMassPrescription() const { return OPT_VALUE("main-sequence-core-mass-prescription", m_MainSequenceCoreMassPrescription.type, true); } + + double MaltsevFallback() const { return OPT_VALUE("maltsev-fallback", m_MaltsevFallback, true); } + MALTSEV_MODE MaltsevMode() const { return OPT_VALUE("maltsev-mode", m_MaltsevMode.type, true); } double MassChangeFraction() const { return m_CmdLine.optionValues.m_MassChangeFraction; } diff --git a/src/changelog.h b/src/changelog.h index 3c48f6fb9..cd4eb1329 100644 --- a/src/changelog.h +++ b/src/changelog.h @@ -1650,6 +1650,9 @@ // 03.23.01 IM - August 18, 2025 - Enhancement, defect repair: // - In the MALTSEV SN prescription, treat wind-stripped stars as if they experienced case B mass transfer // - Limit the output of CalculateEtaPTY() [Helium accretion efficiency onto WDs from Piersanti+ 2014, A3] to be in [0,1] +// 03.24.00 RTW - August 18, 2025 - Enhancement: +// - Updated Maltsev SN prescription, to include Maltsev mode (extrapolation variant outside of Z bounds), +// fallback option, fixed remnant mass, and added lum and teff as attributes of RLOFProperties // // // Version string format is MM.mm.rr, where @@ -1661,7 +1664,7 @@ // if MM is incremented, set mm and rr to 00, even if defect repairs and minor enhancements were also made // if mm is incremented, set rr to 00, even if defect repairs were also made -const std::string VERSION_STRING = "03.23.01"; +const std::string VERSION_STRING = "03.24.00"; # endif // __changelog_h__ diff --git a/src/constants.h b/src/constants.h index d1dd53775..a77b18a1d 100755 --- a/src/constants.h +++ b/src/constants.h @@ -411,7 +411,7 @@ constexpr double MALTSEV2024_M2S = 7.2; constexpr double MALTSEV2024_M2C = 7.1; constexpr double MALTSEV2024_M2B = 8.3; constexpr double MALTSEV2024_M2A = 8.4; -constexpr double MALTSEV2024_M3S = 12.9; +constexpr double MALTSEV2024_M3S = 13.0; constexpr double MALTSEV2024_M3C = 13.2; constexpr double MALTSEV2024_M3B = 15.2; constexpr double MALTSEV2024_M3A = 15.4; diff --git a/src/typedefs.h b/src/typedefs.h index 8be1c4c4c..0b6bc1dc3 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -816,6 +816,14 @@ const COMPASUnorderedMap REMNANT_MASS_PR { REMNANT_MASS_PRESCRIPTION::MALTSEV2024, "MALTSEV2024" } }; +// maltsev remnant mass prescription variant +enum class MALTSEV_MODE: int { OPTIMISTIC, BALANCED, PESSIMISTIC }; +const COMPASUnorderedMap MALTSEV_MODE_LABEL = { + { MALTSEV_MODE::OPTIMISTIC, "OPTIMISTIC" }, + { MALTSEV_MODE::BALANCED, "BALANCED" }, + { MALTSEV_MODE::PESSIMISTIC, "PESSIMISTIC"}, +}; + // response of star to spin-up beyond the Keplerian frequency enum class RESPONSE_TO_SPIN_UP: int { TRANSFER_TO_ORBIT, KEPLERIAN_LIMIT, NO_LIMIT }; const COMPASUnorderedMap RESPONSE_TO_SPIN_UP_LABEL = { @@ -1202,6 +1210,11 @@ typedef struct RLOFProperties { double radius1; double radius2; + double temperature1; + double temperature2; + double luminosity1; + double luminosity2; + double starToRocheLobeRadiusRatio1; double starToRocheLobeRadiusRatio2; diff --git a/src/yaml.h b/src/yaml.h index b93d44047..3aa748e5c 100644 --- a/src/yaml.h +++ b/src/yaml.h @@ -349,6 +349,8 @@ namespace yaml { " --fryer-supernova-engine", " --kick-magnitude-distribution", " --kick-direction-distribution", + " --maltsev-fallback", + " --maltsev-mode", " --neutron-star-accretion-in-ce", " --neutron-star-equation-of-state", " --neutrino-mass-loss-BH-formation",