From 006ef724c4c0fa4589497f69d288756d499eb76e Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 1 Nov 2024 15:40:23 +1100 Subject: [PATCH 01/25] Resolve merge conflicts --- src/MainSequence.cpp | 41 ++++++++++++++++++++++++++--------------- src/MainSequence.h | 2 +- src/Options.cpp | 23 +++++++++++++++++++---- src/Options.h | 14 ++++++++++---- src/constants.h | 30 +++++++++++++++++++++++++++++- src/typedefs.h | 8 ++++++++ 6 files changed, 93 insertions(+), 25 deletions(-) diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index daecdaeff..a175520fa 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -847,21 +847,32 @@ STELLAR_TYPE MainSequence::ResolveEnvelopeLoss(bool p_Force) { * */ void MainSequence::UpdateMinimumCoreMass() { - if (OPTIONS->RetainCoreMassDuringCaseAMassTransfer()) { - - // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. - // Since we are on the main sequence here, we can clone this object as an HG object - // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), - // we can query the cloned object for its core mass. - // - // The clone should not evolve, and so should not log anything, but to be sure the - // clone does not participate in logging, we set its persistence to EPHEMERAL. - - HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); - double TAMSCoreMass = clone->CoreMass(); // get core mass from clone - delete clone; clone = nullptr; // return the memory allocated for the clone - - m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + switch (OPTIONS->MainSequenceCoreMassPrescription()) { + case CORE_MASS_PRESCRIPTION::MANDEL: { + + // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. + // Since we are on the main sequence here, we can clone this object as an HG object + // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), + // we can query the cloned object for its core mass. + // + // The clone should not evolve, and so should not log anything, but to be sure the + // clone does not participate in logging, we set its persistence to EPHEMERAL. + + HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); + double TAMSCoreMass = clone->CoreMass(); // get core mass from clone + delete clone; clone = nullptr; // return the memory allocated for the clone + + m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + break; + } + case CORE_MASS_PRESCRIPTION::NONE: { + m_MinimumCoreMass = 0.0; + break; + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + m_MinimumCoreMass = 0.0; + break; + } } } diff --git a/src/MainSequence.h b/src/MainSequence.h index d2309d0e9..412c90cc5 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -44,7 +44,7 @@ class MainSequence: virtual public BaseStar { double CalculateCOCoreMassAtPhaseEnd() const { return CalculateCOCoreMassOnPhase(); } // Same as on phase double CalculateCOCoreMassOnPhase() const { return 0.0; } // McCO(MS) = 0.0 - double CalculateCoreMassAtPhaseEnd() const { return OPTIONS->RetainCoreMassDuringCaseAMassTransfer() ? MinimumCoreMass() : 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer + double CalculateCoreMassAtPhaseEnd() const { return MinimumCoreMass(); } // Accounts for minimal core mass built up prior to mass loss through mass transfer double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass diff --git a/src/Options.cpp b/src/Options.cpp index aed5b45b2..caa37195b 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -408,11 +408,14 @@ void Options::OptionValues::Initialise() { m_WolfRayetFactor = 1.0; m_ScaleTerminalWindVelocityWithMetallicityPower = 0.0; + // Core mass prescription + m_MainSequenceCoreMassPrescription.type = CORE_MASS_PRESCRIPTION::MANDEL; + m_MainSequenceCoreMassPrescription.typeString = CORE_MASS_PRESCRIPTION_LABEL.at(m_MainSequenceCoreMassPrescription.type); // Mass transfer options m_UseMassTransfer = true; m_CirculariseBinaryDuringMassTransfer = true; m_AngularMomentumConservationDuringCircularisation = false; - m_RetainCoreMassDuringCaseAMassTransfer = true; + //m_RetainCoreMassDuringCaseAMassTransfer = true; m_ConvectiveEnvelopeTemperatureThreshold = CONVECTIVE_BOUNDARY_TEMPERATURE_BELCZYNSKI; // Case BB/BC mass transfer stability prescription @@ -876,11 +879,11 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Suppress printing (default = " + std::string(p_Options->m_Quiet ? "TRUE" : "FALSE") + ")").c_str() ) - ( + /*( "retain-core-mass-during-caseA-mass-transfer", po::value(&p_Options->m_RetainCoreMassDuringCaseAMassTransfer)->default_value(p_Options->m_RetainCoreMassDuringCaseAMassTransfer)->implicit_value(true), ("Retain approximate core mass of a case A donor as a minimum core at end of MS or HeMS (default = " + std::string(p_Options->m_RetainCoreMassDuringCaseAMassTransfer ? "TRUE" : "FALSE") + ")").c_str() - ) + )*/ ( "revised-energy-formalism-nandez-ivanova", po::value(&p_Options->m_RevisedEnergyFormalismNandezIvanova)->default_value(p_Options->m_RevisedEnergyFormalismNandezIvanova)->implicit_value(true), @@ -1778,7 +1781,19 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ) ( - "mass-loss-prescription", + "luminous-blue-variable-prescription", // DEPRECATED June 2024 - remove end 2024 + po::value(&p_Options->m_LBVMassLossPrescription.typeString)->default_value(p_Options->m_LBVMassLossPrescription.typeString), + ("LBV Mass loss prescription (" + AllowedOptionValuesFormatted("luminous-blue-variable-prescription") + ", default = '" + p_Options->m_LBVMassLossPrescription.typeString + "')").c_str() + ) + ( + "main-sequence-core-mass-prescription", + po::value(&p_Options->m_MainSequenceCoreMassPrescription.typeString)->default_value(p_Options->m_MainSequenceCoreMassPrescription.typeString), + + ("Main Sequence core mass prescription (" + AllowedOptionValuesFormatted("main-sequence-core-mass-prescription") + ", default = '" + p_Options->m_MainSequenceCoreMassPrescription.typeString + "')").c_str() + ) + + ( + "mass-loss-prescription", po::value(&p_Options->m_MassLossPrescription.typeString)->default_value(p_Options->m_MassLossPrescription.typeString), ("Mass loss prescription (" + AllowedOptionValuesFormatted("mass-loss-prescription") + ", default = '" + p_Options->m_MassLossPrescription.typeString + "')").c_str() ) diff --git a/src/Options.h b/src/Options.h index 65d4eb66f..d422bd928 100755 --- a/src/Options.h +++ b/src/Options.h @@ -615,7 +615,9 @@ class Options { "logfile-system-parameters", "logfile-system-parameters-record-types", "logfile-type", - + "luminous-blue-variable-prescription", // DEPRECATED June 2024 - remove end 2024 + + "main-sequence-core-mass-prescription", "mass-change-fraction", "mass-loss-prescription", "mass-ratio-distribution", @@ -990,7 +992,9 @@ class Options { bool m_ExpelConvectiveEnvelopeAboveLuminosityThreshold; // Whether to expel the convective envelope in a pulsation when log_10(L/M) reaches the threshold defined by m_LuminosityToMassThreshold double m_LuminosityToMassThreshold; // Threshold value of log_10(L/M) above which the convective envelope is expelled in a pulsation - bool m_RetainCoreMassDuringCaseAMassTransfer; // Whether to retain the approximate core mass of a case A donor as a minimum core at end of MS or HeMS (default = false) +/* bool m_RetainCoreMassDuringCaseAMassTransfer; // Whether to retain the approximate core mass of a case A donor as a minimum core at end of MS or HeMS (default = false) + */ + ENUM_OPT m_MainSequenceCoreMassPrescription; ENUM_OPT m_CaseBBStabilityPrescription; // Which prescription for the stability of case BB/BC mass transfer @@ -1500,6 +1504,8 @@ class Options { double LuminousBlueVariableFactor() const { return OPT_VALUE("luminous-blue-variable-multiplier", m_LuminousBlueVariableFactor, true); } 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 MassChangeFraction() const { return m_CmdLine.optionValues.m_MassChangeFraction; } MASS_LOSS_PRESCRIPTION MassLossPrescription() const { return OPT_VALUE("mass-loss-prescription", m_MassLossPrescription.type, true); } @@ -1617,8 +1623,8 @@ class Options { bool RequestedHelp() const { return m_CmdLine.optionValues.m_VM["help"].as(); } bool RequestedVersion() const { return m_CmdLine.optionValues.m_VM["version"].as(); } - bool RetainCoreMassDuringCaseAMassTransfer() const { return m_CmdLine.optionValues.m_RetainCoreMassDuringCaseAMassTransfer; } - + /*bool RetainCoreMassDuringCaseAMassTransfer() const { return m_CmdLine.optionValues.m_RetainCoreMassDuringCaseAMassTransfer; } + */ bool RLOFPrinting() const { return m_CmdLine.optionValues.m_RlofPrinting; } double RocketKickMagnitude1() const { return OPT_VALUE("rocket-kick-magnitude-1", m_RocketKickMagnitude1, true); } diff --git a/src/constants.h b/src/constants.h index 5706bf313..fbfd318c0 100755 --- a/src/constants.h +++ b/src/constants.h @@ -3670,6 +3670,34 @@ const std::vector>> LOVERIDGE_COE } }; - +// Coefficients for determining core mass for stars on the MS +// from Shikauchi et al. (2024), ArXiv link TBD +// from Table 2 +const std::vector SHIKAUCHI_ALPHA_COEFFICIENTS = { + // Solar metallicity Z_Sun + {0.45, -0.05878711, -0.84646162}, + // 1/3*Z_Sun + {0.45, -0.06968022, -0.73688164}, + // 0.1*Z_Sun + {0.45, -0.0557105, -0.86589929} +}; +// from Table 3 +const std::vector SHIKAUCHI_FMIX_COEFFICIENTS = { + // Solar metallicity Z_Sun + {0.86605495, -0.64960375, 35.57019104}, + // 1/3*Z_Sun + {0.86269445, -0.62623353, 35.74630996}, + // 0.1*Z_Sun + {0.86914766, -0.60815098, 37.20654856} +}; +// from Table 4 +const std::vector SHIKAUCHI_L_COEFFICIENTS = { + // Solar metallicity Z_Sun + {3.27883249, 1.79370338, -0.71413866, -0.77019351, -0.3898752, 0.07499563, 0.5920458, 0.33846556, -0.49649838, 1.71263853}, + // 1/3*Z_Sun + {3.35622529, 1.96904931, -0.88894808, -0.81112488, -0.47925922, 0.09056925, 0.53094768, 0.33971972, -0.35581284, 1.65390003}, + // 0.1*Z_Sun + {3.2555795, 1.84666823, -0.79986388, -0.75728099, -0.38831172, 0.08223542, 0.49543834, 0.31314176, -0.36705796, 1.72200581} +}; #endif // __constants_h__ diff --git a/src/typedefs.h b/src/typedefs.h index cc58128a1..175d22556 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -579,6 +579,14 @@ enum class MASS_CUTOFF: int { COUNT // Sentinel for entry count }; +// main sequence core mass prescription +enum class CORE_MASS_PRESCRIPTION: int { NONE, MANDEL, SHIKAUCHI }; +const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { + { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, // DEPRECATED June 2024 - remove end 2024 + { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, + { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } +}; + // mass loss prescriptions enum class MASS_LOSS_PRESCRIPTION: int { NONE, ZERO, HURLEY, BELCZYNSKI2010, MERRITT2024 }; const COMPASUnorderedMap MASS_LOSS_PRESCRIPTION_LABEL = { From 76ed1abae5f9d231a1185a37fc9859671f39a34a Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 1 Nov 2024 15:42:08 +1100 Subject: [PATCH 02/25] Resolve merge conflicts --- src/MainSequence.cpp | 8 +++----- src/Options.cpp | 12 ++++++++++++ src/Options.h | 2 ++ src/typedefs.h | 16 ++++++++-------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index a175520fa..46aad3f81 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -861,18 +861,16 @@ void MainSequence::UpdateMinimumCoreMass() { HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); double TAMSCoreMass = clone->CoreMass(); // get core mass from clone delete clone; clone = nullptr; // return the memory allocated for the clone - m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass break; } - case CORE_MASS_PRESCRIPTION::NONE: { + case CORE_MASS_PRESCRIPTION::NONE: m_MinimumCoreMass = 0.0; break; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: m_MinimumCoreMass = 0.0; break; - } } } diff --git a/src/Options.cpp b/src/Options.cpp index caa37195b..9f3939c0c 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -2230,6 +2230,16 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(!found, "Unknown LBV Mass Loss Prescription"); } + if (!DEFAULTED("luminous-blue-variable-prescription")) { // DEPRECATED June 2024 - remove end 2024 // LBV mass loss prescription + std::tie(found, m_LBVMassLossPrescription.type) = utils::GetMapKey(m_LBVMassLossPrescription.typeString, LBV_MASS_LOSS_PRESCRIPTION_LABEL, m_LBVMassLossPrescription.type); + COMPLAIN_IF(!found, "Unknown LBV Mass Loss Prescription"); + } + + if (!DEFAULTED("main-sequence-core-mass-prescription")) { + std::tie(found, m_MainSequenceCoreMassPrescription.type) = utils::GetMapKey(m_MainSequenceCoreMassPrescription.typeString, CORE_MASS_PRESCRIPTION_LABEL, m_MainSequenceCoreMassPrescription.type); + COMPLAIN_IF(!found, "Unknown Main Sequence Core Mass 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"); @@ -2585,6 +2595,8 @@ std::vector Options::AllowedOptionValues(const std::string p_Option case _("kick-magnitude-distribution") : POPULATE_RET(KICK_MAGNITUDE_DISTRIBUTION_LABEL); break; case _("logfile-type") : POPULATE_RET(LOGFILETYPELabel); break; case _("LBV-mass-loss-prescription") : POPULATE_RET(LBV_MASS_LOSS_PRESCRIPTION_LABEL); break; + case _("luminous-blue-variable-prescription") : POPULATE_RET(LBV_MASS_LOSS_PRESCRIPTION_LABEL); break; // DEPRECATED June 2024 - remove end 2024 + case _("main-sequence-core-mass-prescription") : POPULATE_RET(CORE_MASS_PRESCRIPTION_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; diff --git a/src/Options.h b/src/Options.h index d422bd928..2d9c64079 100755 --- a/src/Options.h +++ b/src/Options.h @@ -483,6 +483,8 @@ class Options { "logfile-rlof-parameters", "logfile-rlof-parameters-record-types", + "main-sequence-core-mass-prescription", + "mass-ratio", "q", "mass-ratio-max", "mass-ratio-min", diff --git a/src/typedefs.h b/src/typedefs.h index 175d22556..5fcf969b5 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -377,6 +377,14 @@ const COMPASUnorderedMap CHE_MODE_LABEL = { { CHE_MODE::PESSIMISTIC, "PESSIMISTIC" } }; +// main sequence core mass prescription +enum class CORE_MASS_PRESCRIPTION: int { NONE, MANDEL, SHIKAUCHI }; +const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { + { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, + { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, + { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } +}; + // logfile delimiters enum class DELIMITER: int { TAB, SPACE, COMMA }; const COMPASUnorderedMap DELIMITERLabel = { // labels @@ -579,14 +587,6 @@ enum class MASS_CUTOFF: int { COUNT // Sentinel for entry count }; -// main sequence core mass prescription -enum class CORE_MASS_PRESCRIPTION: int { NONE, MANDEL, SHIKAUCHI }; -const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { - { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, // DEPRECATED June 2024 - remove end 2024 - { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, - { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } -}; - // mass loss prescriptions enum class MASS_LOSS_PRESCRIPTION: int { NONE, ZERO, HURLEY, BELCZYNSKI2010, MERRITT2024 }; const COMPASUnorderedMap MASS_LOSS_PRESCRIPTION_LABEL = { From c1c2899ceafd2f7c9267a3851ee1d91057931356 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 1 Nov 2024 15:43:28 +1100 Subject: [PATCH 03/25] Resolve merge conflict --- src/BaseStar.cpp | 26 +++++++++ src/BaseStar.h | 7 +++ src/MainSequence.cpp | 127 ++++++++++++++++++++++++++++++++++++------- src/MainSequence.h | 14 ++++- src/Star.h | 2 + src/constants.h | 1 + 6 files changed, 154 insertions(+), 23 deletions(-) diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 9b4586a7c..0627b4882 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -138,6 +138,8 @@ BaseStar::BaseStar(const unsigned long int p_RandomSeed, m_Mass = m_MZAMS; m_Mass0 = m_MZAMS; m_MinimumCoreMass = 0.0; + m_MixingCoreMass = CalculateMixingCoreMassAtZAMS(m_MZAMS); + m_CentralHeliumFraction = utils::MESAZAMSHeliumFractionByMetallicity(m_Metallicity); m_Luminosity = m_LZAMS; m_Radius = m_RZAMS; m_Temperature = m_TZAMS; @@ -2992,6 +2994,12 @@ void BaseStar::ResolveMassLoss(const bool p_UpdateMDt) { } +double BaseStar::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { + double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); + return fmix * p_MZAMS; +} + + /* * Calculate core mass for a given luminosity using the Mc - L relation * @@ -4721,6 +4729,16 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { m_HeCoreMass = CalculateHeCoreMassOnPhase(); m_Luminosity = CalculateLuminosityOnPhase(); + if (m_StellarType == STELLAR_TYPE::MS_GT_07 & m_DtPrev != 0.0 & m_MassPrev - m_Mass != 0.0) { + m_MixingCoreMass = CalculateMainSequenceCoreMass((m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0)); + //std::cout << m_Time << ":" << m_CentralHeliumFraction << ":" << m_MixingCoreMass << " "; + //std::cout << m_Mass << ":" << m_CentralHeliumFraction << " "; + //UpdateMinimumCoreMass(m_Mdot); + //std::cout << m_MixingCoreMass << " "; + //m_CoreMass = CalculateMainSequenceCoreMass(m_Mdot); + //_HeCoreMass = m_CoreMass; + //std::cout << "MixCore:" << m_MixingCoreMass << " Mass:" << m_Mass << " Mdot:" <<(m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0) << ":timestep" << m_DtPrev << " Yc:" << m_CentralHeliumFraction ; + } // Calculate abundances m_HeliumAbundanceCore = CalculateHeliumAbundanceCoreOnPhase(); @@ -4776,7 +4794,15 @@ STELLAR_TYPE BaseStar::ResolveEndOfPhase(const bool p_ResolveEnvelopeLoss) { m_COCoreMass = CalculateCOCoreMassAtPhaseEnd(); m_CoreMass = CalculateCoreMassAtPhaseEnd(); m_HeCoreMass = CalculateHeCoreMassAtPhaseEnd(); + if (m_StellarType == STELLAR_TYPE::MS_GT_07) { + //m_MixingCoreMass = CalculateMainSequenceCoreMass(m_Mdot); + //m_CoreMass = m_MixingCoreMass; + //m_HeCoreMass = m_MixingCoreMass; + //std::cout << m_MixingCoreMass << " "; + std::cout << "MixCore:" << m_MixingCoreMass << " Mass:" << m_Mass << " Mdot:" <<(m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0) << ":timestep" << m_DtPrev << " Yc:" << m_CentralHeliumFraction << " TimePassed:" << m_Time; + } + m_Luminosity = CalculateLuminosityAtPhaseEnd(); m_Radius = CalculateRadiusAtPhaseEnd(); diff --git a/src/BaseStar.h b/src/BaseStar.h index 94bd17095..6c47410a5 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -144,6 +144,7 @@ class BaseStar { double Mdot() const { return m_Mdot; } double Metallicity() const { return m_Metallicity; } double MinimumCoreMass() const { return m_MinimumCoreMass; } + double MixingCoreMass() const { return m_MixingCoreMass; } double MZAMS() const { return m_MZAMS; } double Omega() const { return m_Omega; } double OmegaCHE() const { return m_OmegaCHE; } @@ -357,6 +358,7 @@ class BaseStar { const double p_Epsilon) { } // Default is NO-OP virtual void UpdateMinimumCoreMass() { } // Only set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass; default is NO-OP + virtual void UpdateMinimumCoreMass(double p_Mdot) { } // printing functions @@ -420,6 +422,8 @@ class BaseStar { double m_CoreMass; // Current core mass (Msol) double m_Dt; // Size of current timestep (Myr) bool m_EnvelopeJustExpelledByPulsations; // Flag to know if the convective envelope has just been expelled by pulsations + double m_MixingCoreMass; + double m_CentralHeliumFraction; double m_HeCoreMass; // Current He core mass (Msol) double m_HeliumAbundanceCore; // Helium abundance in the core double m_HeliumAbundanceSurface; // Helium abundance at the surface @@ -524,6 +528,7 @@ class BaseStar { virtual double CalculateCOCoreMassOnPhase() const { return m_COCoreMass; } // Default is NO-OP virtual double CalculateCoreMassAtPhaseEnd() const { return m_CoreMass; } // Default is NO-OP + virtual double CalculateMainSequenceCoreMass(double p_Mdot) { return m_MixingCoreMass; } static double CalculateCoreMassGivenLuminosity_Static(const double p_Luminosity, const DBL_VECTOR &p_GBParams); virtual double CalculateCoreMassOnPhase() const { return m_CoreMass; } // Default is NO-OP @@ -618,6 +623,8 @@ class BaseStar { virtual double CalculateMassTransferRejuvenationFactor() { return 1.0; } double CalculateMaximumCoreMass(double p_Mass) const; + + double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); double CalculateOmegaBreak() const; diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 46aad3f81..807f65468 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -749,6 +749,22 @@ void MainSequence::UpdateAgeAfterMassLoss() { m_Age *= tMSprime / tMS; } + +bool MainSequence::ShouldEvolveOnPhase() const { + switch (OPTIONS->MainSequenceCoreMassPrescription()) { + case CORE_MASS_PRESCRIPTION::MANDEL: { + return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); + } + case CORE_MASS_PRESCRIPTION::NONE: { + return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + return (m_CentralHeliumFraction <= 1.0 - m_Metallicity & m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); + } + } +} + + /////////////////////////////////////////////////////////////////////////////////////// // // // MISCELLANEOUS FUNCTIONS / CONTROL FUNCTIONS // @@ -835,10 +851,95 @@ STELLAR_TYPE MainSequence::ResolveEnvelopeLoss(bool p_Force) { } +double MainSequence::CalculateMainSequenceCoreMassMandel() { + // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. + // Since we are on the main sequence here, we can clone this object as an HG object + // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), + // we can query the cloned object for its core mass. + // + // The clone should not evolve, and so should not log anything, but to be sure the + // clone does not participate in logging, we set its persistence to EPHEMERAL. + + HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); + double TAMSCoreMass = clone->CoreMass(); // get core mass from clone + delete clone; clone = nullptr; // return the memory allocated for the clone + m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + //double coreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); + return m_MinimumCoreMass; +} + + +double MainSequence::CalculateMainSequenceCoreMassShikauchi(double p_Mdot) { + double totalMass = m_MassPrev; + double centralHeliumFraction = m_CentralHeliumFraction; + double lnMixingCoreMass = std::log(m_MixingCoreMass); + + controlled_stepper_type controlled_stepper; + state_type x(3); + x[0] = totalMass; + x[1] = centralHeliumFraction; + x[2] = lnMixingCoreMass; + double mixingCoreMass = std::exp(lnMixingCoreMass); + double logMixingCoreMass = std::log10(mixingCoreMass); + + if (m_LastSolvedTime == m_Time) { + return mixingCoreMass; + } + + double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(- m_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); + + double beta = 1.0 - SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * totalMass / (SHIKAUCHI_FMIX_COEFFICIENTS[0][2] * fmix) * std::exp(- totalMass / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); + + double logL = SHIKAUCHI_L_COEFFICIENTS[0][0] * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][2] * logMixingCoreMass * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][3] * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][4] * x[1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][6] * x[1] * x[1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][7] * logMixingCoreMass * logMixingCoreMass * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][8] * logMixingCoreMass * x[1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][9]; + + double alpha = PPOW(10.0, std::max(-2.0, SHIKAUCHI_ALPHA_COEFFICIENTS[0][1] * mixingCoreMass + SHIKAUCHI_ALPHA_COEFFICIENTS[0][2])) + SHIKAUCHI_ALPHA_COEFFICIENTS[0][0]; + + double g = -0.0044 * m_MZAMS + 0.27; + + double YZAMS = 0.24 + 2.0 * m_Metallicity; + + double Yhat = (x[1] - YZAMS) / (1.0 - YZAMS - m_Metallicity); + + double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); + + double previousTimestepInYrs = m_DtPrev * 1000000.0; + + auto ode = [&](const state_type &x, state_type &dxdt, double previousTimestepInYrs) { + dxdt[0] = - p_Mdot; + dxdt[1] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); + dxdt[2] = -alpha/(1.0 - alpha * x[1]) * dxdt[1] + beta * delta * dxdt[0] / x[0]; + }; + + integrate_adaptive(controlled_stepper, ode, x, 0.0, previousTimestepInYrs, previousTimestepInYrs/1000.0); + mixingCoreMass = std::exp(x[2]); + m_CentralHeliumFraction = x[1]; + m_LastSolvedTime = m_Time; + return mixingCoreMass; +} + + +double MainSequence::CalculateMainSequenceCoreMass(double p_Mdot) { + switch (OPTIONS->MainSequenceCoreMassPrescription()) { + case CORE_MASS_PRESCRIPTION::MANDEL: { + double coreMass = CalculateMainSequenceCoreMassMandel(); + return coreMass; + } + case CORE_MASS_PRESCRIPTION::NONE: { + double coreMass = 0.0; + return coreMass; + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + double coreMass = CalculateMainSequenceCoreMassShikauchi(p_Mdot); + return coreMass; + } + } +} + + /* * Update the minimum core mass of a main sequence star that loses mass through Case A mass transfer by * setting it equal to the core mass of a TAMS star, scaled by the fractional age. - * + * * The minimum core mass of the star is updated only if the retain-core-mass-during-caseA-mass-transfer * option is specified, otherwise it is left unchanged. * @@ -846,30 +947,16 @@ STELLAR_TYPE MainSequence::ResolveEnvelopeLoss(bool p_Force) { * void UpdateMinimumCoreMass() * */ -void MainSequence::UpdateMinimumCoreMass() { +void MainSequence::UpdateMinimumCoreMass(double p_Mdot) { switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - - // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. - // Since we are on the main sequence here, we can clone this object as an HG object - // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), - // we can query the cloned object for its core mass. - // - // The clone should not evolve, and so should not log anything, but to be sure the - // clone does not participate in logging, we set its persistence to EPHEMERAL. - - HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); - double TAMSCoreMass = clone->CoreMass(); // get core mass from clone - delete clone; clone = nullptr; // return the memory allocated for the clone - m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + case CORE_MASS_PRESCRIPTION::MANDEL: + m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); break; - } case CORE_MASS_PRESCRIPTION::NONE: - m_MinimumCoreMass = 0.0; + m_MinimumCoreMass = 0.0; break; - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: - m_MinimumCoreMass = 0.0; + m_MixingCoreMass = CalculateMainSequenceCoreMassShikauchi(p_Mdot); break; } } diff --git a/src/MainSequence.h b/src/MainSequence.h index 412c90cc5..7b7e7c4fd 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -20,6 +20,11 @@ class MainSequence: virtual public BaseStar { MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::A; } // Always case A + double MixingCoreMass() const { return m_MixingCoreMass; } + + double CentralHeliumFraction() const { return m_CentralHeliumFraction; } + + double m_LastSolvedTime = 0.0; protected: @@ -70,7 +75,9 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityAtPhaseEnd() const { return CalculateLuminosityAtPhaseEnd(m_Mass0); } // Use class member variables double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables - + double CalculateMainSequenceCoreMass(double p_Mdot); + double CalculateMainSequenceCoreMassMandel(); + double CalculateMainSequenceCoreMassShikauchi(double p_Mdot); double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 double CalculatePerturbationMu() const { return 5.0; } // mu(MS) = 5.0 (Hurley et al. 2000, eqs 97 & 98) @@ -103,7 +110,7 @@ class MainSequence: virtual public BaseStar { STELLAR_TYPE ResolveEnvelopeLoss(bool p_Force = false); - bool ShouldEvolveOnPhase() const { return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); } // Evolve on MS phase if age in MS timescale + bool ShouldEvolveOnPhase() const; // Evolve on MS phase if age in MS timescale void UpdateInitialMass() { m_Mass0 = m_Mass; } // Per Hurley et al. 2000, section 7.1 @@ -111,7 +118,8 @@ class MainSequence: virtual public BaseStar { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateMinimumCoreMass(); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + void UpdateMinimumCoreMass() { }; + void UpdateMinimumCoreMass(double p_Mdot); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass }; diff --git a/src/Star.h b/src/Star.h index 3c3932c50..73a039349 100755 --- a/src/Star.h +++ b/src/Star.h @@ -290,6 +290,8 @@ class Star { void UpdateMinimumCoreMass() { m_Star->UpdateMinimumCoreMass(); } + void UpdateMinimumCoreMass(double p_Mdot) { m_Star->UpdateMinimumCoreMass(p_Mdot); } + void UpdatePreviousTimestepDuration() { m_Star->UpdatePreviousTimestepDuration(); } ACCRETION_REGIME WhiteDwarfAccretionRegime() const { return m_Star->WhiteDwarfAccretionRegime(); } diff --git a/src/constants.h b/src/constants.h index fbfd318c0..7b8558504 100755 --- a/src/constants.h +++ b/src/constants.h @@ -288,6 +288,7 @@ constexpr double FARMER_PPISN_UPP_LIM_QUAD_REGIME = 60.0; constexpr double FARMER_PPISN_UPP_LIM_INSTABILLITY = 140.0; // Maximum CO core mass to result in PI (upper edge of PISN gap) from FARMER PPISN prescription constexpr double STARTRACK_PPISN_HE_CORE_MASS = 45.0; // Helium core mass remaining following PPISN as assumed in StarTrack (Belczynski et al. 2017 https://arxiv.org/abs/1607.03116) +constexpr double Q_CNO = 9.9073E10; // Energy released per unit mass by hydrogen fusion via the CNO cycle in Lsol Msol-1 // logging constants From 697a9a419e87c7e547d9e3224c6a65bc72599276 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 1 Nov 2024 15:44:18 +1100 Subject: [PATCH 04/25] Resolve merge conflict --- src/BaseBinaryStar.cpp | 3 ++ src/BaseStar.cpp | 54 ++++++++++++++++++---------------- src/BaseStar.h | 12 ++++---- src/MainSequence.cpp | 67 +++++++++++++++--------------------------- src/MainSequence.h | 12 ++------ src/Star.h | 7 +++-- 6 files changed, 67 insertions(+), 88 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index de504ab7c..1e4b0eb5f 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -354,6 +354,7 @@ void BaseBinaryStar::SetRemainingValues() { m_MassTransferTrackerHistory = MT_TRACKING::NO_MASS_TRANSFER; m_MassTransfer = false; + m_MassLossRateInRLOF = DEFAULT_INITIAL_DOUBLE_VALUE; m_JLoss = OPTIONS->MassTransferJloss(); @@ -2064,6 +2065,8 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { if (utils::Compare(m_MassLossRateInRLOF,donorMassLossRateNuclear) == 0) // if transferring mass on nuclear timescale, limit mass loss amount to rate * timestep (thermal timescale MT always happens in one timestep) massDiffDonor = std::min(massDiffDonor, m_MassLossRateInRLOF * m_Dt); massDiffDonor = -massDiffDonor; // set mass difference + + m_Donor->UpdateTotalMassLossRate(-massDiffDonor / (p_Dt * 1000000.0)); m_Donor->UpdateMinimumCoreMass(); // reset the minimum core mass following case A } } diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 0627b4882..8f7cdfd38 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -137,12 +137,12 @@ BaseStar::BaseStar(const unsigned long int p_RandomSeed, m_Age = 0.0; // ensure age = 0.0 at construction (rather than default initial value) m_Mass = m_MZAMS; m_Mass0 = m_MZAMS; - m_MinimumCoreMass = 0.0; - m_MixingCoreMass = CalculateMixingCoreMassAtZAMS(m_MZAMS); + m_MinimumCoreMass = CalculateMixingCoreMassAtZAMS(m_MZAMS); m_CentralHeliumFraction = utils::MESAZAMSHeliumFractionByMetallicity(m_Metallicity); m_Luminosity = m_LZAMS; m_Radius = m_RZAMS; m_Temperature = m_TZAMS; + m_TotalMassLossRate = DEFAULT_INITIAL_DOUBLE_VALUE; m_ComponentVelocity = Vector3d(); m_CoreMass = DEFAULT_INITIAL_DOUBLE_VALUE; @@ -2858,7 +2858,7 @@ double BaseStar::CalculateMassLossRate() { mDot = mDot * OPTIONS->OverallWindMassLossMultiplier(); // apply overall wind mass loss multiplier } - + UpdateTotalMassLossRate(mDot); return mDot; } @@ -2995,8 +2995,18 @@ void BaseStar::ResolveMassLoss(const bool p_UpdateMDt) { double BaseStar::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { - double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); - return fmix * p_MZAMS; + switch (OPTIONS->MainSequenceCoreMassPrescription()) { + case CORE_MASS_PRESCRIPTION::MANDEL: { + return 0.0; + } + case CORE_MASS_PRESCRIPTION::NONE: { + return 0.0; + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); + return fmix * p_MZAMS; + } + } } @@ -3233,6 +3243,12 @@ void BaseStar::UpdateMassTransferDonorHistory() { } +void BaseStar::UpdateTotalMassLossRate(const double p_MassLossRate) { + //m_TotalMassLossRatePrev = m_TotalMassLossRate; + m_TotalMassLossRate = p_MassLossRate; +} + + /////////////////////////////////////////////////////////////////////////////////////// // // // TEMPERATURE CALCULATIONS // @@ -4724,21 +4740,15 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { if (ShouldEvolveOnPhase()) { // evolve timestep on phase m_Tau = CalculateTauOnPhase(); - m_COCoreMass = CalculateCOCoreMassOnPhase(); - m_CoreMass = CalculateCoreMassOnPhase(); - m_HeCoreMass = CalculateHeCoreMassOnPhase(); + m_COCoreMass = CalculateCOCoreMassOnPhase(); + m_CoreMass = CalculateCoreMassOnPhase(); + m_HeCoreMass = CalculateHeCoreMassOnPhase(); + + if (m_StellarType == STELLAR_TYPE::MS_GT_07 & p_DeltaTime > 0.0 & m_Mdot > 0.0) { + UpdateMinimumCoreMass(); + } m_Luminosity = CalculateLuminosityOnPhase(); - if (m_StellarType == STELLAR_TYPE::MS_GT_07 & m_DtPrev != 0.0 & m_MassPrev - m_Mass != 0.0) { - m_MixingCoreMass = CalculateMainSequenceCoreMass((m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0)); - //std::cout << m_Time << ":" << m_CentralHeliumFraction << ":" << m_MixingCoreMass << " "; - //std::cout << m_Mass << ":" << m_CentralHeliumFraction << " "; - //UpdateMinimumCoreMass(m_Mdot); - //std::cout << m_MixingCoreMass << " "; - //m_CoreMass = CalculateMainSequenceCoreMass(m_Mdot); - //_HeCoreMass = m_CoreMass; - //std::cout << "MixCore:" << m_MixingCoreMass << " Mass:" << m_Mass << " Mdot:" <<(m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0) << ":timestep" << m_DtPrev << " Yc:" << m_CentralHeliumFraction ; - } // Calculate abundances m_HeliumAbundanceCore = CalculateHeliumAbundanceCoreOnPhase(); @@ -4794,15 +4804,7 @@ STELLAR_TYPE BaseStar::ResolveEndOfPhase(const bool p_ResolveEnvelopeLoss) { m_COCoreMass = CalculateCOCoreMassAtPhaseEnd(); m_CoreMass = CalculateCoreMassAtPhaseEnd(); m_HeCoreMass = CalculateHeCoreMassAtPhaseEnd(); - if (m_StellarType == STELLAR_TYPE::MS_GT_07) { - //m_MixingCoreMass = CalculateMainSequenceCoreMass(m_Mdot); - //m_CoreMass = m_MixingCoreMass; - //m_HeCoreMass = m_MixingCoreMass; - //std::cout << m_MixingCoreMass << " "; - std::cout << "MixCore:" << m_MixingCoreMass << " Mass:" << m_Mass << " Mdot:" <<(m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0) << ":timestep" << m_DtPrev << " Yc:" << m_CentralHeliumFraction << " TimePassed:" << m_Time; - } - m_Luminosity = CalculateLuminosityAtPhaseEnd(); m_Radius = CalculateRadiusAtPhaseEnd(); diff --git a/src/BaseStar.h b/src/BaseStar.h index 6c47410a5..fdfb265d9 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -144,7 +144,6 @@ class BaseStar { double Mdot() const { return m_Mdot; } double Metallicity() const { return m_Metallicity; } double MinimumCoreMass() const { return m_MinimumCoreMass; } - double MixingCoreMass() const { return m_MixingCoreMass; } double MZAMS() const { return m_MZAMS; } double Omega() const { return m_Omega; } double OmegaCHE() const { return m_OmegaCHE; } @@ -187,6 +186,7 @@ class BaseStar { double Temperature() const { return m_Temperature; } double Time() const { return m_Time; } double Timescale(TIMESCALE p_Timescale) const { return m_Timescales[static_cast(p_Timescale)]; } + double TotalMassLossRate() const { return m_TotalMassLossRate; } double TZAMS() const { return m_TZAMS; } virtual ACCRETION_REGIME WhiteDwarfAccretionRegime() const { return ACCRETION_REGIME::ZERO; } double XExponent() const { return m_XExponent; } @@ -357,9 +357,9 @@ class BaseStar { const double p_MassGainPerTimeStep, const double p_Epsilon) { } // Default is NO-OP - virtual void UpdateMinimumCoreMass() { } // Only set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass; default is NO-OP - virtual void UpdateMinimumCoreMass(double p_Mdot) { } - + virtual void UpdateMinimumCoreMass() { }; // Only set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass; default is NO-OP + + virtual void UpdateTotalMassLossRate(const double p_MassLossRate); // printing functions bool PrintDetailedOutput(const int p_Id, const SSE_DETAILED_RECORD_TYPE p_RecordType) const { @@ -422,7 +422,6 @@ class BaseStar { double m_CoreMass; // Current core mass (Msol) double m_Dt; // Size of current timestep (Myr) bool m_EnvelopeJustExpelledByPulsations; // Flag to know if the convective envelope has just been expelled by pulsations - double m_MixingCoreMass; double m_CentralHeliumFraction; double m_HeCoreMass; // Current He core mass (Msol) double m_HeliumAbundanceCore; // Helium abundance in the core @@ -444,6 +443,8 @@ class BaseStar { double m_Tau; // Relative time double m_Temperature; // Current temperature (Tsol) double m_Time; // Current physical time the star has been evolved (Myr) + double m_TotalMassLossRate; + double m_TotalMassLossRatePrev; // Previous timestep variables double m_DtPrev; // Previous timestep @@ -528,7 +529,6 @@ class BaseStar { virtual double CalculateCOCoreMassOnPhase() const { return m_COCoreMass; } // Default is NO-OP virtual double CalculateCoreMassAtPhaseEnd() const { return m_CoreMass; } // Default is NO-OP - virtual double CalculateMainSequenceCoreMass(double p_Mdot) { return m_MixingCoreMass; } static double CalculateCoreMassGivenLuminosity_Static(const double p_Luminosity, const DBL_VECTOR &p_GBParams); virtual double CalculateCoreMassOnPhase() const { return m_CoreMass; } // Default is NO-OP diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 807f65468..159e49543 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -863,34 +863,24 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); double TAMSCoreMass = clone->CoreMass(); // get core mass from clone delete clone; clone = nullptr; // return the memory allocated for the clone - m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + double minimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass //double coreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); - return m_MinimumCoreMass; + return minimumCoreMass; } -double MainSequence::CalculateMainSequenceCoreMassShikauchi(double p_Mdot) { - double totalMass = m_MassPrev; +double MainSequence::CalculateMainSequenceCoreMassShikauchi() { + double totalMass = m_Mass; double centralHeliumFraction = m_CentralHeliumFraction; - double lnMixingCoreMass = std::log(m_MixingCoreMass); - - controlled_stepper_type controlled_stepper; - state_type x(3); - x[0] = totalMass; - x[1] = centralHeliumFraction; - x[2] = lnMixingCoreMass; - double mixingCoreMass = std::exp(lnMixingCoreMass); + double mixingCoreMass = m_MinimumCoreMass; + double lnMixingCoreMass = std::log(mixingCoreMass); double logMixingCoreMass = std::log10(mixingCoreMass); - if (m_LastSolvedTime == m_Time) { - return mixingCoreMass; - } - double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(- m_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); double beta = 1.0 - SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * totalMass / (SHIKAUCHI_FMIX_COEFFICIENTS[0][2] * fmix) * std::exp(- totalMass / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); - double logL = SHIKAUCHI_L_COEFFICIENTS[0][0] * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][2] * logMixingCoreMass * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][3] * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][4] * x[1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][6] * x[1] * x[1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][7] * logMixingCoreMass * logMixingCoreMass * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][8] * logMixingCoreMass * x[1] * x[1] + SHIKAUCHI_L_COEFFICIENTS[0][9]; + double logL = SHIKAUCHI_L_COEFFICIENTS[0][0] * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][1] * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][2] * logMixingCoreMass * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][3] * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][4] * centralHeliumFraction * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][9]; double alpha = PPOW(10.0, std::max(-2.0, SHIKAUCHI_ALPHA_COEFFICIENTS[0][1] * mixingCoreMass + SHIKAUCHI_ALPHA_COEFFICIENTS[0][2])) + SHIKAUCHI_ALPHA_COEFFICIENTS[0][0]; @@ -898,44 +888,35 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi(double p_Mdot) { double YZAMS = 0.24 + 2.0 * m_Metallicity; - double Yhat = (x[1] - YZAMS) / (1.0 - YZAMS - m_Metallicity); + double Yhat = (centralHeliumFraction - YZAMS) / (1.0 - YZAMS - m_Metallicity); double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); - double previousTimestepInYrs = m_DtPrev * 1000000.0; + double currentTimestepInYrs = m_Dt * 1.0E6; + + double mDot = m_TotalMassLossRate; //(m_MassPrev - m_Mass) / previousTimestepInYrs; + + controlled_stepper_type controlled_stepper; + state_type x(3); + x[0] = totalMass; + x[1] = centralHeliumFraction; + x[2] = lnMixingCoreMass; - auto ode = [&](const state_type &x, state_type &dxdt, double previousTimestepInYrs) { - dxdt[0] = - p_Mdot; + auto ode = [&](const state_type &x, state_type &dxdt, const double) { + dxdt[0] = - mDot; dxdt[1] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); dxdt[2] = -alpha/(1.0 - alpha * x[1]) * dxdt[1] + beta * delta * dxdt[0] / x[0]; }; - integrate_adaptive(controlled_stepper, ode, x, 0.0, previousTimestepInYrs, previousTimestepInYrs/1000.0); + integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); + mixingCoreMass = std::exp(x[2]); m_CentralHeliumFraction = x[1]; - m_LastSolvedTime = m_Time; + return mixingCoreMass; } -double MainSequence::CalculateMainSequenceCoreMass(double p_Mdot) { - switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - double coreMass = CalculateMainSequenceCoreMassMandel(); - return coreMass; - } - case CORE_MASS_PRESCRIPTION::NONE: { - double coreMass = 0.0; - return coreMass; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - double coreMass = CalculateMainSequenceCoreMassShikauchi(p_Mdot); - return coreMass; - } - } -} - - /* * Update the minimum core mass of a main sequence star that loses mass through Case A mass transfer by * setting it equal to the core mass of a TAMS star, scaled by the fractional age. @@ -947,7 +928,7 @@ double MainSequence::CalculateMainSequenceCoreMass(double p_Mdot) { * void UpdateMinimumCoreMass() * */ -void MainSequence::UpdateMinimumCoreMass(double p_Mdot) { +void MainSequence::UpdateMinimumCoreMass() { switch (OPTIONS->MainSequenceCoreMassPrescription()) { case CORE_MASS_PRESCRIPTION::MANDEL: m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); @@ -956,7 +937,7 @@ void MainSequence::UpdateMinimumCoreMass(double p_Mdot) { m_MinimumCoreMass = 0.0; break; case CORE_MASS_PRESCRIPTION::SHIKAUCHI: - m_MixingCoreMass = CalculateMainSequenceCoreMassShikauchi(p_Mdot); + m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); break; } } diff --git a/src/MainSequence.h b/src/MainSequence.h index 7b7e7c4fd..b4ae29da5 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -20,12 +20,6 @@ class MainSequence: virtual public BaseStar { MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::A; } // Always case A - double MixingCoreMass() const { return m_MixingCoreMass; } - - double CentralHeliumFraction() const { return m_CentralHeliumFraction; } - - double m_LastSolvedTime = 0.0; - protected: @@ -75,9 +69,8 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityAtPhaseEnd() const { return CalculateLuminosityAtPhaseEnd(m_Mass0); } // Use class member variables double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables - double CalculateMainSequenceCoreMass(double p_Mdot); double CalculateMainSequenceCoreMassMandel(); - double CalculateMainSequenceCoreMassShikauchi(double p_Mdot); + double CalculateMainSequenceCoreMassShikauchi(); double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 double CalculatePerturbationMu() const { return 5.0; } // mu(MS) = 5.0 (Hurley et al. 2000, eqs 97 & 98) @@ -118,8 +111,7 @@ class MainSequence: virtual public BaseStar { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateMinimumCoreMass() { }; - void UpdateMinimumCoreMass(double p_Mdot); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + void UpdateMinimumCoreMass(); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass }; diff --git a/src/Star.h b/src/Star.h index 73a039349..e8762acba 100755 --- a/src/Star.h +++ b/src/Star.h @@ -142,6 +142,7 @@ class Star { double Tau() const { return m_Star->Tau(); } double Temperature() const { return m_Star->Temperature(); } double Timescale(TIMESCALE p_Timescale) const { return m_Star->Timescale(p_Timescale); } + double TotalMassLossRate() const { return m_Star->TotalMassLossRate(); } double XExponent() const { return m_Star->XExponent(); } @@ -289,11 +290,11 @@ class Star { p_Epsilon);} void UpdateMinimumCoreMass() { m_Star->UpdateMinimumCoreMass(); } - - void UpdateMinimumCoreMass(double p_Mdot) { m_Star->UpdateMinimumCoreMass(p_Mdot); } - + void UpdatePreviousTimestepDuration() { m_Star->UpdatePreviousTimestepDuration(); } + void UpdateTotalMassLossRate(const double p_MassLossRate) { m_Star->UpdateTotalMassLossRate(p_MassLossRate); } + ACCRETION_REGIME WhiteDwarfAccretionRegime() const { return m_Star->WhiteDwarfAccretionRegime(); } private: From 2c415c6796d4145824994906559e36cb4f10f5fe Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Wed, 25 Sep 2024 17:28:31 +1000 Subject: [PATCH 05/25] Resolve merge conflicts --- src/BaseBinaryStar.cpp | 7 ++++--- src/BaseStar.cpp | 12 ++---------- src/BaseStar.h | 5 ++--- src/CH.h | 6 ++++-- src/GiantBranch.h | 2 +- src/MainSequence.cpp | 20 +++++++++++++++----- src/Options.cpp | 6 ------ src/Options.h | 7 +------ 8 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index 1e4b0eb5f..0e9be474c 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -2065,9 +2065,10 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { if (utils::Compare(m_MassLossRateInRLOF,donorMassLossRateNuclear) == 0) // if transferring mass on nuclear timescale, limit mass loss amount to rate * timestep (thermal timescale MT always happens in one timestep) massDiffDonor = std::min(massDiffDonor, m_MassLossRateInRLOF * m_Dt); massDiffDonor = -massDiffDonor; // set mass difference - - m_Donor->UpdateTotalMassLossRate(-massDiffDonor / (p_Dt * 1000000.0)); - m_Donor->UpdateMinimumCoreMass(); // reset the minimum core mass following case A + m_Donor->UpdateTotalMassLossRate(-massDiffDonor / (p_Dt * 1.0E6)); + m_Donor->UpdateMinimumCoreMass(p_Dt, m_Donor->TotalMassLossRate()); // reset the minimum core mass following case A + m_Accretor->UpdateTotalMassLossRate((massDiffDonor * m_FractionAccreted) / (p_Dt * 1.0E6)); + m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); } } diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 8f7cdfd38..05e480cac 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -3243,12 +3243,6 @@ void BaseStar::UpdateMassTransferDonorHistory() { } -void BaseStar::UpdateTotalMassLossRate(const double p_MassLossRate) { - //m_TotalMassLossRatePrev = m_TotalMassLossRate; - m_TotalMassLossRate = p_MassLossRate; -} - - /////////////////////////////////////////////////////////////////////////////////////// // // // TEMPERATURE CALCULATIONS // @@ -4743,10 +4737,8 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { m_COCoreMass = CalculateCOCoreMassOnPhase(); m_CoreMass = CalculateCoreMassOnPhase(); m_HeCoreMass = CalculateHeCoreMassOnPhase(); - - if (m_StellarType == STELLAR_TYPE::MS_GT_07 & p_DeltaTime > 0.0 & m_Mdot > 0.0) { - UpdateMinimumCoreMass(); - } + + UpdateMinimumCoreMass(p_DeltaTime, m_Mdot); m_Luminosity = CalculateLuminosityOnPhase(); diff --git a/src/BaseStar.h b/src/BaseStar.h index fdfb265d9..9ba83ce8d 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -357,9 +357,9 @@ class BaseStar { const double p_MassGainPerTimeStep, const double p_Epsilon) { } // Default is NO-OP - virtual void UpdateMinimumCoreMass() { }; // Only set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass; default is NO-OP + virtual void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { }; // Only set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass; default is NO-OP - virtual void UpdateTotalMassLossRate(const double p_MassLossRate); + virtual void UpdateTotalMassLossRate(const double p_MassLossRate) { m_TotalMassLossRate = p_MassLossRate; } // printing functions bool PrintDetailedOutput(const int p_Id, const SSE_DETAILED_RECORD_TYPE p_RecordType) const { @@ -444,7 +444,6 @@ class BaseStar { double m_Temperature; // Current temperature (Tsol) double m_Time; // Current physical time the star has been evolved (Myr) double m_TotalMassLossRate; - double m_TotalMassLossRatePrev; // Previous timestep variables double m_DtPrev; // Previous timestep diff --git a/src/CH.h b/src/CH.h index 57572ef38..4ec6b6b2f 100755 --- a/src/CH.h +++ b/src/CH.h @@ -88,8 +88,10 @@ class CH: virtual public BaseStar, public MS_gt_07 { bool ShouldEvolveOnPhase() const { return m_Age < m_Timescales[static_cast(TIMESCALE::tMS)] && (OPTIONS->OptimisticCHE() || m_Omega >= m_OmegaCHE); } // Evolve on CHE phase if age in MS timescale and spinning at least as fast as CHE threshold - void UpdateAgeAfterMassLoss(); - + void UpdateAgeAfterMassLoss(); + + void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } + }; #endif // __CH_h__ diff --git a/src/GiantBranch.h b/src/GiantBranch.h index 93c432029..c595cbbeb 100755 --- a/src/GiantBranch.h +++ b/src/GiantBranch.h @@ -136,7 +136,7 @@ class GiantBranch: virtual public BaseStar, public MainSequence { void UpdateInitialMass() { } // NO-OP for most stellar types - void UpdateMinimumCoreMass() { } // NO-OP for most stellar types + void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // NO-OP for most stellar types }; diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 159e49543..4e8433cd7 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -928,17 +928,27 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi() { * void UpdateMinimumCoreMass() * */ -void MainSequence::UpdateMinimumCoreMass() { +void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { + if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate) // Only proceed with calculation if time advances and calculation was not executed in binary evolution + return; switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: + case CORE_MASS_PRESCRIPTION::MANDEL: { m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); break; - case CORE_MASS_PRESCRIPTION::NONE: + } + case CORE_MASS_PRESCRIPTION::NONE: { m_MinimumCoreMass = 0.0; break; - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: - m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + if (p_TotalMassLossRate >= 0.0) { // Mass Loss + m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); + } + else { // Mass gain + m_MinimumCoreMass = m_MinimumCoreMass; + } break; + } } } diff --git a/src/Options.cpp b/src/Options.cpp index 9f3939c0c..d0281d830 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -415,7 +415,6 @@ void Options::OptionValues::Initialise() { m_UseMassTransfer = true; m_CirculariseBinaryDuringMassTransfer = true; m_AngularMomentumConservationDuringCircularisation = false; - //m_RetainCoreMassDuringCaseAMassTransfer = true; m_ConvectiveEnvelopeTemperatureThreshold = CONVECTIVE_BOUNDARY_TEMPERATURE_BELCZYNSKI; // Case BB/BC mass transfer stability prescription @@ -879,11 +878,6 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Suppress printing (default = " + std::string(p_Options->m_Quiet ? "TRUE" : "FALSE") + ")").c_str() ) - /*( - "retain-core-mass-during-caseA-mass-transfer", - po::value(&p_Options->m_RetainCoreMassDuringCaseAMassTransfer)->default_value(p_Options->m_RetainCoreMassDuringCaseAMassTransfer)->implicit_value(true), - ("Retain approximate core mass of a case A donor as a minimum core at end of MS or HeMS (default = " + std::string(p_Options->m_RetainCoreMassDuringCaseAMassTransfer ? "TRUE" : "FALSE") + ")").c_str() - )*/ ( "revised-energy-formalism-nandez-ivanova", po::value(&p_Options->m_RevisedEnergyFormalismNandezIvanova)->default_value(p_Options->m_RevisedEnergyFormalismNandezIvanova)->implicit_value(true), diff --git a/src/Options.h b/src/Options.h index 2d9c64079..cc882faf2 100755 --- a/src/Options.h +++ b/src/Options.h @@ -993,9 +993,7 @@ class Options { bool m_ExpelConvectiveEnvelopeAboveLuminosityThreshold; // Whether to expel the convective envelope in a pulsation when log_10(L/M) reaches the threshold defined by m_LuminosityToMassThreshold double m_LuminosityToMassThreshold; // Threshold value of log_10(L/M) above which the convective envelope is expelled in a pulsation - -/* bool m_RetainCoreMassDuringCaseAMassTransfer; // Whether to retain the approximate core mass of a case A donor as a minimum core at end of MS or HeMS (default = false) - */ + ENUM_OPT m_MainSequenceCoreMassPrescription; ENUM_OPT m_CaseBBStabilityPrescription; // Which prescription for the stability of case BB/BC mass transfer @@ -1624,9 +1622,6 @@ class Options { bool RequestedHelp() const { return m_CmdLine.optionValues.m_VM["help"].as(); } bool RequestedVersion() const { return m_CmdLine.optionValues.m_VM["version"].as(); } - - /*bool RetainCoreMassDuringCaseAMassTransfer() const { return m_CmdLine.optionValues.m_RetainCoreMassDuringCaseAMassTransfer; } - */ bool RLOFPrinting() const { return m_CmdLine.optionValues.m_RlofPrinting; } double RocketKickMagnitude1() const { return OPT_VALUE("rocket-kick-magnitude-1", m_RocketKickMagnitude1, true); } From 220b46663ee30b1dab905fd8ab7ec02e2e685a3a Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 1 Nov 2024 15:50:35 +1100 Subject: [PATCH 06/25] Resolve merge conflicts --- src/MainSequence.cpp | 28 +++++++++++++++++----------- src/Options.cpp | 2 +- src/Options.h | 4 +++- src/typedefs.h | 8 ++++++++ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 4e8433cd7..22106481b 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -928,25 +928,31 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi() { * void UpdateMinimumCoreMass() * */ -void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { - if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate) // Only proceed with calculation if time advances and calculation was not executed in binary evolution - return; +void MainSequence::UpdateMinimumCoreMass() { switch (OPTIONS->MainSequenceCoreMassPrescription()) { case CORE_MASS_PRESCRIPTION::MANDEL: { - m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); + + // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. + // Since we are on the main sequence here, we can clone this object as an HG object + // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), + // we can query the cloned object for its core mass. + // + // The clone should not evolve, and so should not log anything, but to be sure the + // clone does not participate in logging, we set its persistence to EPHEMERAL. + + HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); + double TAMSCoreMass = clone->CoreMass(); // get core mass from clone + delete clone; clone = nullptr; // return the memory allocated for the clone + + m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass break; } case CORE_MASS_PRESCRIPTION::NONE: { - m_MinimumCoreMass = 0.0; + m_MinimumCoreMass = 0.0; break; } case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - if (p_TotalMassLossRate >= 0.0) { // Mass Loss - m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); - } - else { // Mass gain - m_MinimumCoreMass = m_MinimumCoreMass; - } + m_MinimumCoreMass = 0.0; break; } } diff --git a/src/Options.cpp b/src/Options.cpp index d0281d830..865cac830 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -411,6 +411,7 @@ void Options::OptionValues::Initialise() { // Core mass prescription m_MainSequenceCoreMassPrescription.type = CORE_MASS_PRESCRIPTION::MANDEL; m_MainSequenceCoreMassPrescription.typeString = CORE_MASS_PRESCRIPTION_LABEL.at(m_MainSequenceCoreMassPrescription.type); + // Mass transfer options m_UseMassTransfer = true; m_CirculariseBinaryDuringMassTransfer = true; @@ -1785,7 +1786,6 @@ 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() ) - ( "mass-loss-prescription", po::value(&p_Options->m_MassLossPrescription.typeString)->default_value(p_Options->m_MassLossPrescription.typeString), diff --git a/src/Options.h b/src/Options.h index cc882faf2..5ce06f1ce 100755 --- a/src/Options.h +++ b/src/Options.h @@ -618,7 +618,7 @@ class Options { "logfile-system-parameters-record-types", "logfile-type", "luminous-blue-variable-prescription", // DEPRECATED June 2024 - remove end 2024 - + "main-sequence-core-mass-prescription", "mass-change-fraction", "mass-loss-prescription", @@ -1506,6 +1506,8 @@ class Options { CORE_MASS_PRESCRIPTION MainSequenceCoreMassPrescription() const { return OPT_VALUE("main-sequence-core-mass-prescription", m_MainSequenceCoreMassPrescription.type, true); } + CORE_MASS_PRESCRIPTION MainSequenceCoreMassPrescription() const { return OPT_VALUE("main-sequence-core-mass-prescription", m_MainSequenceCoreMassPrescription.type, true); } + double MassChangeFraction() const { return m_CmdLine.optionValues.m_MassChangeFraction; } MASS_LOSS_PRESCRIPTION MassLossPrescription() const { return OPT_VALUE("mass-loss-prescription", m_MassLossPrescription.type, true); } diff --git a/src/typedefs.h b/src/typedefs.h index 5fcf969b5..0b5c111f2 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -587,6 +587,14 @@ enum class MASS_CUTOFF: int { COUNT // Sentinel for entry count }; +// main sequence core mass prescription +enum class CORE_MASS_PRESCRIPTION: int { NONE, MANDEL, SHIKAUCHI }; +const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { + { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, // DEPRECATED June 2024 - remove end 2024 + { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, + { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } +}; + // mass loss prescriptions enum class MASS_LOSS_PRESCRIPTION: int { NONE, ZERO, HURLEY, BELCZYNSKI2010, MERRITT2024 }; const COMPASUnorderedMap MASS_LOSS_PRESCRIPTION_LABEL = { From e90d028bddacbdbe0fe3a5a57338ae0e426414ee Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 30 Aug 2024 13:15:06 +1000 Subject: [PATCH 07/25] Fixed issues with --main-sequence-core-mass-prescription --- src/MainSequence.cpp | 8 +++----- src/typedefs.h | 8 -------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 22106481b..cd662cf0f 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -943,18 +943,16 @@ void MainSequence::UpdateMinimumCoreMass() { HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); double TAMSCoreMass = clone->CoreMass(); // get core mass from clone delete clone; clone = nullptr; // return the memory allocated for the clone - m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass break; } - case CORE_MASS_PRESCRIPTION::NONE: { + case CORE_MASS_PRESCRIPTION::NONE: m_MinimumCoreMass = 0.0; break; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: m_MinimumCoreMass = 0.0; break; - } } } diff --git a/src/typedefs.h b/src/typedefs.h index 0b5c111f2..5fcf969b5 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -587,14 +587,6 @@ enum class MASS_CUTOFF: int { COUNT // Sentinel for entry count }; -// main sequence core mass prescription -enum class CORE_MASS_PRESCRIPTION: int { NONE, MANDEL, SHIKAUCHI }; -const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { - { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, // DEPRECATED June 2024 - remove end 2024 - { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, - { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } -}; - // mass loss prescriptions enum class MASS_LOSS_PRESCRIPTION: int { NONE, ZERO, HURLEY, BELCZYNSKI2010, MERRITT2024 }; const COMPASUnorderedMap MASS_LOSS_PRESCRIPTION_LABEL = { From 836871fdde22ed8e2cec54a78c9245de59d4af25 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Sun, 3 Nov 2024 15:01:01 +1100 Subject: [PATCH 08/25] Resolve merge conflicts --- src/BaseStar.cpp | 22 ++++++++++------------ src/BaseStar.h | 2 ++ src/MainSequence.cpp | 24 +++++------------------- src/MainSequence.h | 3 ++- src/Star.h | 4 +++- 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 05e480cac..3e49d4210 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -2995,18 +2995,8 @@ void BaseStar::ResolveMassLoss(const bool p_UpdateMDt) { double BaseStar::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { - switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - return 0.0; - } - case CORE_MASS_PRESCRIPTION::NONE: { - return 0.0; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); - return fmix * p_MZAMS; - } - } + double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); + return fmix * p_MZAMS; } @@ -4796,7 +4786,15 @@ STELLAR_TYPE BaseStar::ResolveEndOfPhase(const bool p_ResolveEnvelopeLoss) { m_COCoreMass = CalculateCOCoreMassAtPhaseEnd(); m_CoreMass = CalculateCoreMassAtPhaseEnd(); m_HeCoreMass = CalculateHeCoreMassAtPhaseEnd(); + if (m_StellarType == STELLAR_TYPE::MS_GT_07) { + //m_MixingCoreMass = CalculateMainSequenceCoreMass(m_Mdot); + //m_CoreMass = m_MixingCoreMass; + //m_HeCoreMass = m_MixingCoreMass; + //std::cout << m_MixingCoreMass << " "; + std::cout << "MixCore:" << m_MixingCoreMass << " Mass:" << m_Mass << " Mdot:" <<(m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0) << ":timestep" << m_DtPrev << " Yc:" << m_CentralHeliumFraction << " TimePassed:" << m_Time; + } + m_Luminosity = CalculateLuminosityAtPhaseEnd(); m_Radius = CalculateRadiusAtPhaseEnd(); diff --git a/src/BaseStar.h b/src/BaseStar.h index 9ba83ce8d..c0a189281 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -144,6 +144,7 @@ class BaseStar { double Mdot() const { return m_Mdot; } double Metallicity() const { return m_Metallicity; } double MinimumCoreMass() const { return m_MinimumCoreMass; } + double MixingCoreMass() const { return m_MixingCoreMass; } double MZAMS() const { return m_MZAMS; } double Omega() const { return m_Omega; } double OmegaCHE() const { return m_OmegaCHE; } @@ -528,6 +529,7 @@ class BaseStar { virtual double CalculateCOCoreMassOnPhase() const { return m_COCoreMass; } // Default is NO-OP virtual double CalculateCoreMassAtPhaseEnd() const { return m_CoreMass; } // Default is NO-OP + virtual double CalculateMainSequenceCoreMass(double p_Mdot) { return m_MixingCoreMass; } static double CalculateCoreMassGivenLuminosity_Static(const double p_Luminosity, const DBL_VECTOR &p_GBParams); virtual double CalculateCoreMassOnPhase() const { return m_CoreMass; } // Default is NO-OP diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index cd662cf0f..c4ad31f32 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -928,30 +928,16 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi() { * void UpdateMinimumCoreMass() * */ -void MainSequence::UpdateMinimumCoreMass() { +void MainSequence::UpdateMinimumCoreMass(double p_Mdot) { switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - - // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. - // Since we are on the main sequence here, we can clone this object as an HG object - // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), - // we can query the cloned object for its core mass. - // - // The clone should not evolve, and so should not log anything, but to be sure the - // clone does not participate in logging, we set its persistence to EPHEMERAL. - - HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); - double TAMSCoreMass = clone->CoreMass(); // get core mass from clone - delete clone; clone = nullptr; // return the memory allocated for the clone - m_MinimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + case CORE_MASS_PRESCRIPTION::MANDEL: + m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); break; - } case CORE_MASS_PRESCRIPTION::NONE: - m_MinimumCoreMass = 0.0; + m_MinimumCoreMass = 0.0; break; - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: - m_MinimumCoreMass = 0.0; + m_MixingCoreMass = CalculateMainSequenceCoreMassShikauchi(p_Mdot); break; } } diff --git a/src/MainSequence.h b/src/MainSequence.h index b4ae29da5..9e2e0f8bf 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -111,7 +111,8 @@ class MainSequence: virtual public BaseStar { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateMinimumCoreMass(); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + void UpdateMinimumCoreMass() { }; + void UpdateMinimumCoreMass(double p_Mdot); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass }; diff --git a/src/Star.h b/src/Star.h index e8762acba..85e92bf77 100755 --- a/src/Star.h +++ b/src/Star.h @@ -290,7 +290,9 @@ class Star { p_Epsilon);} void UpdateMinimumCoreMass() { m_Star->UpdateMinimumCoreMass(); } - + + void UpdateMinimumCoreMass(double p_Mdot) { m_Star->UpdateMinimumCoreMass(p_Mdot); } + void UpdatePreviousTimestepDuration() { m_Star->UpdatePreviousTimestepDuration(); } void UpdateTotalMassLossRate(const double p_MassLossRate) { m_Star->UpdateTotalMassLossRate(p_MassLossRate); } From af542fed2137a106d8e6aef67fec656ccda74c15 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Sun, 3 Nov 2024 15:03:49 +1100 Subject: [PATCH 09/25] Resolve merge conflicts --- src/BaseStar.cpp | 28 ++++++++++++++++++---------- src/BaseStar.h | 2 -- src/MainSequence.cpp | 4 ++-- src/MainSequence.h | 3 +-- src/Star.h | 4 +--- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 3e49d4210..ac4f7b0f1 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -2995,8 +2995,18 @@ void BaseStar::ResolveMassLoss(const bool p_UpdateMDt) { double BaseStar::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { - double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); - return fmix * p_MZAMS; + switch (OPTIONS->MainSequenceCoreMassPrescription()) { + case CORE_MASS_PRESCRIPTION::MANDEL: { + return 0.0; + } + case CORE_MASS_PRESCRIPTION::NONE: { + return 0.0; + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); + return fmix * p_MZAMS; + } + } } @@ -3233,6 +3243,12 @@ void BaseStar::UpdateMassTransferDonorHistory() { } +void BaseStar::UpdateTotalMassLossRate(const double p_MassLossRate) { + //m_TotalMassLossRatePrev = m_TotalMassLossRate; + m_TotalMassLossRate = p_MassLossRate; +} + + /////////////////////////////////////////////////////////////////////////////////////// // // // TEMPERATURE CALCULATIONS // @@ -4786,15 +4802,7 @@ STELLAR_TYPE BaseStar::ResolveEndOfPhase(const bool p_ResolveEnvelopeLoss) { m_COCoreMass = CalculateCOCoreMassAtPhaseEnd(); m_CoreMass = CalculateCoreMassAtPhaseEnd(); m_HeCoreMass = CalculateHeCoreMassAtPhaseEnd(); - if (m_StellarType == STELLAR_TYPE::MS_GT_07) { - //m_MixingCoreMass = CalculateMainSequenceCoreMass(m_Mdot); - //m_CoreMass = m_MixingCoreMass; - //m_HeCoreMass = m_MixingCoreMass; - //std::cout << m_MixingCoreMass << " "; - std::cout << "MixCore:" << m_MixingCoreMass << " Mass:" << m_Mass << " Mdot:" <<(m_MassPrev - m_Mass) / (m_DtPrev * 1000000.0) << ":timestep" << m_DtPrev << " Yc:" << m_CentralHeliumFraction << " TimePassed:" << m_Time; - } - m_Luminosity = CalculateLuminosityAtPhaseEnd(); m_Radius = CalculateRadiusAtPhaseEnd(); diff --git a/src/BaseStar.h b/src/BaseStar.h index c0a189281..9ba83ce8d 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -144,7 +144,6 @@ class BaseStar { double Mdot() const { return m_Mdot; } double Metallicity() const { return m_Metallicity; } double MinimumCoreMass() const { return m_MinimumCoreMass; } - double MixingCoreMass() const { return m_MixingCoreMass; } double MZAMS() const { return m_MZAMS; } double Omega() const { return m_Omega; } double OmegaCHE() const { return m_OmegaCHE; } @@ -529,7 +528,6 @@ class BaseStar { virtual double CalculateCOCoreMassOnPhase() const { return m_COCoreMass; } // Default is NO-OP virtual double CalculateCoreMassAtPhaseEnd() const { return m_CoreMass; } // Default is NO-OP - virtual double CalculateMainSequenceCoreMass(double p_Mdot) { return m_MixingCoreMass; } static double CalculateCoreMassGivenLuminosity_Static(const double p_Luminosity, const DBL_VECTOR &p_GBParams); virtual double CalculateCoreMassOnPhase() const { return m_CoreMass; } // Default is NO-OP diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index c4ad31f32..159e49543 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -928,7 +928,7 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi() { * void UpdateMinimumCoreMass() * */ -void MainSequence::UpdateMinimumCoreMass(double p_Mdot) { +void MainSequence::UpdateMinimumCoreMass() { switch (OPTIONS->MainSequenceCoreMassPrescription()) { case CORE_MASS_PRESCRIPTION::MANDEL: m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); @@ -937,7 +937,7 @@ void MainSequence::UpdateMinimumCoreMass(double p_Mdot) { m_MinimumCoreMass = 0.0; break; case CORE_MASS_PRESCRIPTION::SHIKAUCHI: - m_MixingCoreMass = CalculateMainSequenceCoreMassShikauchi(p_Mdot); + m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); break; } } diff --git a/src/MainSequence.h b/src/MainSequence.h index 9e2e0f8bf..b4ae29da5 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -111,8 +111,7 @@ class MainSequence: virtual public BaseStar { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateMinimumCoreMass() { }; - void UpdateMinimumCoreMass(double p_Mdot); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + void UpdateMinimumCoreMass(); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass }; diff --git a/src/Star.h b/src/Star.h index 85e92bf77..e8762acba 100755 --- a/src/Star.h +++ b/src/Star.h @@ -290,9 +290,7 @@ class Star { p_Epsilon);} void UpdateMinimumCoreMass() { m_Star->UpdateMinimumCoreMass(); } - - void UpdateMinimumCoreMass(double p_Mdot) { m_Star->UpdateMinimumCoreMass(p_Mdot); } - + void UpdatePreviousTimestepDuration() { m_Star->UpdatePreviousTimestepDuration(); } void UpdateTotalMassLossRate(const double p_MassLossRate) { m_Star->UpdateTotalMassLossRate(p_MassLossRate); } From 777f67d496d0f21b4b8f7956037597f86df0e8e3 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Sun, 3 Nov 2024 15:59:46 +1100 Subject: [PATCH 10/25] Resolve merge conflicts --- src/BaseStar.cpp | 6 ------ src/MainSequence.cpp | 20 +++++++++++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index ac4f7b0f1..05e480cac 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -3243,12 +3243,6 @@ void BaseStar::UpdateMassTransferDonorHistory() { } -void BaseStar::UpdateTotalMassLossRate(const double p_MassLossRate) { - //m_TotalMassLossRatePrev = m_TotalMassLossRate; - m_TotalMassLossRate = p_MassLossRate; -} - - /////////////////////////////////////////////////////////////////////////////////////// // // // TEMPERATURE CALCULATIONS // diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 159e49543..4e8433cd7 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -928,17 +928,27 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi() { * void UpdateMinimumCoreMass() * */ -void MainSequence::UpdateMinimumCoreMass() { +void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { + if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate) // Only proceed with calculation if time advances and calculation was not executed in binary evolution + return; switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: + case CORE_MASS_PRESCRIPTION::MANDEL: { m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); break; - case CORE_MASS_PRESCRIPTION::NONE: + } + case CORE_MASS_PRESCRIPTION::NONE: { m_MinimumCoreMass = 0.0; break; - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: - m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + if (p_TotalMassLossRate >= 0.0) { // Mass Loss + m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); + } + else { // Mass gain + m_MinimumCoreMass = m_MinimumCoreMass; + } break; + } } } From 681d88d49c173ae9b8a26d46df18c34ac3ffe397 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Sun, 3 Nov 2024 16:01:31 +1100 Subject: [PATCH 11/25] Resolve merge conflicts --- src/BaseBinaryStar.cpp | 3 +- src/BaseStar.cpp | 27 +--- src/BaseStar.h | 3 - src/MS_gt_07.h | 1 + src/MainSequence.cpp | 320 ++++++++++++++++++++++++++--------------- src/MainSequence.h | 9 +- src/Star.h | 2 +- src/constants.h | 26 ++-- 8 files changed, 234 insertions(+), 157 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index 0e9be474c..b4aec3147 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -354,7 +354,6 @@ void BaseBinaryStar::SetRemainingValues() { m_MassTransferTrackerHistory = MT_TRACKING::NO_MASS_TRANSFER; m_MassTransfer = false; - m_MassLossRateInRLOF = DEFAULT_INITIAL_DOUBLE_VALUE; m_JLoss = OPTIONS->MassTransferJloss(); @@ -2066,7 +2065,7 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { massDiffDonor = std::min(massDiffDonor, m_MassLossRateInRLOF * m_Dt); massDiffDonor = -massDiffDonor; // set mass difference m_Donor->UpdateTotalMassLossRate(-massDiffDonor / (p_Dt * 1.0E6)); - m_Donor->UpdateMinimumCoreMass(p_Dt, m_Donor->TotalMassLossRate()); // reset the minimum core mass following case A + m_Donor->UpdateMinimumCoreMass(p_Dt, m_Donor->TotalMassLossRate()); m_Accretor->UpdateTotalMassLossRate((massDiffDonor * m_FractionAccreted) / (p_Dt * 1.0E6)); m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); } diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 05e480cac..c7fd950b3 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -137,8 +137,7 @@ BaseStar::BaseStar(const unsigned long int p_RandomSeed, m_Age = 0.0; // ensure age = 0.0 at construction (rather than default initial value) m_Mass = m_MZAMS; m_Mass0 = m_MZAMS; - m_MinimumCoreMass = CalculateMixingCoreMassAtZAMS(m_MZAMS); - m_CentralHeliumFraction = utils::MESAZAMSHeliumFractionByMetallicity(m_Metallicity); + m_MinimumCoreMass = DEFAULT_INITIAL_DOUBLE_VALUE; m_Luminosity = m_LZAMS; m_Radius = m_RZAMS; m_Temperature = m_TZAMS; @@ -2994,22 +2993,6 @@ void BaseStar::ResolveMassLoss(const bool p_UpdateMDt) { } -double BaseStar::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { - switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - return 0.0; - } - case CORE_MASS_PRESCRIPTION::NONE: { - return 0.0; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(-p_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); - return fmix * p_MZAMS; - } - } -} - - /* * Calculate core mass for a given luminosity using the Mc - L relation * @@ -4734,11 +4717,11 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { if (ShouldEvolveOnPhase()) { // evolve timestep on phase m_Tau = CalculateTauOnPhase(); - m_COCoreMass = CalculateCOCoreMassOnPhase(); - m_CoreMass = CalculateCoreMassOnPhase(); - m_HeCoreMass = CalculateHeCoreMassOnPhase(); + m_COCoreMass = CalculateCOCoreMassOnPhase(); + m_CoreMass = CalculateCoreMassOnPhase(); + m_HeCoreMass = CalculateHeCoreMassOnPhase(); - UpdateMinimumCoreMass(p_DeltaTime, m_Mdot); + UpdateMinimumCoreMass(p_DeltaTime, m_Mdot); // update core mass, relevant for MS stars m_Luminosity = CalculateLuminosityOnPhase(); diff --git a/src/BaseStar.h b/src/BaseStar.h index 9ba83ce8d..26d698789 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -422,7 +422,6 @@ class BaseStar { double m_CoreMass; // Current core mass (Msol) double m_Dt; // Size of current timestep (Myr) bool m_EnvelopeJustExpelledByPulsations; // Flag to know if the convective envelope has just been expelled by pulsations - double m_CentralHeliumFraction; double m_HeCoreMass; // Current He core mass (Msol) double m_HeliumAbundanceCore; // Helium abundance in the core double m_HeliumAbundanceSurface; // Helium abundance at the surface @@ -623,8 +622,6 @@ class BaseStar { double CalculateMaximumCoreMass(double p_Mass) const; - double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); - double CalculateOmegaBreak() const; static double CalculateOpacity_Static(const double p_HeliumAbundanceSurface); diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index 9619eb288..daed64515 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -41,6 +41,7 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { void Initialise() { CalculateTimescales(); // Initialise timescales // Age for MS_GT_07 is carried over from CH stars switching to MS after spinning down, so not set to 0.0 here + m_MinimumCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); // Initialise initial core mass } diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 4e8433cd7..5440aa24d 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -29,6 +29,8 @@ * @return Helium abundance in the core (Y_c) */ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const { + if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) + return m_HeliumAbundanceCore; double heliumAbundanceCoreMax = 1.0 - m_Metallicity; return ((heliumAbundanceCoreMax - m_InitialHeliumAbundance) * p_Tau) + m_InitialHeliumAbundance; } @@ -49,6 +51,8 @@ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) con * @return Hydrogen abundance in the core (X_c) */ double MainSequence::CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) const { + if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) + return 1.0 - m_HeliumAbundanceCore - m_Metallicity; return m_InitialHydrogenAbundance * (1.0 - p_Tau); } @@ -676,6 +680,195 @@ DBL_DBL MainSequence::CalculateConvectiveEnvelopeMass() const { } +/* + * Calculate the minimum core mass of a main sequence star that loses mass through Case A mass transfer as + * the core mass of a TAMS star, scaled by the fractional age. + * + * The minimum core mass of the star is updated only if the retain-core-mass-during-caseA-mass-transfer + * option is specified, otherwise it is left unchanged. + * + * double CalculateMainSequenceCoreMassMandel() + * + * @return Minimum convective core mass in Msol + */ +double MainSequence::CalculateMainSequenceCoreMassMandel() { + // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. + // Since we are on the main sequence here, we can clone this object as an HG object + // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), + // we can query the cloned object for its core mass. + // + // The clone should not evolve, and so should not log anything, but to be sure the + // clone does not participate in logging, we set its persistence to EPHEMERAL. + + HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); + double TAMSCoreMass = clone->CoreMass(); // get core mass from clone + delete clone; clone = nullptr; // return the memory allocated for the clone + double minimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + return minimumCoreMass; +} + + +/* + * Calculate the convective core mass of a main sequence star that loses mass either through winds or + * Case A mass transfer according to Shikauchi et al. (2024) + * + * double CalculateMainSequenceCoreMassShikauchi() + * + * @return Mass of the convective core in Msol + */ +double MainSequence::CalculateMainSequenceCoreMassShikauchi() { + DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); + DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); + DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); + + double totalMass = m_Mass; + double centralHeliumFraction = m_HeliumAbundanceCore; + double mixingCoreMass = m_MinimumCoreMass; + double lnMixingCoreMass = std::log(mixingCoreMass); + double logMixingCoreMass = std::log10(mixingCoreMass); + + // Equation (A3) + double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(- m_MZAMS / FMIX_COEFFICIENTS[2]); + // Equation (A4) + double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(- totalMass / FMIX_COEFFICIENTS[2]); + // Equation (A5) + double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; + // Equation (A2) + double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * mixingCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; + // Equation (A7) + double g = -0.0044 * m_MZAMS + 0.27; + // Equation (10) + double Yhat = (centralHeliumFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity); + // Equation (A6) + double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); + + double currentTimestepInYrs = m_Dt * 1.0E6; + double mDot = m_TotalMassLossRate; + + // Use boost adaptive ODE solver for speed and accuracy + controlled_stepper_type controlled_stepper; + state_type x(3); + x[0] = totalMass; + x[1] = centralHeliumFraction; + x[2] = lnMixingCoreMass; + + auto ode = [&](const state_type &x, state_type &dxdt, const double) { + dxdt[0] = - mDot; + dxdt[1] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); + dxdt[2] = -alpha/(1.0 - alpha * x[1]) * dxdt[1] + beta * delta * dxdt[0] / x[0]; + }; + + integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); + + mixingCoreMass = std::exp(x[2]); // New mixing core mass + m_HeliumAbundanceCore = x[1]; // Update m_HeliumAbundanceCore + return mixingCoreMass; +} + + +/* + * Calculate the initial core mass of a main sequence star using Equation (A3) from Shikauchi et al. (2024) + * + * double CalculateMixingCoreMassAtZAMS(const double p_MZAMS) + * + * @param [IN] p_MZAMS Mass at ZAMS in Msol + * @return Mass of the convective core in Msol at ZAMS + */ +double MainSequence::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { + switch (OPTIONS->MainSequenceCoreMassPrescription()) { + case CORE_MASS_PRESCRIPTION::MANDEL: { + return 0.0; + } + case CORE_MASS_PRESCRIPTION::NONE: { + return 0.0; + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); + double fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_MZAMS / fmixCoefficients[2]); + return fmix * p_MZAMS; + } + } +} + + +/* + * Linear interpolation/extrapolation for coefficients from Shikauchi et al. (2024) + * + * std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) + * + * @param [IN] p_Metallicity Metallicity + * @return Tuple containing vectors of coefficients for the specified metallicity + */ +std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) const { + // Coefficients are given for these metallicities + double low = 0.1 * ZSOL_ASPLUND; + double middle = 1.0/3.0 * ZSOL_ASPLUND; + double high = ZSOL_ASPLUND; + + if (p_Metallicity <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound + return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); + else if ((p_Metallicity > low) && (p_Metallicity <= middle)) { // Linear interpolation between metallicity low and middle + DBL_VECTOR alphaCoeff(3, 0.0); + DBL_VECTOR fmixCoeff(3, 0.0); + DBL_VECTOR lCoeff(10, 0.0); + for (int i = 0; i < 3; i++) + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - p_Metallicity) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (p_Metallicity - low)) / (middle - low); + for (int i = 0; i < 3; i++) + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - p_Metallicity) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (p_Metallicity - low)) / (middle - low); + for (int i = 0; i < 10; i++) + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - p_Metallicity) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (p_Metallicity - low)) / (middle - low); + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + } + else if ((p_Metallicity > middle) && (p_Metallicity <= high)) { // Linear interpolation between metallicity middle and high + DBL_VECTOR alphaCoeff(3, 0.0); + DBL_VECTOR fmixCoeff(3, 0.0); + DBL_VECTOR lCoeff(10, 0.0); + for (int i = 0; i < 3; i++) + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - p_Metallicity) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (p_Metallicity - middle)) / (high - middle); + for (int i = 0; i < 3; i++) + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - p_Metallicity) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (p_Metallicity - middle)) / (high - middle); + for (int i = 0; i < 10; i++) + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - p_Metallicity) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (p_Metallicity - middle)) / (high - middle); + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + } + else // Linear extrapolation (constant) for metallicity higher than solar + return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); +} + + +/* + * Update the minimum core mass of a main sequence star that loses mass through winds or Case A mass transfer + * + * void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) + * + * @param [IN] p_Dt Current timestep + * @param [IN] p_TotalMassLossRate Mass loss rate either from stellar winds or mass transfer + */ +void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { + // Only proceed with updating the core mass if time advances, calculation was not executed in binary evolution, and MZAMS >= 15 Msol + if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate || m_MZAMS < 15.0) + return; + + switch (OPTIONS->MainSequenceCoreMassPrescription()) { + case CORE_MASS_PRESCRIPTION::NONE: { + m_MinimumCoreMass = 0.0; + break; + } + case CORE_MASS_PRESCRIPTION::MANDEL: { + m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); + break; + } + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + if (p_TotalMassLossRate >= 0.0) // Mass Loss + m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); + else // Mass gain + m_MinimumCoreMass = CalculateMixingCoreMassAtZAMS(m_Mass); + break; + } + } +} + + /////////////////////////////////////////////////////////////////////////////////////// // // // LIFETIME / AGE CALCULATIONS // @@ -748,20 +941,21 @@ void MainSequence::UpdateAgeAfterMassLoss() { m_Age *= tMSprime / tMS; } + - +/* + * Determine whether star should continue to evolve on phase + * + * + * bool ShouldEvolveOnPhase() + * + * @return true if evolution should continue on phase, false otherwise + */ bool MainSequence::ShouldEvolveOnPhase() const { - switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); - } - case CORE_MASS_PRESCRIPTION::NONE: { - return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - return (m_CentralHeliumFraction <= 1.0 - m_Metallicity & m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); - } - } + if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) + return ((m_HeliumAbundanceCore <= 1.0 - m_Metallicity) && (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)])); + else + return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); } @@ -851,108 +1045,6 @@ STELLAR_TYPE MainSequence::ResolveEnvelopeLoss(bool p_Force) { } -double MainSequence::CalculateMainSequenceCoreMassMandel() { - // We need TAMSCoreMass, which is just the core mass at the start of the HG phase. - // Since we are on the main sequence here, we can clone this object as an HG object - // and, as long as it is initialised (to correctly set Tau to 0.0 on the HG phase), - // we can query the cloned object for its core mass. - // - // The clone should not evolve, and so should not log anything, but to be sure the - // clone does not participate in logging, we set its persistence to EPHEMERAL. - - HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); - double TAMSCoreMass = clone->CoreMass(); // get core mass from clone - delete clone; clone = nullptr; // return the memory allocated for the clone - double minimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass - //double coreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); - return minimumCoreMass; -} - - -double MainSequence::CalculateMainSequenceCoreMassShikauchi() { - double totalMass = m_Mass; - double centralHeliumFraction = m_CentralHeliumFraction; - double mixingCoreMass = m_MinimumCoreMass; - double lnMixingCoreMass = std::log(mixingCoreMass); - double logMixingCoreMass = std::log10(mixingCoreMass); - - double fmix = SHIKAUCHI_FMIX_COEFFICIENTS[0][0] + SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * std::exp(- m_MZAMS / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); - - double beta = 1.0 - SHIKAUCHI_FMIX_COEFFICIENTS[0][1] * totalMass / (SHIKAUCHI_FMIX_COEFFICIENTS[0][2] * fmix) * std::exp(- totalMass / SHIKAUCHI_FMIX_COEFFICIENTS[0][2]); - - double logL = SHIKAUCHI_L_COEFFICIENTS[0][0] * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][1] * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][2] * logMixingCoreMass * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][3] * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][4] * centralHeliumFraction * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + SHIKAUCHI_L_COEFFICIENTS[0][6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + SHIKAUCHI_L_COEFFICIENTS[0][9]; - - double alpha = PPOW(10.0, std::max(-2.0, SHIKAUCHI_ALPHA_COEFFICIENTS[0][1] * mixingCoreMass + SHIKAUCHI_ALPHA_COEFFICIENTS[0][2])) + SHIKAUCHI_ALPHA_COEFFICIENTS[0][0]; - - double g = -0.0044 * m_MZAMS + 0.27; - - double YZAMS = 0.24 + 2.0 * m_Metallicity; - - double Yhat = (centralHeliumFraction - YZAMS) / (1.0 - YZAMS - m_Metallicity); - - double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); - - double currentTimestepInYrs = m_Dt * 1.0E6; - - double mDot = m_TotalMassLossRate; //(m_MassPrev - m_Mass) / previousTimestepInYrs; - - controlled_stepper_type controlled_stepper; - state_type x(3); - x[0] = totalMass; - x[1] = centralHeliumFraction; - x[2] = lnMixingCoreMass; - - auto ode = [&](const state_type &x, state_type &dxdt, const double) { - dxdt[0] = - mDot; - dxdt[1] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); - dxdt[2] = -alpha/(1.0 - alpha * x[1]) * dxdt[1] + beta * delta * dxdt[0] / x[0]; - }; - - integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); - - mixingCoreMass = std::exp(x[2]); - m_CentralHeliumFraction = x[1]; - - return mixingCoreMass; -} - - -/* - * Update the minimum core mass of a main sequence star that loses mass through Case A mass transfer by - * setting it equal to the core mass of a TAMS star, scaled by the fractional age. - * - * The minimum core mass of the star is updated only if the retain-core-mass-during-caseA-mass-transfer - * option is specified, otherwise it is left unchanged. - * - * - * void UpdateMinimumCoreMass() - * - */ -void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { - if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate) // Only proceed with calculation if time advances and calculation was not executed in binary evolution - return; - switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); - break; - } - case CORE_MASS_PRESCRIPTION::NONE: { - m_MinimumCoreMass = 0.0; - break; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - if (p_TotalMassLossRate >= 0.0) { // Mass Loss - m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); - } - else { // Mass gain - m_MinimumCoreMass = m_MinimumCoreMass; - } - break; - } - } -} - - /* * Sets the mass and age of a merge product of two main sequence stars * (note: treats merger products as main-sequence stars, not CHE, and does not check for MS_lte_07) diff --git a/src/MainSequence.h b/src/MainSequence.h index b4ae29da5..9c151691f 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -19,6 +19,8 @@ class MainSequence: virtual public BaseStar { MainSequence(const BaseStar& p_BaseStar) : BaseStar(p_BaseStar) {} MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::A; } // Always case A + + const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); protected: @@ -71,6 +73,7 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables double CalculateMainSequenceCoreMassMandel(); double CalculateMainSequenceCoreMassShikauchi(); + double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 double CalculatePerturbationMu() const { return 5.0; } // mu(MS) = 5.0 (Hurley et al. 2000, eqs 97 & 98) @@ -96,7 +99,9 @@ class MainSequence: virtual public BaseStar { void EvolveOneTimestepPreamble(); STELLAR_TYPE EvolveToNextPhase() { return STELLAR_TYPE::HERTZSPRUNG_GAP; } - + + std::tuple InterpolateShikauchiCoefficients(const double p_Metallicity) const; + bool IsEndOfPhase() const { return !ShouldEvolveOnPhase(); } // Phase ends when age at or after MS timescale void PerturbLuminosityAndRadius() { } // NO-OP @@ -111,7 +116,7 @@ class MainSequence: virtual public BaseStar { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateMinimumCoreMass(); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass }; diff --git a/src/Star.h b/src/Star.h index e8762acba..f6b1b01d3 100755 --- a/src/Star.h +++ b/src/Star.h @@ -289,7 +289,7 @@ class Star { p_MassGainPerTimeStep, p_Epsilon);} - void UpdateMinimumCoreMass() { m_Star->UpdateMinimumCoreMass(); } + void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { m_Star->UpdateMinimumCoreMass(p_Dt, p_TotalMassLossRate); } void UpdatePreviousTimestepDuration() { m_Star->UpdatePreviousTimestepDuration(); } diff --git a/src/constants.h b/src/constants.h index 7b8558504..40eb70daa 100755 --- a/src/constants.h +++ b/src/constants.h @@ -3672,33 +3672,33 @@ const std::vector>> LOVERIDGE_COE }; // Coefficients for determining core mass for stars on the MS -// from Shikauchi et al. (2024), ArXiv link TBD +// from Shikauchi et al. (2024), ArXiv link https://arxiv.org/abs/2409.00460 // from Table 2 const std::vector SHIKAUCHI_ALPHA_COEFFICIENTS = { - // Solar metallicity Z_Sun - {0.45, -0.05878711, -0.84646162}, + // 0.1*Z_Sun + {0.45, -0.0557105, -0.86589929}, // 1/3*Z_Sun {0.45, -0.06968022, -0.73688164}, - // 0.1*Z_Sun - {0.45, -0.0557105, -0.86589929} + // Solar metallicity Z_Sun + {0.45, -0.05878711, -0.84646162} }; // from Table 3 const std::vector SHIKAUCHI_FMIX_COEFFICIENTS = { - // Solar metallicity Z_Sun - {0.86605495, -0.64960375, 35.57019104}, + // 0.1*Z_Sun + {0.86914766, -0.60815098, 37.20654856}, // 1/3*Z_Sun {0.86269445, -0.62623353, 35.74630996}, - // 0.1*Z_Sun - {0.86914766, -0.60815098, 37.20654856} + // Solar metallicity Z_Sun + {0.86605495, -0.64960375, 35.57019104} }; // from Table 4 const std::vector SHIKAUCHI_L_COEFFICIENTS = { - // Solar metallicity Z_Sun - {3.27883249, 1.79370338, -0.71413866, -0.77019351, -0.3898752, 0.07499563, 0.5920458, 0.33846556, -0.49649838, 1.71263853}, + // 0.1*Z_Sun + {3.2555795, 1.84666823, -0.79986388, -0.75728099, -0.38831172, 0.08223542, 0.49543834, 0.31314176, -0.36705796, 1.72200581}, // 1/3*Z_Sun {3.35622529, 1.96904931, -0.88894808, -0.81112488, -0.47925922, 0.09056925, 0.53094768, 0.33971972, -0.35581284, 1.65390003}, - // 0.1*Z_Sun - {3.2555795, 1.84666823, -0.79986388, -0.75728099, -0.38831172, 0.08223542, 0.49543834, 0.31314176, -0.36705796, 1.72200581} + // Solar metallicity Z_Sun + {3.27883249, 1.79370338, -0.71413866, -0.77019351, -0.3898752, 0.07499563, 0.5920458, 0.33846556, -0.49649838, 1.71263853} }; #endif // __constants_h__ From 57992ff71e225308968f2d79937e06833bdd4cf3 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Thu, 17 Oct 2024 15:47:53 +1100 Subject: [PATCH 12/25] Fixed stellar tracks, code clean up, added comments --- .../program-options-list-defaults.rst | 15 +- src/BaseBinaryStar.cpp | 4 +- src/BaseStar.cpp | 9 +- src/BaseStar.h | 6 +- src/GiantBranch.h | 2 - src/HG.h | 3 +- src/MS_gt_07.h | 9 +- src/MainSequence.cpp | 212 ++++++++++++------ src/MainSequence.h | 15 +- src/constants.h | 2 +- 10 files changed, 187 insertions(+), 90 deletions(-) diff --git a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst index 3b7f3385a..b256d66fb 100644 --- a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst +++ b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst @@ -796,6 +796,15 @@ Default = 4.2 :ref:`Back to Top ` +**--main-sequence-core-mass-prescription** |br| +Main sequence core mass prescription. |br| +Options: {NONE, MANDEL, SHIKAUCHI} |br| +``NONE`` : No treatment, core mass not tracked on the MS |br| +``MANDEL`` : The core following case A mass transfer is set equal to the expected core mass of a newly formed HG star + with mass equal to that of the donor, scaled by the fraction of the donor's MS lifetime at mass transfer |br| +``SHIKAUCHI`` : Core mass according to Shikauchi et al. (2024) |br| +Default = MANDEL |br| + **--mass-change-fraction** |br| Approximate desired fractional change in stellar mass on phase when setting SSE and BSE timesteps (applied before ``--timestep--multiplier``). |br| Recommended value is 0.005. |br| @@ -1157,12 +1166,6 @@ Remnant mass prescription. |br| Options: { HURLEY2000, BELCZYNSKI2002, FRYER2012, FRYER2022, MULLER2016, MULLERMANDEL, SCHNEIDER2020, SCHNEIDER2020ALT, MALTSEV2024 } |br| Default = MULLERMANDEL -**--retain-core-mass-during-caseA-mass-transfer** |br| -If TRUE, preserve a larger donor core mass following case A mass transfer. |br| -The core is set equal to the expected core mass of a newly formed HG star with mass equal to that of the donor, -scaled by the fraction of the donor's MS lifetime at mass transfer. |br| -Default = TRUE - **--revised-energy-formalism-nandez-ivanova** |br| Enable revised energy formalism of Nandez & Ivanova. |br| Default = FALSE diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index b4aec3147..b05f70372 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -2064,9 +2064,9 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { if (utils::Compare(m_MassLossRateInRLOF,donorMassLossRateNuclear) == 0) // if transferring mass on nuclear timescale, limit mass loss amount to rate * timestep (thermal timescale MT always happens in one timestep) massDiffDonor = std::min(massDiffDonor, m_MassLossRateInRLOF * m_Dt); massDiffDonor = -massDiffDonor; // set mass difference - m_Donor->UpdateTotalMassLossRate(-massDiffDonor / (p_Dt * 1.0E6)); + m_Donor->UpdateTotalMassLossRate(-massDiffDonor / (p_Dt * MYR_TO_YEAR)); m_Donor->UpdateMinimumCoreMass(p_Dt, m_Donor->TotalMassLossRate()); - m_Accretor->UpdateTotalMassLossRate((massDiffDonor * m_FractionAccreted) / (p_Dt * 1.0E6)); + m_Accretor->UpdateTotalMassLossRate((massDiffDonor * m_FractionAccreted) / (p_Dt * MYR_TO_YEAR)); m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); } } diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index c7fd950b3..f817f8313 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -2857,7 +2857,7 @@ double BaseStar::CalculateMassLossRate() { mDot = mDot * OPTIONS->OverallWindMassLossMultiplier(); // apply overall wind mass loss multiplier } - UpdateTotalMassLossRate(mDot); + UpdateTotalMassLossRate(mDot); // update mass loss rate return mDot; } @@ -4715,14 +4715,14 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { STELLAR_TYPE stellarType = m_StellarType; if (ShouldEvolveOnPhase()) { // evolve timestep on phase + UpdateMinimumCoreMass(p_DeltaTime, m_Mdot); // update core mass, relevant for MS stars + m_Tau = CalculateTauOnPhase(); m_COCoreMass = CalculateCOCoreMassOnPhase(); m_CoreMass = CalculateCoreMassOnPhase(); m_HeCoreMass = CalculateHeCoreMassOnPhase(); - - UpdateMinimumCoreMass(p_DeltaTime, m_Mdot); // update core mass, relevant for MS stars - + m_Luminosity = CalculateLuminosityOnPhase(); // Calculate abundances @@ -4773,7 +4773,6 @@ STELLAR_TYPE BaseStar::ResolveEndOfPhase(const bool p_ResolveEnvelopeLoss) { if (p_ResolveEnvelopeLoss) stellarType = ResolveEnvelopeLoss(); // if required, resolve envelope loss if it occurs if (stellarType == m_StellarType) { // staying on phase? - m_Tau = CalculateTauAtPhaseEnd(); m_COCoreMass = CalculateCOCoreMassAtPhaseEnd(); diff --git a/src/BaseStar.h b/src/BaseStar.h index 26d698789..4e9730b61 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -357,9 +357,9 @@ class BaseStar { const double p_MassGainPerTimeStep, const double p_Epsilon) { } // Default is NO-OP - virtual void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { }; // Only set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass; default is NO-OP + virtual void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // Set minimal core mass for Main Sequence stars losing mass through winds or mass transfer; default is NO-OP - virtual void UpdateTotalMassLossRate(const double p_MassLossRate) { m_TotalMassLossRate = p_MassLossRate; } + virtual void UpdateTotalMassLossRate(const double p_MassLossRate) { m_TotalMassLossRate = p_MassLossRate; } // m_TotalMassLossRate = m_Mdot in SSE, during a mass transfer episode m_TotalMassLossRate = m_MassLossRateInRLOF // printing functions bool PrintDetailedOutput(const int p_Id, const SSE_DETAILED_RECORD_TYPE p_RecordType) const { @@ -442,7 +442,7 @@ class BaseStar { double m_Tau; // Relative time double m_Temperature; // Current temperature (Tsol) double m_Time; // Current physical time the star has been evolved (Myr) - double m_TotalMassLossRate; + double m_TotalMassLossRate; // Current mass loss rate from winds or mass transfer (Msol per yr) // Previous timestep variables double m_DtPrev; // Previous timestep diff --git a/src/GiantBranch.h b/src/GiantBranch.h index c595cbbeb..2e47849b9 100755 --- a/src/GiantBranch.h +++ b/src/GiantBranch.h @@ -135,8 +135,6 @@ class GiantBranch: virtual public BaseStar, public MainSequence { void UpdateAgeAfterMassLoss() { } // NO-OP for most stellar types void UpdateInitialMass() { } // NO-OP for most stellar types - - void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // NO-OP for most stellar types }; diff --git a/src/HG.h b/src/HG.h index 423076095..1e93fc3d9 100755 --- a/src/HG.h +++ b/src/HG.h @@ -56,7 +56,7 @@ class HG: virtual public BaseStar, public GiantBranch { m_Age = m_Timescales[static_cast(TIMESCALE::tMS)]; // Set age appropriately // update effective "initial" mass (m_Mass0) so that the core mass is at least equal to the minimum core mass but no more than total mass - // (only relevant if RetainCoreMassDuringCaseAMassTransfer()) + // (only relevant if Mandel or Shikauchi main sequence core mass prescription is used) if (utils::Compare(CalculateCoreMassOnPhase(m_Mass0, m_Age), std::min(m_Mass, MinimumCoreMass())) < 0) { double desiredCoreMass = std::min(m_Mass, MinimumCoreMass()); // desired core mass m_Mass0 = Mass0ToMatchDesiredCoreMass(this, desiredCoreMass); // use root finder to find new core mass estimate @@ -145,6 +145,7 @@ class HG: virtual public BaseStar, public GiantBranch { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 void UpdateInitialMass(); // Per Hurley et al. 2000, section 7.1 + void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // NO-OP for stellar types other than MS /* diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index daed64515..8e5ad2181 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -41,7 +41,14 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { void Initialise() { CalculateTimescales(); // Initialise timescales // Age for MS_GT_07 is carried over from CH stars switching to MS after spinning down, so not set to 0.0 here - m_MinimumCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); // Initialise initial core mass + + // Initialise core mass, luminosity, radius, and temperature if Shikauchi core mass prescription is used + if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) { + m_MinimumCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); + m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MinimumCoreMass, m_InitialHeliumAbundance); + m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); + m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); + } } diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 5440aa24d..56df478f6 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -286,7 +286,13 @@ double MainSequence::CalculateLuminosityAtPhaseEnd(const double p_Mass) const { double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const { #define a m_AnCoefficients // for convenience and readability - undefined at end of function #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function - + + // If Shikauchi+ core prescription is used, return Shikauchi luminosity + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { + if (m_Age <= 0.99 * timescales(tMS)) return CalculateLuminosityShikauchi(m_MinimumCoreMass, m_HeliumAbundanceCore); + else return CalculateLuminosityShikauchiTransitionToHG(); + } + const double epsilon = 0.01; double LTMS = CalculateLuminosityAtPhaseEnd(p_Mass); @@ -314,6 +320,52 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl } +/* + * Calculate luminosity on the Main Sequence when Shikauchi et al. (2024) core mass prescription is used + * + * Shikauchi et al. 2024, eq (A5) + * + * + * double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) + * + * @param [IN] p_CoreMass Main sequence core mass in Msol + * @param [IN] p_HeliumAbundanceCore Central helium fraction + * @return Luminosity on the Main Sequence as a function of current core mass and central helium fraction + */ +double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const { + DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); + double centralHeliumFraction = p_HeliumAbundanceCore; + double logMixingCoreMass = std::log10(p_CoreMass); + double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; + return PPOW(10.0, logL); +} + + +/* + * Calculate luminosity on the transition from the Main Sequence to the HG when Shikauchi et al. (2024) core mass prescription is used + * + * Shikauchi+ prescription cannot be used beyond the MS hook (at age 0.99 * tMS), and this smoothly connects luminosity between + * the beginning of the hook and the beginning of the HG + * + * double CalculateLuminosityShikauchiTransitionToHG() + * + * @return Luminosity on the Main Sequence (for age between tHook and tMS) + */ +double MainSequence::CalculateLuminosityShikauchiTransitionToHG() const { + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime + double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts + + HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); + double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) + delete clone; clone = nullptr; // Return the memory allocated for the clone + + double luminosityAtShikauchiPhaseEnd = CalculateLuminosityShikauchi(m_MinimumCoreMass, 1.0 - m_Metallicity); + // Linear interpolation + double luminosity = (luminosityAtShikauchiPhaseEnd * (tMS - m_Age) + luminosityTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); + return luminosity; +} + + /////////////////////////////////////////////////////////////////////////////////////// // // // RADIUS CALCULATIONS // @@ -481,7 +533,11 @@ double MainSequence::CalculateRadiusAtPhaseEnd(const double p_Mass, const double double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_Time, const double p_RZAMS) const { #define a m_AnCoefficients // for convenience and readability - undefined at end of function #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function - + + // If Shikauchi+ core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_Age >= 0.99 * timescales(tMS))) + return CalculateRadiusShikauchiTransitionToHG(); + const double epsilon = 0.01; double RTMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); @@ -567,6 +623,27 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double } +/* + * Calculate radius on the transition from the Main Sequence to the HG when Shikauchi et al. (2024) core mass prescription is used + * + * Shikauchi+ prescription cannot be used beyond the MS hook (at age 0.99 * tMS), and this smoothly connects the radius between + * the beginning of the hook and the beginning of the HG + * + * double CalculateRadiusShikauchiTransitionToHG() + * + * @return Radius on the Main Sequence (for age between tHook and tMS) + */ +double MainSequence::CalculateRadiusShikauchiTransitionToHG() const { + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime + double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts + + double radiusTAMS = CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); // Radius at TAMS + double tauAtShikauchiPhaseEnd = ageAtShikauchiPhaseEnd / tMS; // Tau when the MS hook starts + double radiusAtShikauchiPhaseEnd = CalculateRadiusOnPhaseTau(m_Mass, tauAtShikauchiPhaseEnd); // Radius when the MS hook starts + // Linear interpolation + double radius = (radiusAtShikauchiPhaseEnd * (tMS - m_Age) + radiusTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); + return radius; +} /* @@ -703,7 +780,7 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); double TAMSCoreMass = clone->CoreMass(); // get core mass from clone delete clone; clone = nullptr; // return the memory allocated for the clone - double minimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); // update minimum core mass + double minimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); return minimumCoreMass; } @@ -712,11 +789,13 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { * Calculate the convective core mass of a main sequence star that loses mass either through winds or * Case A mass transfer according to Shikauchi et al. (2024) * + * This function also calculates and updates the core helium abundance + * * double CalculateMainSequenceCoreMassShikauchi() * - * @return Mass of the convective core in Msol + * @return Tuple containing the mass of the convective core in Msol and core helium abundance */ -double MainSequence::CalculateMainSequenceCoreMassShikauchi() { +DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); @@ -725,27 +804,26 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi() { double centralHeliumFraction = m_HeliumAbundanceCore; double mixingCoreMass = m_MinimumCoreMass; double lnMixingCoreMass = std::log(mixingCoreMass); - double logMixingCoreMass = std::log10(mixingCoreMass); - // Equation (A3) + // Eq (A3) double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(- m_MZAMS / FMIX_COEFFICIENTS[2]); - // Equation (A4) + // Eq (A4) double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(- totalMass / FMIX_COEFFICIENTS[2]); - // Equation (A5) - double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; - // Equation (A2) + // Eq (A5) + double logL = std::log10(CalculateLuminosityShikauchi(mixingCoreMass, centralHeliumFraction)); + // Eq (A2) double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * mixingCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; - // Equation (A7) + // Eq (A7) double g = -0.0044 * m_MZAMS + 0.27; - // Equation (10) + // Eq (10) double Yhat = (centralHeliumFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity); - // Equation (A6) + // Eq (A6) double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); - double currentTimestepInYrs = m_Dt * 1.0E6; + double currentTimestepInYrs = m_Dt * MYR_TO_YEAR; double mDot = m_TotalMassLossRate; - // Use boost adaptive ODE solver for speed and accuracy + // Use boost adaptive ODE solver controlled_stepper_type controlled_stepper; state_type x(3); x[0] = totalMass; @@ -761,8 +839,8 @@ double MainSequence::CalculateMainSequenceCoreMassShikauchi() { integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); mixingCoreMass = std::exp(x[2]); // New mixing core mass - m_HeliumAbundanceCore = x[1]; // Update m_HeliumAbundanceCore - return mixingCoreMass; + centralHeliumFraction = x[1]; // New central helium fraction + return std::tuple (mixingCoreMass, centralHeliumFraction); } @@ -792,7 +870,7 @@ double MainSequence::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { /* - * Linear interpolation/extrapolation for coefficients from Shikauchi et al. (2024) + * Linear interpolation/extrapolation for coefficients from Shikauchi et al. (2024), used for core mass calculations * * std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) * @@ -800,38 +878,44 @@ double MainSequence::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { * @return Tuple containing vectors of coefficients for the specified metallicity */ std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) const { + double logZ = std::log10(p_Metallicity); + DBL_VECTOR alphaCoeff(3, 0.0); + DBL_VECTOR fmixCoeff(3, 0.0); + DBL_VECTOR lCoeff(10, 0.0); + + // Skip calculation if Shikauchi+ core prescription is not used + if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::SHIKAUCHI) + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + // Coefficients are given for these metallicities - double low = 0.1 * ZSOL_ASPLUND; - double middle = 1.0/3.0 * ZSOL_ASPLUND; - double high = ZSOL_ASPLUND; + double low = std::log10(0.1 * ZSOL_ASPLUND); + double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); + double high = std::log10(ZSOL_ASPLUND); - if (p_Metallicity <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound + if (logZ <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); - else if ((p_Metallicity > low) && (p_Metallicity <= middle)) { // Linear interpolation between metallicity low and middle - DBL_VECTOR alphaCoeff(3, 0.0); - DBL_VECTOR fmixCoeff(3, 0.0); - DBL_VECTOR lCoeff(10, 0.0); + + else if ((logZ > low) && (logZ <= middle)) { // Linear interpolation between metallicity low and middle for (int i = 0; i < 3; i++) - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - p_Metallicity) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (p_Metallicity - low)) / (middle - low); + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - p_Metallicity) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (p_Metallicity - low)) / (middle - low); + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - p_Metallicity) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (p_Metallicity - low)) / (middle - low); + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } - else if ((p_Metallicity > middle) && (p_Metallicity <= high)) { // Linear interpolation between metallicity middle and high - DBL_VECTOR alphaCoeff(3, 0.0); - DBL_VECTOR fmixCoeff(3, 0.0); - DBL_VECTOR lCoeff(10, 0.0); + + else if ((logZ > middle) && (logZ < high)) { // Linear interpolation between metallicity middle and high for (int i = 0; i < 3; i++) - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - p_Metallicity) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (p_Metallicity - middle)) / (high - middle); + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - p_Metallicity) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (p_Metallicity - middle)) / (high - middle); + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - p_Metallicity) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (p_Metallicity - middle)) / (high - middle); + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } - else // Linear extrapolation (constant) for metallicity higher than solar + + else // Linear extrapolation (constant) for metallicity equal to solar or higher return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); } @@ -845,8 +929,8 @@ std::tuple MainSequence::InterpolateShikauc * @param [IN] p_TotalMassLossRate Mass loss rate either from stellar winds or mass transfer */ void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { - // Only proceed with updating the core mass if time advances, calculation was not executed in binary evolution, and MZAMS >= 15 Msol - if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate || m_MZAMS < 15.0) + // Only proceed with updating the core mass if time advances and calculation was not executed in binary evolution (total mass loss rate had been updated) + if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate) return; switch (OPTIONS->MainSequenceCoreMassPrescription()) { @@ -854,15 +938,33 @@ void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_Total m_MinimumCoreMass = 0.0; break; } - case CORE_MASS_PRESCRIPTION::MANDEL: { - m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); + case CORE_MASS_PRESCRIPTION::MANDEL: { // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + if (p_TotalMassLossRate != m_Mdot) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE + m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); break; } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - if (p_TotalMassLossRate >= 0.0) // Mass Loss - m_MinimumCoreMass = CalculateMainSequenceCoreMassShikauchi(); - else // Mass gain - m_MinimumCoreMass = CalculateMixingCoreMassAtZAMS(m_Mass); + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi+ prescription + if ((p_TotalMassLossRate >= 0.0) && (m_MZAMS >= 10.0)) { // Mass loss and MZAMS >= 10 Msol (Shikauchi+ prescription is valid) + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + std::tuple ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(); + double mixingCoreMass = std::get<0>(ShikauchiSolution); + double centralHeliumFraction = std::get<1>(ShikauchiSolution); + m_MinimumCoreMass = mixingCoreMass; // Update the core mass + m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance + if (m_Age <= 0.99 * tMS) + m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age + } + else if ((p_TotalMassLossRate >= 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer (not applied to SSE)) + m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); + } + else if ((p_TotalMassLossRate < 0.0) && (m_MZAMS >= 10.0)) { // Mass gain and MZAMS >= 10 Msol + double massPostMT = m_Mass + p_TotalMassLossRate * p_Dt * MYR_TO_YEAR; + double tBGB = CalculateLifetimeToBGB(massPostMT); + double tMS = CalculateLifetimeOnPhase(massPostMT, tBGB); + double fRej = 1.0; // Rejuvenation factor + m_HeliumAbundanceCore = m_Tau / tMS * m_InitialHydrogenAbundance * fRej + m_InitialHeliumAbundance; // New central helium fraction, we assume that core mass does not change + } + else { }; // Mass gain and MZAMS < 10 Msol break; } } @@ -941,22 +1043,6 @@ void MainSequence::UpdateAgeAfterMassLoss() { m_Age *= tMSprime / tMS; } - - -/* - * Determine whether star should continue to evolve on phase - * - * - * bool ShouldEvolveOnPhase() - * - * @return true if evolution should continue on phase, false otherwise - */ -bool MainSequence::ShouldEvolveOnPhase() const { - if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) - return ((m_HeliumAbundanceCore <= 1.0 - m_Metallicity) && (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)])); - else - return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); -} /////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/MainSequence.h b/src/MainSequence.h index 9c151691f..f2ce994ad 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -20,7 +20,7 @@ class MainSequence: virtual public BaseStar { MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::A; } // Always case A - const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); + const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); // Interpolate the Shikauchi coefficients for the given metallicity; protected: @@ -45,7 +45,7 @@ class MainSequence: virtual public BaseStar { double CalculateCOCoreMassAtPhaseEnd() const { return CalculateCOCoreMassOnPhase(); } // Same as on phase double CalculateCOCoreMassOnPhase() const { return 0.0; } // McCO(MS) = 0.0 - double CalculateCoreMassAtPhaseEnd() const { return MinimumCoreMass(); } // Accounts for minimal core mass built up prior to mass loss through mass transfer + double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass @@ -71,8 +71,10 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityAtPhaseEnd() const { return CalculateLuminosityAtPhaseEnd(m_Mass0); } // Use class member variables double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables + double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const; + double CalculateLuminosityShikauchiTransitionToHG() const; double CalculateMainSequenceCoreMassMandel(); - double CalculateMainSequenceCoreMassShikauchi(); + DBL_DBL CalculateMainSequenceCoreMassShikauchi(); double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 @@ -86,7 +88,8 @@ class MainSequence: virtual public BaseStar { double CalculateRadiusAtPhaseEnd(const double p_Mass, const double p_RZAMS) const; double CalculateRadiusAtPhaseEnd() const { return CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); } // Use class member variables double CalculateRadiusOnPhase() const { return CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); } // Use class member variables - + double CalculateRadiusShikauchiTransitionToHG() const; + double CalculateTauAtPhaseEnd() const { return 1.0; } // tau = 1.0 at end of MS double CalculateTauOnPhase() const; @@ -108,7 +111,7 @@ class MainSequence: virtual public BaseStar { STELLAR_TYPE ResolveEnvelopeLoss(bool p_Force = false); - bool ShouldEvolveOnPhase() const; // Evolve on MS phase if age in MS timescale + bool ShouldEvolveOnPhase() const { return (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]); } // Evolve on MS phase if age in MS timescale void UpdateInitialMass() { m_Mass0 = m_Mass; } // Per Hurley et al. 2000, section 7.1 @@ -116,7 +119,7 @@ class MainSequence: virtual public BaseStar { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate); // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate); }; diff --git a/src/constants.h b/src/constants.h index 40eb70daa..25f6b8b66 100755 --- a/src/constants.h +++ b/src/constants.h @@ -3672,7 +3672,7 @@ const std::vector>> LOVERIDGE_COE }; // Coefficients for determining core mass for stars on the MS -// from Shikauchi et al. (2024), ArXiv link https://arxiv.org/abs/2409.00460 +// from Shikauchi et al. (2024), https://arxiv.org/abs/2409.00460 // from Table 2 const std::vector SHIKAUCHI_ALPHA_COEFFICIENTS = { // 0.1*Z_Sun From b8fd4925c9a3c8fbdfce82a3f2d9a2de37d7ecd5 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 1 Nov 2024 13:19:52 +1100 Subject: [PATCH 13/25] Rejuvenation implementation --- src/BaseBinaryStar.cpp | 10 ++- src/BaseStar.cpp | 4 +- src/MS_gt_07.h | 5 +- src/MainSequence.cpp | 176 ++++++++++++++++++++++++++--------------- src/MainSequence.h | 13 +-- 5 files changed, 133 insertions(+), 75 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index b05f70372..da9fd352f 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -2052,6 +2052,8 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { if (utils::Compare(m_Donor->CoreMass(), 0) > 0 && utils::Compare(envMassDonor, 0) > 0) { // donor has a core and an envelope? massDiffDonor = -envMassDonor; // yes - set donor mass loss to (negative of) the envelope mass isEnvelopeRemoved = true; + m_Accretor->UpdateTotalMassLossRate((-massDiffDonor * m_FractionAccreted) / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS accretor + m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); // update core mass for MS accretor } else { // donor has no envelope massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, m_FractionAccreted); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe @@ -2064,10 +2066,10 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { if (utils::Compare(m_MassLossRateInRLOF,donorMassLossRateNuclear) == 0) // if transferring mass on nuclear timescale, limit mass loss amount to rate * timestep (thermal timescale MT always happens in one timestep) massDiffDonor = std::min(massDiffDonor, m_MassLossRateInRLOF * m_Dt); massDiffDonor = -massDiffDonor; // set mass difference - m_Donor->UpdateTotalMassLossRate(-massDiffDonor / (p_Dt * MYR_TO_YEAR)); - m_Donor->UpdateMinimumCoreMass(p_Dt, m_Donor->TotalMassLossRate()); - m_Accretor->UpdateTotalMassLossRate((massDiffDonor * m_FractionAccreted) / (p_Dt * MYR_TO_YEAR)); - m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); + m_Donor->UpdateTotalMassLossRate(massDiffDonor / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS donor + m_Donor->UpdateMinimumCoreMass(p_Dt, m_Donor->TotalMassLossRate()); // update core mass for MS donor + m_Accretor->UpdateTotalMassLossRate((-massDiffDonor * m_FractionAccreted) / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS accretor + m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); // update core mass for MS accretor } } diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index f817f8313..2460e5b25 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -2857,7 +2857,7 @@ double BaseStar::CalculateMassLossRate() { mDot = mDot * OPTIONS->OverallWindMassLossMultiplier(); // apply overall wind mass loss multiplier } - UpdateTotalMassLossRate(mDot); // update mass loss rate + UpdateTotalMassLossRate(-mDot); // update mass loss rate return mDot; } @@ -4715,7 +4715,7 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { STELLAR_TYPE stellarType = m_StellarType; if (ShouldEvolveOnPhase()) { // evolve timestep on phase - UpdateMinimumCoreMass(p_DeltaTime, m_Mdot); // update core mass, relevant for MS stars + UpdateMinimumCoreMass(p_DeltaTime, -m_Mdot); // update core mass, relevant for MS stars m_Tau = CalculateTauOnPhase(); diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index 8e5ad2181..c4d676112 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -43,8 +43,9 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { // Age for MS_GT_07 is carried over from CH stars switching to MS after spinning down, so not set to 0.0 here // Initialise core mass, luminosity, radius, and temperature if Shikauchi core mass prescription is used - if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) { - m_MinimumCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { + m_InitialMixingCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); + m_MinimumCoreMass = m_InitialMixingCoreMass; m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MinimumCoreMass, m_InitialHeliumAbundance); m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 56df478f6..d5cdbf40f 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -29,7 +29,7 @@ * @return Helium abundance in the core (Y_c) */ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const { - if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) return m_HeliumAbundanceCore; double heliumAbundanceCoreMax = 1.0 - m_Metallicity; return ((heliumAbundanceCoreMax - m_InitialHeliumAbundance) * p_Tau) + m_InitialHeliumAbundance; @@ -51,7 +51,7 @@ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) con * @return Hydrogen abundance in the core (X_c) */ double MainSequence::CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) const { - if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) return 1.0 - m_HeliumAbundanceCore - m_Metallicity; return m_InitialHydrogenAbundance * (1.0 - p_Tau); } @@ -352,16 +352,21 @@ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const * @return Luminosity on the Main Sequence (for age between tHook and tMS) */ double MainSequence::CalculateLuminosityShikauchiTransitionToHG() const { - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime - double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts - - HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); - double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) - delete clone; clone = nullptr; // Return the memory allocated for the clone - - double luminosityAtShikauchiPhaseEnd = CalculateLuminosityShikauchi(m_MinimumCoreMass, 1.0 - m_Metallicity); - // Linear interpolation - double luminosity = (luminosityAtShikauchiPhaseEnd * (tMS - m_Age) + luminosityTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); + double luminosity; + if (m_TotalMassLossRate != -m_Mdot) // Ongoing mass transfer episode + luminosity = CalculateLuminosityShikauchi(m_MinimumCoreMass, m_HeliumAbundanceCore); + else { + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime + double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts + + HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); + double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) + delete clone; clone = nullptr; // Return the memory allocated for the clone + + double luminosityAtShikauchiPhaseEnd = CalculateLuminosityShikauchi(m_MinimumCoreMass, 1.0 - m_Metallicity); + // Linear interpolation + luminosity = (luminosityAtShikauchiPhaseEnd * (tMS - m_Age) + luminosityTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); + } return luminosity; } @@ -535,7 +540,7 @@ double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_ #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function // If Shikauchi+ core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_Age >= 0.99 * timescales(tMS))) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0) && (m_Age >= 0.99 * timescales(tMS)) && (m_TotalMassLossRate == -m_Mdot)) return CalculateRadiusShikauchiTransitionToHG(); const double epsilon = 0.01; @@ -634,12 +639,12 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double * @return Radius on the Main Sequence (for age between tHook and tMS) */ double MainSequence::CalculateRadiusShikauchiTransitionToHG() const { - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts - double radiusTAMS = CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); // Radius at TAMS + double radiusTAMS = CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); // Radius at TAMS double tauAtShikauchiPhaseEnd = ageAtShikauchiPhaseEnd / tMS; // Tau when the MS hook starts - double radiusAtShikauchiPhaseEnd = CalculateRadiusOnPhaseTau(m_Mass, tauAtShikauchiPhaseEnd); // Radius when the MS hook starts + double radiusAtShikauchiPhaseEnd = CalculateRadiusOnPhaseTau(m_Mass, tauAtShikauchiPhaseEnd); // Radius when the MS hook starts // Linear interpolation double radius = (radiusAtShikauchiPhaseEnd * (tMS - m_Age) + radiusTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); return radius; @@ -804,11 +809,12 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { double centralHeliumFraction = m_HeliumAbundanceCore; double mixingCoreMass = m_MinimumCoreMass; double lnMixingCoreMass = std::log(mixingCoreMass); + double heliumFractionOut = m_HeliumAbundanceCoreOut; // Eq (A3) double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(- m_MZAMS / FMIX_COEFFICIENTS[2]); // Eq (A4) - double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(- totalMass / FMIX_COEFFICIENTS[2]); + double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-totalMass / FMIX_COEFFICIENTS[2]); // Eq (A5) double logL = std::log10(CalculateLuminosityShikauchi(mixingCoreMass, centralHeliumFraction)); // Eq (A2) @@ -825,22 +831,49 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { // Use boost adaptive ODE solver controlled_stepper_type controlled_stepper; - state_type x(3); - x[0] = totalMass; - x[1] = centralHeliumFraction; - x[2] = lnMixingCoreMass; - + state_type x(2); + x[0] = centralHeliumFraction; + x[1] = lnMixingCoreMass; auto ode = [&](const state_type &x, state_type &dxdt, const double) { - dxdt[0] = - mDot; - dxdt[1] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); - dxdt[2] = -alpha/(1.0 - alpha * x[1]) * dxdt[1] + beta * delta * dxdt[0] / x[0]; + dxdt[0] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); + dxdt[1] = - alpha / (1.0 - alpha * centralHeliumFraction) * dxdt[0] + beta * delta * mDot / totalMass; }; - integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); - mixingCoreMass = std::exp(x[2]); // New mixing core mass - centralHeliumFraction = x[1]; // New central helium fraction - return std::tuple (mixingCoreMass, centralHeliumFraction); + double newMixingCoreMass = std::exp(x[1]); // New mixing core mass + double newCentralHeliumFraction = x[0]; // New central helium fraction + double deltaCoreMass = newMixingCoreMass - mixingCoreMass; // Difference in core mass + + if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation + if (mixingCoreMass < m_InitialMixingCoreMass) { // New core mass less than initial core mass? + // Use boost adaptive ODE solver + state_type x(1); + x[0] = heliumFractionOut; + auto ode = [&](const state_type &x, state_type &dxdt, const double) { + dxdt[0] = (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * (deltaCoreMass / currentTimestepInYrs); + }; + integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); + + double deltaY = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * deltaCoreMass + 1 / (mixingCoreMass + deltaCoreMass) * 0.5 * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; + newCentralHeliumFraction = centralHeliumFraction + deltaY; + m_HeliumAbundanceCoreOut = x[0]; // Update the helium abundance just outside the core + } + else { // New core mass greater than initial core mass? + // Use boost adaptive ODE solver + state_type x(1); + x[0] = centralHeliumFraction; + auto ode = [&](const state_type &x, state_type &dxdt, const double) { + dxdt[0] = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * (deltaCoreMass / currentTimestepInYrs); + }; + integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); + + m_InitialMixingCoreMass = newMixingCoreMass; // Update initial core mass, we assume that helium abundance just outside the core does not change + } + } + else + m_HeliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c + + return std::tuple (newMixingCoreMass, newCentralHeliumFraction); } @@ -853,19 +886,9 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { * @return Mass of the convective core in Msol at ZAMS */ double MainSequence::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { - switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::MANDEL: { - return 0.0; - } - case CORE_MASS_PRESCRIPTION::NONE: { - return 0.0; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { - DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); - double fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_MZAMS / fmixCoefficients[2]); - return fmix * p_MZAMS; - } - } + DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); + double fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_MZAMS / fmixCoefficients[2]); + return fmix * p_MZAMS; } @@ -930,41 +953,48 @@ std::tuple MainSequence::InterpolateShikauc */ void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { // Only proceed with updating the core mass if time advances and calculation was not executed in binary evolution (total mass loss rate had been updated) - if (p_Dt == 0.0 || p_TotalMassLossRate != m_TotalMassLossRate) - return; - + switch (OPTIONS->MainSequenceCoreMassPrescription()) { case CORE_MASS_PRESCRIPTION::NONE: { m_MinimumCoreMass = 0.0; break; } case CORE_MASS_PRESCRIPTION::MANDEL: { // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass - if (p_TotalMassLossRate != m_Mdot) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE + if ((p_Dt != 0.0) && (p_TotalMassLossRate != -m_Mdot)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); break; } case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi+ prescription - if ((p_TotalMassLossRate >= 0.0) && (m_MZAMS >= 10.0)) { // Mass loss and MZAMS >= 10 Msol (Shikauchi+ prescription is valid) - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - std::tuple ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(); - double mixingCoreMass = std::get<0>(ShikauchiSolution); - double centralHeliumFraction = std::get<1>(ShikauchiSolution); + if ((p_Dt == 0.0) || (p_TotalMassLossRate != m_TotalMassLossRate)) + return; + double mixingCoreMass; + double centralHeliumFraction; + std::tuple ShikauchiSolution; + + if ((p_TotalMassLossRate <= 0.0) && (m_MZAMS >= 10.0)) { // Mass loss and MZAMS >= 10 Msol (Shikauchi+ prescription is valid) + ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(); + mixingCoreMass = std::get<0>(ShikauchiSolution); + centralHeliumFraction = std::get<1>(ShikauchiSolution); m_MinimumCoreMass = mixingCoreMass; // Update the core mass m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance - if (m_Age <= 0.99 * tMS) - m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age + + double massPostMT = m_Mass + p_TotalMassLossRate * p_Dt * MYR_TO_YEAR; // Total mass after mass loss + UpdateAgeBasedOnCentralHelium(massPostMT, m_HeliumAbundanceCore); // Update the effective age } - else if ((p_TotalMassLossRate >= 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer (not applied to SSE)) + else if ((p_TotalMassLossRate <= 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer (does not take into account mass loss from winds)) m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); } - else if ((p_TotalMassLossRate < 0.0) && (m_MZAMS >= 10.0)) { // Mass gain and MZAMS >= 10 Msol - double massPostMT = m_Mass + p_TotalMassLossRate * p_Dt * MYR_TO_YEAR; - double tBGB = CalculateLifetimeToBGB(massPostMT); - double tMS = CalculateLifetimeOnPhase(massPostMT, tBGB); - double fRej = 1.0; // Rejuvenation factor - m_HeliumAbundanceCore = m_Tau / tMS * m_InitialHydrogenAbundance * fRej + m_InitialHeliumAbundance; // New central helium fraction, we assume that core mass does not change + else if ((p_TotalMassLossRate > 0.0) && (m_MZAMS >= 10.0)) { // Mass gain and MZAMS >= 10 Msol + ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(); + mixingCoreMass = std::get<0>(ShikauchiSolution); + centralHeliumFraction = std::get<1>(ShikauchiSolution); + m_MinimumCoreMass = mixingCoreMass; // Update the core mass + m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance (previous core helium abundance is updated in the function itself) + + double massPostMT = m_Mass + p_TotalMassLossRate * p_Dt * MYR_TO_YEAR; // Total mass after mass gain + UpdateAgeBasedOnCentralHelium(massPostMT, m_HeliumAbundanceCore); // Update the effective age } - else { }; // Mass gain and MZAMS < 10 Msol + else { }; // Mass gain and MZAMS < 10 Msol break; } } @@ -1040,8 +1070,30 @@ void MainSequence::UpdateAgeAfterMassLoss() { double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; double tBGBprime = CalculateLifetimeToBGB(m_Mass); double tMSprime = MainSequence::CalculateLifetimeOnPhase(m_Mass, tBGBprime); + + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0) && (m_Age < 0.99 * tMSprime)) + return; + else + m_Age *= tMSprime / tMS; +} - m_Age *= tMSprime / tMS; + +/* + * Update the effective age based on the central helium fraction (only valid for 99% of the main sequence lifetime, before + * the hook begins + * + * Used in Shikauchi+ 2024 core mass calculations + * + * void UpdateAgeBasedOnCentralHelium(const double p_Mass, const double p_HeliumAbundanceCore) + * + * @param [IN] p_Mass Mass in Msol + * @param [IN] p_HeliumAbundanceCore Helium abundance in the core + */ +void MainSequence::UpdateAgeBasedOnCentralHelium(const double p_Mass, const double p_HeliumAbundanceCore) { + double tBGB = CalculateLifetimeToBGB(p_Mass); + double tMS = CalculateLifetimeOnPhase(p_Mass, tBGB); + double age = (p_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; + if (age < 0.99 * tMS) m_Age = age; } diff --git a/src/MainSequence.h b/src/MainSequence.h index f2ce994ad..e2079e86f 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -20,10 +20,12 @@ class MainSequence: virtual public BaseStar { MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::A; } // Always case A - const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); // Interpolate the Shikauchi coefficients for the given metallicity; + const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); // Interpolate Shikauchi coefficients for the given metallicity; protected: - + + double m_InitialMixingCoreMass = 0.0; + double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Previous helium core mass abundance, needed for Shikauchi+ core mass calculation (in case of mass gain) // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; @@ -45,11 +47,11 @@ class MainSequence: virtual public BaseStar { double CalculateCOCoreMassAtPhaseEnd() const { return CalculateCOCoreMassOnPhase(); } // Same as on phase double CalculateCOCoreMassOnPhase() const { return 0.0; } // McCO(MS) = 0.0 - double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer + double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass - double CalculateHeCoreMassOnPhase() const { return 0.0; } // McHe(MS) = 0.0 + double CalculateHeCoreMassOnPhase() const { return m_MinimumCoreMass; } // McHe(MS) = 0.0 double CalculateHeliumAbundanceCoreAtPhaseEnd() const { return CalculateHeliumAbundanceCoreOnPhase(); } double CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const; @@ -118,7 +120,8 @@ class MainSequence: virtual public BaseStar { void UpdateAfterMerger(double p_Mass, double p_HydrogenMass); void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - + void UpdateAgeBasedOnCentralHelium(const double p_Mass, const double p_HeliumAbundanceCore); + void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate); }; From 762323ad359e72309a31b9fc11aedd56de819aef Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Tue, 5 Nov 2024 14:22:13 +1100 Subject: [PATCH 14/25] Rejuvenation fix and code clean up --- src/BaseBinaryStar.cpp | 9 ++- src/BaseStar.cpp | 4 +- src/BaseStar.h | 6 +- src/CH.h | 2 +- src/HG.h | 6 +- src/MS_gt_07.h | 4 +- src/MainSequence.cpp | 151 ++++++++++++++++------------------------- src/MainSequence.h | 11 ++- src/Options.cpp | 2 +- src/Options.h | 4 +- src/Star.h | 2 +- 11 files changed, 81 insertions(+), 120 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index da9fd352f..22c42b245 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -2052,8 +2052,6 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { if (utils::Compare(m_Donor->CoreMass(), 0) > 0 && utils::Compare(envMassDonor, 0) > 0) { // donor has a core and an envelope? massDiffDonor = -envMassDonor; // yes - set donor mass loss to (negative of) the envelope mass isEnvelopeRemoved = true; - m_Accretor->UpdateTotalMassLossRate((-massDiffDonor * m_FractionAccreted) / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS accretor - m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); // update core mass for MS accretor } else { // donor has no envelope massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, m_FractionAccreted); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe @@ -2067,9 +2065,7 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { massDiffDonor = std::min(massDiffDonor, m_MassLossRateInRLOF * m_Dt); massDiffDonor = -massDiffDonor; // set mass difference m_Donor->UpdateTotalMassLossRate(massDiffDonor / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS donor - m_Donor->UpdateMinimumCoreMass(p_Dt, m_Donor->TotalMassLossRate()); // update core mass for MS donor - m_Accretor->UpdateTotalMassLossRate((-massDiffDonor * m_FractionAccreted) / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS accretor - m_Accretor->UpdateMinimumCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); // update core mass for MS accretor + m_Donor->UpdateMainSequenceCoreMass(p_Dt, m_Donor->TotalMassLossRate()); // update core mass for MS donor } } @@ -2077,6 +2073,9 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { // no double massGainAccretor = -massDiffDonor * m_FractionAccreted; // set accretor mass gain to mass loss * conservativeness + m_Accretor->UpdateTotalMassLossRate(massGainAccretor / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS accretor + m_Accretor->UpdateMainSequenceCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); // update core mass for MS accretor + m_Donor->SetMassTransferDiffAndResolveWDShellChange(massDiffDonor); // set new mass of donor m_Accretor->SetMassTransferDiffAndResolveWDShellChange(massGainAccretor); // set new mass of accretor diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 2460e5b25..fbde7d6d8 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -135,9 +135,9 @@ BaseStar::BaseStar(const unsigned long int p_RandomSeed, m_Dt = DEFAULT_INITIAL_DOUBLE_VALUE; m_Tau = DEFAULT_INITIAL_DOUBLE_VALUE; m_Age = 0.0; // ensure age = 0.0 at construction (rather than default initial value) + m_MainSequenceCoreMass = DEFAULT_INITIAL_DOUBLE_VALUE; m_Mass = m_MZAMS; m_Mass0 = m_MZAMS; - m_MinimumCoreMass = DEFAULT_INITIAL_DOUBLE_VALUE; m_Luminosity = m_LZAMS; m_Radius = m_RZAMS; m_Temperature = m_TZAMS; @@ -4715,7 +4715,7 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { STELLAR_TYPE stellarType = m_StellarType; if (ShouldEvolveOnPhase()) { // evolve timestep on phase - UpdateMinimumCoreMass(p_DeltaTime, -m_Mdot); // update core mass, relevant for MS stars + UpdateMainSequenceCoreMass(p_DeltaTime, -m_Mdot); // update core mass, relevant for MS stars m_Tau = CalculateTauOnPhase(); diff --git a/src/BaseStar.h b/src/BaseStar.h index 4e9730b61..d01a1385e 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -137,13 +137,13 @@ class BaseStar { double LogMetallicitySigma() const { return m_Log10Metallicity; } // sigma in Hurley+ 2000 double LogMetallicityXi() const { return m_Log10Metallicity - LOG10_ZSOL; } // xi in Hurley+ 2000 double Luminosity() const { return m_Luminosity; } + double MainSequenceCoreMass() const { return m_MainSequenceCoreMass; } double Mass() const { return m_Mass; } double Mass0() const { return m_Mass0; } double MassPrev() const { return m_MassPrev; } ST_VECTOR MassTransferDonorHistory() const { return m_MassTransferDonorHistory; } double Mdot() const { return m_Mdot; } double Metallicity() const { return m_Metallicity; } - double MinimumCoreMass() const { return m_MinimumCoreMass; } double MZAMS() const { return m_MZAMS; } double Omega() const { return m_Omega; } double OmegaCHE() const { return m_OmegaCHE; } @@ -357,7 +357,7 @@ class BaseStar { const double p_MassGainPerTimeStep, const double p_Epsilon) { } // Default is NO-OP - virtual void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // Set minimal core mass for Main Sequence stars losing mass through winds or mass transfer; default is NO-OP + virtual void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // Set minimal core mass for Main Sequence stars losing mass through winds or mass transfer; default is NO-OP virtual void UpdateTotalMassLossRate(const double p_MassLossRate) { m_TotalMassLossRate = p_MassLossRate; } // m_TotalMassLossRate = m_Mdot in SSE, during a mass transfer episode m_TotalMassLossRate = m_MassLossRateInRLOF @@ -429,9 +429,9 @@ class BaseStar { double m_HydrogenAbundanceSurface; // Hydrogen abundance at the surface bool m_LBVphaseFlag; // Flag to know if the star satisfied the conditions, at any point in its evolution, to be considered a Luminous Blue Variable (LBV) double m_Luminosity; // Current luminosity (Lsol) + double m_MainSequenceCoreMass; // Core mass of main sequence stars (Msol) double m_Mass; // Current mass (Msol) double m_Mass0; // Current effective initial mass (Msol) - double m_MinimumCoreMass; // Minimum core mass at end of main sequence (MS stars have no core in the Hurley prescription) double m_MinimumLuminosityOnPhase; // Only required for CHeB stars, but only needs to be calculated once per star double m_Mdot; // Current mass loss rate in winds (Msol per yr) MASS_LOSS_TYPE m_DominantMassLossRate; // Current dominant type of wind mass loss diff --git a/src/CH.h b/src/CH.h index 4ec6b6b2f..4ce7a75e4 100755 --- a/src/CH.h +++ b/src/CH.h @@ -90,7 +90,7 @@ class CH: virtual public BaseStar, public MS_gt_07 { void UpdateAgeAfterMassLoss(); - void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } + void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } }; diff --git a/src/HG.h b/src/HG.h index 1e93fc3d9..5a5049645 100755 --- a/src/HG.h +++ b/src/HG.h @@ -57,8 +57,8 @@ class HG: virtual public BaseStar, public GiantBranch { // update effective "initial" mass (m_Mass0) so that the core mass is at least equal to the minimum core mass but no more than total mass // (only relevant if Mandel or Shikauchi main sequence core mass prescription is used) - if (utils::Compare(CalculateCoreMassOnPhase(m_Mass0, m_Age), std::min(m_Mass, MinimumCoreMass())) < 0) { - double desiredCoreMass = std::min(m_Mass, MinimumCoreMass()); // desired core mass + if (utils::Compare(CalculateCoreMassOnPhase(m_Mass0, m_Age), std::min(m_Mass, MainSequenceCoreMass())) < 0) { + double desiredCoreMass = std::min(m_Mass, MainSequenceCoreMass()); // desired core mass m_Mass0 = Mass0ToMatchDesiredCoreMass(this, desiredCoreMass); // use root finder to find new core mass estimate if (m_Mass0 <= 0.0) { // no root found - no solution for estimated core mass m_Mass0 = m_Mass; // if no root found we keep m_Mass0 equal to the total mass @@ -145,7 +145,7 @@ class HG: virtual public BaseStar, public GiantBranch { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 void UpdateInitialMass(); // Per Hurley et al. 2000, section 7.1 - void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // NO-OP for stellar types other than MS + void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // NO-OP for stellar types other than MS /* diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index c4d676112..1f4250503 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -45,8 +45,8 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { // Initialise core mass, luminosity, radius, and temperature if Shikauchi core mass prescription is used if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { m_InitialMixingCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); - m_MinimumCoreMass = m_InitialMixingCoreMass; - m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MinimumCoreMass, m_InitialHeliumAbundance); + m_MainSequenceCoreMass = m_InitialMixingCoreMass; + m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_InitialHeliumAbundance); m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); } diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index d5cdbf40f..aebfcee0a 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -29,6 +29,7 @@ * @return Helium abundance in the core (Y_c) */ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const { + // If Shikauchi+ core mass prescription is used, core helium abundance is calculated with the core mass if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) return m_HeliumAbundanceCore; double heliumAbundanceCoreMax = 1.0 - m_Metallicity; @@ -289,8 +290,10 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl // If Shikauchi+ core prescription is used, return Shikauchi luminosity if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { - if (m_Age <= 0.99 * timescales(tMS)) return CalculateLuminosityShikauchi(m_MinimumCoreMass, m_HeliumAbundanceCore); - else return CalculateLuminosityShikauchiTransitionToHG(); + if ((m_Age < 0.99 * timescales(tMS)) || (m_TotalMassLossRate != -m_Mdot)) + return CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore); + else + return CalculateLuminosityShikauchiTransitionToHG(); } const double epsilon = 0.01; @@ -352,21 +355,16 @@ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const * @return Luminosity on the Main Sequence (for age between tHook and tMS) */ double MainSequence::CalculateLuminosityShikauchiTransitionToHG() const { - double luminosity; - if (m_TotalMassLossRate != -m_Mdot) // Ongoing mass transfer episode - luminosity = CalculateLuminosityShikauchi(m_MinimumCoreMass, m_HeliumAbundanceCore); - else { - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime - double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts - - HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); - double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) - delete clone; clone = nullptr; // Return the memory allocated for the clone - - double luminosityAtShikauchiPhaseEnd = CalculateLuminosityShikauchi(m_MinimumCoreMass, 1.0 - m_Metallicity); - // Linear interpolation - luminosity = (luminosityAtShikauchiPhaseEnd * (tMS - m_Age) + luminosityTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); - } + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime + double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts + + HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); + double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) + delete clone; clone = nullptr; // Return the memory allocated for the clone + + double luminosityAtShikauchiPhaseEnd = CalculateLuminosityShikauchi(m_MainSequenceCoreMass, 1.0 - m_Metallicity); + // Linear interpolation + double luminosity = (luminosityAtShikauchiPhaseEnd * (tMS - m_Age) + luminosityTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); return luminosity; } @@ -540,9 +538,12 @@ double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_ #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function // If Shikauchi+ core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0) && (m_Age >= 0.99 * timescales(tMS)) && (m_TotalMassLossRate == -m_Mdot)) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { + if ((m_Age >= 0.99 * timescales(tMS)) && (m_TotalMassLossRate == -m_Mdot)) return CalculateRadiusShikauchiTransitionToHG(); - + else { }; + } + const double epsilon = 0.01; double RTMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); @@ -782,10 +783,10 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { // The clone should not evolve, and so should not log anything, but to be sure the // clone does not participate in logging, we set its persistence to EPHEMERAL. - HG *clone = HG::Clone(*this, OBJECT_PERSISTENCE::EPHEMERAL); + HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); double TAMSCoreMass = clone->CoreMass(); // get core mass from clone delete clone; clone = nullptr; // return the memory allocated for the clone - double minimumCoreMass = std::max(m_MinimumCoreMass, CalculateTauOnPhase() * TAMSCoreMass); + double minimumCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass); return minimumCoreMass; } @@ -796,23 +797,24 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { * * This function also calculates and updates the core helium abundance * - * double CalculateMainSequenceCoreMassShikauchi() + * double CalculateMainSequenceCoreMassShikauchi(const double p_Dt) * + * @param [IN] p_Dt Current time step in Myr * @return Tuple containing the mass of the convective core in Msol and core helium abundance */ -DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { +DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) { DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); double totalMass = m_Mass; double centralHeliumFraction = m_HeliumAbundanceCore; - double mixingCoreMass = m_MinimumCoreMass; + double mixingCoreMass = m_MainSequenceCoreMass; double lnMixingCoreMass = std::log(mixingCoreMass); double heliumFractionOut = m_HeliumAbundanceCoreOut; - + // Eq (A3) - double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(- m_MZAMS / FMIX_COEFFICIENTS[2]); + double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); // Eq (A4) double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-totalMass / FMIX_COEFFICIENTS[2]); // Eq (A5) @@ -826,8 +828,8 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { // Eq (A6) double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); - double currentTimestepInYrs = m_Dt * MYR_TO_YEAR; - double mDot = m_TotalMassLossRate; + double currentTimestepInYrs = p_Dt * MYR_TO_YEAR; + double mDot = m_TotalMassLossRate; // Positive for mass gain, negative for mass loss // Use boost adaptive ODE solver controlled_stepper_type controlled_stepper; @@ -835,7 +837,9 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { x[0] = centralHeliumFraction; x[1] = lnMixingCoreMass; auto ode = [&](const state_type &x, state_type &dxdt, const double) { + // Eq (14) dxdt[0] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); + // Eq (13) dxdt[1] = - alpha / (1.0 - alpha * centralHeliumFraction) * dxdt[0] + beta * delta * mDot / totalMass; }; integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); @@ -845,7 +849,7 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { double deltaCoreMass = newMixingCoreMass - mixingCoreMass; // Difference in core mass if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation - if (mixingCoreMass < m_InitialMixingCoreMass) { // New core mass less than initial core mass? + if (newMixingCoreMass < m_InitialMixingCoreMass) { // New core mass less than initial core mass? // Use boost adaptive ODE solver state_type x(1); x[0] = heliumFractionOut; @@ -853,21 +857,15 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi() { dxdt[0] = (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * (deltaCoreMass / currentTimestepInYrs); }; integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); - - double deltaY = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * deltaCoreMass + 1 / (mixingCoreMass + deltaCoreMass) * 0.5 * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; + // Calculate the change in Yc, we assume linear profile between Yc and Y0, and that the accreted material has helium fraction Y0 + double deltaY = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * deltaCoreMass + 0.5 / (mixingCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; newCentralHeliumFraction = centralHeliumFraction + deltaY; m_HeliumAbundanceCoreOut = x[0]; // Update the helium abundance just outside the core } else { // New core mass greater than initial core mass? - // Use boost adaptive ODE solver - state_type x(1); - x[0] = centralHeliumFraction; - auto ode = [&](const state_type &x, state_type &dxdt, const double) { - dxdt[0] = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * (deltaCoreMass / currentTimestepInYrs); - }; - integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); - m_InitialMixingCoreMass = newMixingCoreMass; // Update initial core mass, we assume that helium abundance just outside the core does not change + double deltaY = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * deltaCoreMass + 0.5 / (mixingCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; + newCentralHeliumFraction = centralHeliumFraction + deltaY; } } else @@ -946,55 +944,44 @@ std::tuple MainSequence::InterpolateShikauc /* * Update the minimum core mass of a main sequence star that loses mass through winds or Case A mass transfer * - * void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) + * void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) * * @param [IN] p_Dt Current timestep - * @param [IN] p_TotalMassLossRate Mass loss rate either from stellar winds or mass transfer + * @param [IN] p_TotalMassLossRate Mass loss rate either from stellar winds or mass transfer (Msol/yr) */ -void MainSequence::UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { - // Only proceed with updating the core mass if time advances and calculation was not executed in binary evolution (total mass loss rate had been updated) - +void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { switch (OPTIONS->MainSequenceCoreMassPrescription()) { case CORE_MASS_PRESCRIPTION::NONE: { - m_MinimumCoreMass = 0.0; + m_MainSequenceCoreMass = 0.0; break; } case CORE_MASS_PRESCRIPTION::MANDEL: { // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass if ((p_Dt != 0.0) && (p_TotalMassLossRate != -m_Mdot)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE - m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); + m_MainSequenceCoreMass = CalculateMainSequenceCoreMassMandel(); break; } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi+ prescription - if ((p_Dt == 0.0) || (p_TotalMassLossRate != m_TotalMassLossRate)) + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi, Hirai, Mandel (2024) + if ((p_Dt == 0.0) || (p_TotalMassLossRate != m_TotalMassLossRate)) // Do not proceed with updating the core mass if time does not advance or calculation was done in binary evolution (total mass loss rate had been updated) return; double mixingCoreMass; double centralHeliumFraction; std::tuple ShikauchiSolution; + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - if ((p_TotalMassLossRate <= 0.0) && (m_MZAMS >= 10.0)) { // Mass loss and MZAMS >= 10 Msol (Shikauchi+ prescription is valid) - ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(); + if (m_MZAMS >= 10.0) { // MZAMS >= 10 Msol (Shikauchi+ prescription) + if (m_HeliumAbundanceCore == 1.0 - m_Metallicity) // No calculations past the MS hook (Yc = 1 - Z) + return; + ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(p_Dt); mixingCoreMass = std::get<0>(ShikauchiSolution); centralHeliumFraction = std::get<1>(ShikauchiSolution); - m_MinimumCoreMass = mixingCoreMass; // Update the core mass m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance - - double massPostMT = m_Mass + p_TotalMassLossRate * p_Dt * MYR_TO_YEAR; // Total mass after mass loss - UpdateAgeBasedOnCentralHelium(massPostMT, m_HeliumAbundanceCore); // Update the effective age + m_MainSequenceCoreMass = mixingCoreMass; // Update the core mass + m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age based on central helium fraction } - else if ((p_TotalMassLossRate <= 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer (does not take into account mass loss from winds)) - m_MinimumCoreMass = CalculateMainSequenceCoreMassMandel(); + else if ((p_TotalMassLossRate <= 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer (does not take into account mass loss from winds)) + m_MainSequenceCoreMass = CalculateMainSequenceCoreMassMandel(); } - else if ((p_TotalMassLossRate > 0.0) && (m_MZAMS >= 10.0)) { // Mass gain and MZAMS >= 10 Msol - ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(); - mixingCoreMass = std::get<0>(ShikauchiSolution); - centralHeliumFraction = std::get<1>(ShikauchiSolution); - m_MinimumCoreMass = mixingCoreMass; // Update the core mass - m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance (previous core helium abundance is updated in the function itself) - - double massPostMT = m_Mass + p_TotalMassLossRate * p_Dt * MYR_TO_YEAR; // Total mass after mass gain - UpdateAgeBasedOnCentralHelium(massPostMT, m_HeliumAbundanceCore); // Update the effective age - } - else { }; // Mass gain and MZAMS < 10 Msol + else { }; // Other cases break; } } @@ -1071,29 +1058,7 @@ void MainSequence::UpdateAgeAfterMassLoss() { double tBGBprime = CalculateLifetimeToBGB(m_Mass); double tMSprime = MainSequence::CalculateLifetimeOnPhase(m_Mass, tBGBprime); - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0) && (m_Age < 0.99 * tMSprime)) - return; - else - m_Age *= tMSprime / tMS; -} - - -/* - * Update the effective age based on the central helium fraction (only valid for 99% of the main sequence lifetime, before - * the hook begins - * - * Used in Shikauchi+ 2024 core mass calculations - * - * void UpdateAgeBasedOnCentralHelium(const double p_Mass, const double p_HeliumAbundanceCore) - * - * @param [IN] p_Mass Mass in Msol - * @param [IN] p_HeliumAbundanceCore Helium abundance in the core - */ -void MainSequence::UpdateAgeBasedOnCentralHelium(const double p_Mass, const double p_HeliumAbundanceCore) { - double tBGB = CalculateLifetimeToBGB(p_Mass); - double tMS = CalculateLifetimeOnPhase(p_Mass, tBGB); - double age = (p_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; - if (age < 0.99 * tMS) m_Age = age; + m_Age *= tMSprime / tMS; } @@ -1198,9 +1163,9 @@ STELLAR_TYPE MainSequence::ResolveEnvelopeLoss(bool p_Force) { void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function - m_Mass = p_Mass; - m_Mass0 = m_Mass; - m_MinimumCoreMass = 0.0; + m_Mass = p_Mass; + m_Mass0 = m_Mass; + m_MainSequenceCoreMass = 0.0; double initialHydrogenFraction = m_InitialHydrogenAbundance; diff --git a/src/MainSequence.h b/src/MainSequence.h index e2079e86f..99c0932ea 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -24,8 +24,8 @@ class MainSequence: virtual public BaseStar { protected: - double m_InitialMixingCoreMass = 0.0; - double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Previous helium core mass abundance, needed for Shikauchi+ core mass calculation (in case of mass gain) + double m_InitialMixingCoreMass = 0.0; // Initial mass of the mixing core, gets initialised in MS_gt_07 class + double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, needed for Shikauchi+ core mass calculation (in case of mass gain) // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; @@ -51,7 +51,7 @@ class MainSequence: virtual public BaseStar { double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass - double CalculateHeCoreMassOnPhase() const { return m_MinimumCoreMass; } // McHe(MS) = 0.0 + double CalculateHeCoreMassOnPhase() const { return 0.0; } // McHe(MS) = 0.0 double CalculateHeliumAbundanceCoreAtPhaseEnd() const { return CalculateHeliumAbundanceCoreOnPhase(); } double CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const; @@ -76,7 +76,7 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const; double CalculateLuminosityShikauchiTransitionToHG() const; double CalculateMainSequenceCoreMassMandel(); - DBL_DBL CalculateMainSequenceCoreMassShikauchi(); + DBL_DBL CalculateMainSequenceCoreMassShikauchi(const double p_Dt); double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 @@ -120,9 +120,8 @@ class MainSequence: virtual public BaseStar { void UpdateAfterMerger(double p_Mass, double p_HydrogenMass); void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateAgeBasedOnCentralHelium(const double p_Mass, const double p_HeliumAbundanceCore); - void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate); + void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate); }; diff --git a/src/Options.cpp b/src/Options.cpp index 865cac830..665b07de0 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -2229,7 +2229,7 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(!found, "Unknown LBV Mass Loss Prescription"); } - if (!DEFAULTED("main-sequence-core-mass-prescription")) { + if (!DEFAULTED("main-sequence-core-mass-prescription")) { // MS core mass prescription std::tie(found, m_MainSequenceCoreMassPrescription.type) = utils::GetMapKey(m_MainSequenceCoreMassPrescription.typeString, CORE_MASS_PRESCRIPTION_LABEL, m_MainSequenceCoreMassPrescription.type); COMPLAIN_IF(!found, "Unknown Main Sequence Core Mass Prescription"); } diff --git a/src/Options.h b/src/Options.h index 5ce06f1ce..5bcae915f 100755 --- a/src/Options.h +++ b/src/Options.h @@ -994,7 +994,7 @@ class Options { bool m_ExpelConvectiveEnvelopeAboveLuminosityThreshold; // Whether to expel the convective envelope in a pulsation when log_10(L/M) reaches the threshold defined by m_LuminosityToMassThreshold double m_LuminosityToMassThreshold; // Threshold value of log_10(L/M) above which the convective envelope is expelled in a pulsation - ENUM_OPT m_MainSequenceCoreMassPrescription; + ENUM_OPT m_MainSequenceCoreMassPrescription; // Which MS core prescription ENUM_OPT m_CaseBBStabilityPrescription; // Which prescription for the stability of case BB/BC mass transfer @@ -1506,8 +1506,6 @@ class Options { CORE_MASS_PRESCRIPTION MainSequenceCoreMassPrescription() const { return OPT_VALUE("main-sequence-core-mass-prescription", m_MainSequenceCoreMassPrescription.type, true); } - CORE_MASS_PRESCRIPTION MainSequenceCoreMassPrescription() const { return OPT_VALUE("main-sequence-core-mass-prescription", m_MainSequenceCoreMassPrescription.type, true); } - double MassChangeFraction() const { return m_CmdLine.optionValues.m_MassChangeFraction; } MASS_LOSS_PRESCRIPTION MassLossPrescription() const { return OPT_VALUE("mass-loss-prescription", m_MassLossPrescription.type, true); } diff --git a/src/Star.h b/src/Star.h index f6b1b01d3..9bff19eca 100755 --- a/src/Star.h +++ b/src/Star.h @@ -289,7 +289,7 @@ class Star { p_MassGainPerTimeStep, p_Epsilon);} - void UpdateMinimumCoreMass(const double p_Dt, const double p_TotalMassLossRate) { m_Star->UpdateMinimumCoreMass(p_Dt, p_TotalMassLossRate); } + void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { m_Star->UpdateMainSequenceCoreMass(p_Dt, p_TotalMassLossRate); } void UpdatePreviousTimestepDuration() { m_Star->UpdatePreviousTimestepDuration(); } From 2ceb24040230d4426b60f142d9afd1047f2772c9 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Mon, 18 Nov 2024 13:01:55 +1100 Subject: [PATCH 15/25] Merger treatment, rejuvenation fix, code cleanup --- src/BaseBinaryStar.cpp | 10 ++ src/HG.cpp | 5 + src/MainSequence.cpp | 230 +++++++++++++++++++++++------------------ src/MainSequence.h | 12 ++- src/Options.cpp | 12 --- src/Options.h | 1 - src/Star.h | 1 + 7 files changed, 153 insertions(+), 118 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index 6535ace68..85c749213 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -1724,6 +1724,16 @@ void BaseBinaryStar::ResolveMainSequenceMerger() { m_SemiMajorAxis = std::numeric_limits::infinity(); // set separation to infinity to avoid subsequent fake interactions with a massless companion (RLOF, CE, etc.) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_Star1->MZAMS() >= 10.0) && (m_Star2->MZAMS() >= 10.0)) { + double coreMass1 = m_Star1->MainSequenceCoreMass(); + double coreMass2 = m_Star2->MainSequenceCoreMass(); + + double coreHeliumMass1 = m_Star1->HeliumAbundanceCore() * coreMass1; + double coreHeliumMass2 = m_Star2->HeliumAbundanceCore() * coreMass2; + + finalHydrogenMass = finalMass * initialHydrogenFraction - coreHeliumMass1 - coreHeliumMass2; + } + m_Star1->UpdateAfterMerger(finalMass, finalHydrogenMass); m_Star2->SwitchTo(STELLAR_TYPE::MASSLESS_REMNANT); diff --git a/src/HG.cpp b/src/HG.cpp index 107bbdc42..eebd79216 100755 --- a/src/HG.cpp +++ b/src/HG.cpp @@ -817,6 +817,11 @@ double HG::CalculateRadiusOnPhase(const double p_Mass, const double p_Tau, const #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function double RTMS = MainSequence::CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); + + // This ensures continuity of stellar tracks when Shikauchi+ core prescription is used + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) + RTMS = MainSequence::CalculateRadiusAtPhaseEnd(m_Mass, p_RZAMS); + double RGB = GiantBranch::CalculateRadiusOnPhase_Static(p_Mass, m_Luminosity, b); double rx = RGB; // Hurley sse terminlogy (rx) diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index aebfcee0a..ac377b4fb 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -29,9 +29,11 @@ * @return Helium abundance in the core (Y_c) */ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const { - // If Shikauchi+ core mass prescription is used, core helium abundance is calculated with the core mass + + // If SHIKAUCHI core mass prescription is used, core helium abundance is calculated with the core mass if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) return m_HeliumAbundanceCore; + double heliumAbundanceCoreMax = 1.0 - m_Metallicity; return ((heliumAbundanceCoreMax - m_InitialHeliumAbundance) * p_Tau) + m_InitialHeliumAbundance; } @@ -52,8 +54,11 @@ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) con * @return Hydrogen abundance in the core (X_c) */ double MainSequence::CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) const { + + // If SHIKAUCHI core mass prescription is used, core helium abundance is calculated with the core mass if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) return 1.0 - m_HeliumAbundanceCore - m_Metallicity; + return m_InitialHydrogenAbundance * (1.0 - p_Tau); } @@ -288,12 +293,12 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl #define a m_AnCoefficients // for convenience and readability - undefined at end of function #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function - // If Shikauchi+ core prescription is used, return Shikauchi luminosity + // If SHIKAUCHI core prescription is used, return Shikauchi luminosity if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { - if ((m_Age < 0.99 * timescales(tMS)) || (m_TotalMassLossRate != -m_Mdot)) - return CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore); + if ((m_HeliumAbundanceCore == 1.0 - m_Metallicity) && (m_TotalMassLossRate == -m_Mdot)) // star in MS hook and mass transfer not ongoing? + return CalculateLuminosityShikauchiTransitionToHG(p_Time); else - return CalculateLuminosityShikauchiTransitionToHG(); + return CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore); } const double epsilon = 0.01; @@ -336,11 +341,13 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl * @return Luminosity on the Main Sequence as a function of current core mass and central helium fraction */ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const { - DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); - double centralHeliumFraction = p_HeliumAbundanceCore; - double logMixingCoreMass = std::log10(p_CoreMass); - double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; - return PPOW(10.0, logL); + DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); + double centralHeliumFraction = p_HeliumAbundanceCore; + double logMixingCoreMass = std::log10(p_CoreMass); + + double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; + + return PPOW(10.0, logL); } @@ -350,21 +357,22 @@ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const * Shikauchi+ prescription cannot be used beyond the MS hook (at age 0.99 * tMS), and this smoothly connects luminosity between * the beginning of the hook and the beginning of the HG * - * double CalculateLuminosityShikauchiTransitionToHG() + * double CalculateLuminosityShikauchiTransitionToHG(const double p_Age) * + * @param [IN] p_Age Age in Myr * @return Luminosity on the Main Sequence (for age between tHook and tMS) */ -double MainSequence::CalculateLuminosityShikauchiTransitionToHG() const { - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime - double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts - +double MainSequence::CalculateLuminosityShikauchiTransitionToHG(const double p_Age) const { HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) delete clone; clone = nullptr; // Return the memory allocated for the clone - double luminosityAtShikauchiPhaseEnd = CalculateLuminosityShikauchi(m_MainSequenceCoreMass, 1.0 - m_Metallicity); + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + double ageAtHookStart = 0.99 * tMS; + double luminosityAtHookStart = CalculateLuminosityShikauchi(m_MainSequenceCoreMass, 1.0 - m_Metallicity); + // Linear interpolation - double luminosity = (luminosityAtShikauchiPhaseEnd * (tMS - m_Age) + luminosityTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); + double luminosity = (luminosityAtHookStart * (tMS - p_Age) + luminosityTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); return luminosity; } @@ -537,11 +545,11 @@ double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_ #define a m_AnCoefficients // for convenience and readability - undefined at end of function #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function - // If Shikauchi+ core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG + // If SHIKAUCHI core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { - if ((m_Age >= 0.99 * timescales(tMS)) && (m_TotalMassLossRate == -m_Mdot)) - return CalculateRadiusShikauchiTransitionToHG(); - else { }; + if ((m_HeliumAbundanceCore == 1.0 - m_Metallicity) && (m_TotalMassLossRate == -m_Mdot)) // star in MS hook and mass transfer not ongoing? + return CalculateRadiusShikauchiTransitionToHG(p_Mass, p_Time, p_RZAMS); + else { }; // this is where a new radius prescription would go } const double epsilon = 0.01; @@ -632,22 +640,25 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double /* * Calculate radius on the transition from the Main Sequence to the HG when Shikauchi et al. (2024) core mass prescription is used * - * Shikauchi+ prescription cannot be used beyond the MS hook (at age 0.99 * tMS), and this smoothly connects the radius between + * Shikauchi+ prescription cannot be used beyond the MS hook (beyond age 0.99 * tMS), and this smoothly connects the radius between * the beginning of the hook and the beginning of the HG * * double CalculateRadiusShikauchiTransitionToHG() - * + + * @param [IN] p_Mass Mass in Msol + * @param [IN] p_Age Age in Myr + * @param [IN] p_RZAMS Zero Age Main Sequence (ZAMS) Radius * @return Radius on the Main Sequence (for age between tHook and tMS) */ -double MainSequence::CalculateRadiusShikauchiTransitionToHG() const { - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; // MS lifetime - double ageAtShikauchiPhaseEnd = 0.99 * tMS; // Age at which the MS hook starts - - double radiusTAMS = CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); // Radius at TAMS - double tauAtShikauchiPhaseEnd = ageAtShikauchiPhaseEnd / tMS; // Tau when the MS hook starts - double radiusAtShikauchiPhaseEnd = CalculateRadiusOnPhaseTau(m_Mass, tauAtShikauchiPhaseEnd); // Radius when the MS hook starts +double MainSequence::CalculateRadiusShikauchiTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const { + double radiusTAMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + double tauAtHookStart = 0.99; + double ageAtHookStart = tauAtHookStart * tMS; + double radiusAtHookStart = CalculateRadiusOnPhaseTau(p_Mass, tauAtHookStart); + // Linear interpolation - double radius = (radiusAtShikauchiPhaseEnd * (tMS - m_Age) + radiusTAMS * (m_Age - ageAtShikauchiPhaseEnd)) / (tMS - ageAtShikauchiPhaseEnd); + double radius = (radiusAtHookStart * (tMS - p_Age) + radiusTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); return radius; } @@ -795,7 +806,8 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { * Calculate the convective core mass of a main sequence star that loses mass either through winds or * Case A mass transfer according to Shikauchi et al. (2024) * - * This function also calculates and updates the core helium abundance + * This function also accounts for mass gain by modeling rejuvenation and can modify the initial mixing core mass + * and the helium abundance just outside the core * * double CalculateMainSequenceCoreMassShikauchi(const double p_Dt) * @@ -837,9 +849,9 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) x[0] = centralHeliumFraction; x[1] = lnMixingCoreMass; auto ode = [&](const state_type &x, state_type &dxdt, const double) { - // Eq (14) - dxdt[0] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); // Eq (13) + dxdt[0] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); + // Eq (12) dxdt[1] = - alpha / (1.0 - alpha * centralHeliumFraction) * dxdt[0] + beta * delta * mDot / totalMass; }; integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); @@ -847,25 +859,31 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) double newMixingCoreMass = std::exp(x[1]); // New mixing core mass double newCentralHeliumFraction = x[0]; // New central helium fraction double deltaCoreMass = newMixingCoreMass - mixingCoreMass; // Difference in core mass - + if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation if (newMixingCoreMass < m_InitialMixingCoreMass) { // New core mass less than initial core mass? // Use boost adaptive ODE solver state_type x(1); x[0] = heliumFractionOut; - auto ode = [&](const state_type &x, state_type &dxdt, const double) { + auto ode = [&](const state_type &x, state_type &dxdt, const double) { // Calculate the change in helium abundance just outside the core, assuming linear profile dxdt[0] = (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * (deltaCoreMass / currentTimestepInYrs); }; integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); - // Calculate the change in Yc, we assume linear profile between Yc and Y0, and that the accreted material has helium fraction Y0 - double deltaY = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * deltaCoreMass + 0.5 / (mixingCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; + + double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; // Calculate the change in helium abundance in the core, assuming linear profile between Yc and Y0, and that the gas mixed into the core has helium fraction Y0 newCentralHeliumFraction = centralHeliumFraction + deltaY; m_HeliumAbundanceCoreOut = x[0]; // Update the helium abundance just outside the core } - else { // New core mass greater than initial core mass? - m_InitialMixingCoreMass = newMixingCoreMass; // Update initial core mass, we assume that helium abundance just outside the core does not change - double deltaY = (heliumFractionOut - centralHeliumFraction) / mixingCoreMass * deltaCoreMass + 0.5 / (mixingCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; - newCentralHeliumFraction = centralHeliumFraction + deltaY; + else { // New core mass greater or equal to the initial core mass? + if (mixingCoreMass != m_InitialMixingCoreMass) { + double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; + newCentralHeliumFraction = centralHeliumFraction + deltaY; + m_InitialMixingCoreMass = newMixingCoreMass; // Update initial core mass and we assume that helium abundance just outside the core does not change + } + else { + newCentralHeliumFraction = centralHeliumFraction; + m_InitialMixingCoreMass = newMixingCoreMass; // Update initial core mass and we assume that helium abundance just outside the core does not change + } } } else @@ -891,58 +909,8 @@ double MainSequence::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { /* - * Linear interpolation/extrapolation for coefficients from Shikauchi et al. (2024), used for core mass calculations - * - * std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) - * - * @param [IN] p_Metallicity Metallicity - * @return Tuple containing vectors of coefficients for the specified metallicity - */ -std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) const { - double logZ = std::log10(p_Metallicity); - DBL_VECTOR alphaCoeff(3, 0.0); - DBL_VECTOR fmixCoeff(3, 0.0); - DBL_VECTOR lCoeff(10, 0.0); - - // Skip calculation if Shikauchi+ core prescription is not used - if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::SHIKAUCHI) - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); - - // Coefficients are given for these metallicities - double low = std::log10(0.1 * ZSOL_ASPLUND); - double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); - double high = std::log10(ZSOL_ASPLUND); - - if (logZ <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound - return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); - - else if ((logZ > low) && (logZ <= middle)) { // Linear interpolation between metallicity low and middle - for (int i = 0; i < 3; i++) - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); - } - - else if ((logZ > middle) && (logZ < high)) { // Linear interpolation between metallicity middle and high - for (int i = 0; i < 3; i++) - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); - } - - else // Linear extrapolation (constant) for metallicity equal to solar or higher - return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); -} - - -/* - * Update the minimum core mass of a main sequence star that loses mass through winds or Case A mass transfer + * Update the core mass of a main sequence star that loses mass through winds or Case A mass transfer + * When Shikauchi et al. (2024) core prescription is used, also update the core helium abundance and effective age * * void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) * @@ -956,11 +924,11 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ break; } case CORE_MASS_PRESCRIPTION::MANDEL: { // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass - if ((p_Dt != 0.0) && (p_TotalMassLossRate != -m_Mdot)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE + if ((p_Dt != 0.0) && (p_TotalMassLossRate != -m_Mdot) && (p_TotalMassLossRate < 0.0)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE and only applied to donors m_MainSequenceCoreMass = CalculateMainSequenceCoreMassMandel(); break; } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi, Hirai, Mandel (2024) + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi et al. (2024) if ((p_Dt == 0.0) || (p_TotalMassLossRate != m_TotalMassLossRate)) // Do not proceed with updating the core mass if time does not advance or calculation was done in binary evolution (total mass loss rate had been updated) return; double mixingCoreMass; @@ -968,9 +936,7 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ std::tuple ShikauchiSolution; double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - if (m_MZAMS >= 10.0) { // MZAMS >= 10 Msol (Shikauchi+ prescription) - if (m_HeliumAbundanceCore == 1.0 - m_Metallicity) // No calculations past the MS hook (Yc = 1 - Z) - return; + if ((m_MZAMS >= 10.0) && (m_HeliumAbundanceCore < 1.0 - m_Metallicity)) { // MZAMS >= 10 Msol (SHIKAUCHI prescription) and no calculations past the MS hook (Yc = 1 - Z) ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(p_Dt); mixingCoreMass = std::get<0>(ShikauchiSolution); centralHeliumFraction = std::get<1>(ShikauchiSolution); @@ -978,7 +944,7 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ m_MainSequenceCoreMass = mixingCoreMass; // Update the core mass m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age based on central helium fraction } - else if ((p_TotalMassLossRate <= 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer (does not take into account mass loss from winds)) + else if ((p_TotalMassLossRate < 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer -- does not take into account mass loss from winds) m_MainSequenceCoreMass = CalculateMainSequenceCoreMassMandel(); } else { }; // Other cases @@ -1108,6 +1074,57 @@ double MainSequence::ChooseTimestep(const double p_Time) const { } +/* + * Linear interpolation/extrapolation for coefficients from Shikauchi et al. (2024), used for core mass calculations + * + * std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) + * + * @param [IN] p_Metallicity Metallicity + * @return Tuple containing vectors of coefficients for the specified metallicity + */ +std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) const { + double logZ = std::log10(p_Metallicity); + DBL_VECTOR alphaCoeff(3, 0.0); + DBL_VECTOR fmixCoeff(3, 0.0); + DBL_VECTOR lCoeff(10, 0.0); + + // Skip calculation if Shikauchi+ core prescription is not used + if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::SHIKAUCHI) + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + + // Coefficients are given for these metallicities + double low = std::log10(0.1 * ZSOL_ASPLUND); + double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); + double high = std::log10(ZSOL_ASPLUND); + + if (logZ <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound + return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); + + else if ((logZ > low) && (logZ <= middle)) { // Linear interpolation between metallicity low and middle + for (int i = 0; i < 3; i++) + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + for (int i = 0; i < 3; i++) + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + for (int i = 0; i < 10; i++) + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + } + + else if ((logZ > middle) && (logZ < high)) { // Linear interpolation between metallicity middle and high + for (int i = 0; i < 3; i++) + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + for (int i = 0; i < 3; i++) + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + for (int i = 0; i < 10; i++) + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + } + + else // Linear extrapolation (constant) for metallicity equal to solar or higher + return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); +} + + /* * Resolve changes to the remnant after the star loses its envelope * @@ -1176,6 +1193,19 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { m_Age = m_Tau * timescales(tMS); + m_HeliumAbundanceCore = 1.0 - m_Metallicity - p_HydrogenMass / p_Mass; + + m_HydrogenAbundanceCore = 1.0 - m_Metallicity - m_HeliumAbundanceCore; + + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (p_Mass >= 10.0)) { + DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); + m_InitialMixingCoreMass = CalculateMixingCoreMassAtZAMS(p_Mass); // update initial mixing core mass + + double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * m_InitialMixingCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; + + m_MainSequenceCoreMass = m_InitialMixingCoreMass * (1 - alpha * m_HeliumAbundanceCore) / (1 - alpha * m_InitialHeliumAbundance); // derived from integrating eq. (4) in Shikauchi et al. (2024) + } + UpdateAttributesAndAgeOneTimestep(0.0, 0.0, 0.0, true); #undef timescales diff --git a/src/MainSequence.h b/src/MainSequence.h index 99c0932ea..45eaa060c 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -20,12 +20,12 @@ class MainSequence: virtual public BaseStar { MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::A; } // Always case A - const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); // Interpolate Shikauchi coefficients for the given metallicity; + const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); // Interpolate Shikauchi coefficients for the given metallicity; protected: - double m_InitialMixingCoreMass = 0.0; // Initial mass of the mixing core, gets initialised in MS_gt_07 class - double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, needed for Shikauchi+ core mass calculation (in case of mass gain) + double m_InitialMixingCoreMass = 0.0; // Initial mass of the mixing core is properly initialised in MS_gt_07 class + double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, needed for Shikauchi+ core mass calculation (in case of mass gain) // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; @@ -74,7 +74,7 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const; - double CalculateLuminosityShikauchiTransitionToHG() const; + double CalculateLuminosityShikauchiTransitionToHG(const double p_Age) const; double CalculateMainSequenceCoreMassMandel(); DBL_DBL CalculateMainSequenceCoreMassShikauchi(const double p_Dt); double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); @@ -90,7 +90,9 @@ class MainSequence: virtual public BaseStar { double CalculateRadiusAtPhaseEnd(const double p_Mass, const double p_RZAMS) const; double CalculateRadiusAtPhaseEnd() const { return CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); } // Use class member variables double CalculateRadiusOnPhase() const { return CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); } // Use class member variables - double CalculateRadiusShikauchiTransitionToHG() const; + double CalculateRadiusShikauchiTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const; + + double CalculateRadiusShikauchiTransitionToHGTau(const double p_Mass, const double p_Tau) const; double CalculateTauAtPhaseEnd() const { return 1.0; } // tau = 1.0 at end of MS double CalculateTauOnPhase() const; diff --git a/src/Options.cpp b/src/Options.cpp index 680ad5b46..3b4e4ccfc 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -1785,12 +1785,6 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt po::value(&p_Options->m_LBVMassLossPrescription.typeString)->default_value(p_Options->m_LBVMassLossPrescription.typeString), ("LBV Mass loss prescription (" + AllowedOptionValuesFormatted("LBV-mass-loss-prescription") + ", default = '" + p_Options->m_LBVMassLossPrescription.typeString + "')").c_str() ) - - ( - "luminous-blue-variable-prescription", // DEPRECATED June 2024 - remove end 2024 - po::value(&p_Options->m_LBVMassLossPrescription.typeString)->default_value(p_Options->m_LBVMassLossPrescription.typeString), - ("LBV Mass loss prescription (" + AllowedOptionValuesFormatted("luminous-blue-variable-prescription") + ", default = '" + p_Options->m_LBVMassLossPrescription.typeString + "')").c_str() - ) ( "main-sequence-core-mass-prescription", po::value(&p_Options->m_MainSequenceCoreMassPrescription.typeString)->default_value(p_Options->m_MainSequenceCoreMassPrescription.typeString), @@ -2234,11 +2228,6 @@ std::string Options::OptionValues::CheckAndSetOptions() { std::tie(found, m_LBVMassLossPrescription.type) = utils::GetMapKey(m_LBVMassLossPrescription.typeString, LBV_MASS_LOSS_PRESCRIPTION_LABEL, m_LBVMassLossPrescription.type); COMPLAIN_IF(!found, "Unknown LBV Mass Loss Prescription"); } - - if (!DEFAULTED("luminous-blue-variable-prescription")) { // DEPRECATED June 2024 - remove end 2024 // LBV mass loss prescription - std::tie(found, m_LBVMassLossPrescription.type) = utils::GetMapKey(m_LBVMassLossPrescription.typeString, LBV_MASS_LOSS_PRESCRIPTION_LABEL, m_LBVMassLossPrescription.type); - COMPLAIN_IF(!found, "Unknown LBV Mass Loss Prescription"); - } if (!DEFAULTED("main-sequence-core-mass-prescription")) { // MS core mass prescription std::tie(found, m_MainSequenceCoreMassPrescription.type) = utils::GetMapKey(m_MainSequenceCoreMassPrescription.typeString, CORE_MASS_PRESCRIPTION_LABEL, m_MainSequenceCoreMassPrescription.type); @@ -2600,7 +2589,6 @@ std::vector Options::AllowedOptionValues(const std::string p_Option case _("kick-magnitude-distribution") : POPULATE_RET(KICK_MAGNITUDE_DISTRIBUTION_LABEL); break; case _("logfile-type") : POPULATE_RET(LOGFILETYPELabel); break; case _("LBV-mass-loss-prescription") : POPULATE_RET(LBV_MASS_LOSS_PRESCRIPTION_LABEL); break; - case _("luminous-blue-variable-prescription") : POPULATE_RET(LBV_MASS_LOSS_PRESCRIPTION_LABEL); break; // DEPRECATED June 2024 - remove end 2024 case _("main-sequence-core-mass-prescription") : POPULATE_RET(CORE_MASS_PRESCRIPTION_LABEL); break; case _("mass-loss-prescription") : POPULATE_RET(MASS_LOSS_PRESCRIPTION_LABEL); break; case _("mass-ratio-distribution") : POPULATE_RET(MASS_RATIO_DISTRIBUTION_LABEL); break; diff --git a/src/Options.h b/src/Options.h index 5bcae915f..59fa6103b 100755 --- a/src/Options.h +++ b/src/Options.h @@ -617,7 +617,6 @@ class Options { "logfile-system-parameters", "logfile-system-parameters-record-types", "logfile-type", - "luminous-blue-variable-prescription", // DEPRECATED June 2024 - remove end 2024 "main-sequence-core-mass-prescription", "mass-change-fraction", diff --git a/src/Star.h b/src/Star.h index 9bff19eca..199a7719b 100755 --- a/src/Star.h +++ b/src/Star.h @@ -117,6 +117,7 @@ class Star { double LambdaKruckow() const { return m_Star->LambdaKruckow(); } double LambdaDewi() const { return m_Star->LambdaDewi(); } double Luminosity() const { return m_Star->Luminosity(); } + double MainSequenceCoreMass() const { return m_Star->MainSequenceCoreMass(); } double Mass() const { return m_Star->Mass(); } double Mass0() const { return m_Star->Mass0(); } double MassPrev() const { return m_Star->MassPrev(); } From 202741fdcf34ca5c14cda247d5a3519b3b264520 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Mon, 18 Nov 2024 15:54:46 +1100 Subject: [PATCH 16/25] Code cleanup --- .../preprocessing/compasConfigDefault.yaml | 2 +- .../program-options-list-defaults.rst | 6 +- src/BaseStar.cpp | 6 +- src/BaseStar.h | 8 +-- src/CH.h | 2 - src/GiantBranch.h | 2 + src/HG.cpp | 2 +- src/HG.h | 3 +- src/MS_gt_07.h | 2 +- src/MainSequence.cpp | 61 ++++++++----------- src/MainSequence.h | 11 ++-- src/constants.h | 8 +-- src/yaml.h | 2 +- 13 files changed, 51 insertions(+), 64 deletions(-) diff --git a/compas_python_utils/preprocessing/compasConfigDefault.yaml b/compas_python_utils/preprocessing/compasConfigDefault.yaml index 85cc6194e..4c5a536d8 100644 --- a/compas_python_utils/preprocessing/compasConfigDefault.yaml +++ b/compas_python_utils/preprocessing/compasConfigDefault.yaml @@ -43,7 +43,6 @@ booleanChoices: # --hmxr-binaries: False # Default: False # --mass-transfer: True # Default: True # --use-mass-transfer: True # Default: True -# --retain-core-mass-during-caseA-mass-transfer: True # Default: True ### COMMON ENVELOPE # --common-envelope-allow-immediate-RLOF-post-CE-survive: False # Default: False @@ -236,6 +235,7 @@ stringChoices: # --initial-mass-function: 'KROUPA' # Default: 'KROUPA' # Options: ['KROUPA','UNIFORM','POWERLAW','SALPETER'] # --LBV-mass-loss-prescription: 'HURLEY_ADD' # Default: 'HURLEY_ADD' # Options: ['BELCZYNSKI','HURLEY','HURLEY_ADD','ZERO','NONE'] # --luminous-blue-variable-prescription: 'HURLEY_ADD' # Default: 'HURLEY_ADD' # Options: ['BELCZYNSKI','HURLEY','HURLEY_ADD','ZERO','NONE'] +# --main-sequence-core-mass-prescription: 'MANDEL' # Default: 'MANDEL' # Options: ['SHIKAUCHI','MANDEL','NONE'] # --mass-loss-prescription: 'MERRITT2024' # Default: 'MERRITT2024' # Options: ['MERRITT2024','BELCZYNSKI2010','HURLEY','ZERO','NONE'] # --OB-mass-loss: 'VINK2021' # Default: 'VINK2021' # Options: ['KRTICKA2018','BJORKLUND2022','VINK2021','VINK2001','ZERO','NONE'] # --OB-mass-loss-prescription: 'VINK2021' # Default: 'VINK2021' # Options: ['KRTICKA2018','BJORKLUND2022','VINK2021','VINK2001','ZERO','NONE'] diff --git a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst index 82997736a..222776b79 100644 --- a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst +++ b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst @@ -798,7 +798,7 @@ Default = 4.2 **--main-sequence-core-mass-prescription** |br| Main sequence core mass prescription. |br| Options: {NONE, MANDEL, SHIKAUCHI} |br| -``NONE`` : No treatment, core mass not tracked on the MS |br| +``NONE`` : No core mass treatment |br| ``MANDEL`` : The core following case A mass transfer is set equal to the expected core mass of a newly formed HG star with mass equal to that of the donor, scaled by the fraction of the donor's MS lifetime at mass transfer |br| ``SHIKAUCHI`` : Core mass according to Shikauchi et al. (2024) |br| @@ -1420,7 +1420,7 @@ Go to :ref:`the top of this page ` for the full alphabetical **Stellar evolution and winds** --use-mass-loss, --check-photon-tiring-limit, --cool-wind-mass-loss-multiplier, --luminous-blue-variable-prescription, --LBV-mass-loss-prescription ---luminous-blue-variable-multiplier, --mass-loss-prescription, --overall-wind-mass-loss-multiplier, --wolf-rayet-multiplier, +--luminous-blue-variable-multiplier, --main-sequence-core-mass-prescription, --mass-loss-prescription, --overall-wind-mass-loss-multiplier, --wolf-rayet-multiplier, --expel-convective-envelope-above-luminosity-threshold, --luminosity-to-mass-threshold, --OB-mass-loss, --OB-mass-loss-prescription, --RSG-mass-loss, --RSG-mass-loss-prescription, --VMS-mass-loss, --vms-mass-loss-prescription, --WR-mass-loss, --WR-mass-loss-prescription @@ -1440,7 +1440,7 @@ Go to :ref:`the top of this page ` for the full alphabetical --critical-mass-ratio-helium-giant-degenerate-accretor, --critical-mass-ratio-helium-giant-non-degenerate-accretor, --critical-mass-ratio-white-dwarf-degenerate-accretor, --critical-mass-ratio-white-dwarf-non-degenerate-accretor, --eddington-accretion-factor, --mass-transfer, --use-mass-transfer, --mass-transfer-accretion-efficiency-prescription, --mass-transfer-angular-momentum-loss-prescription, --mass-transfer-fa, --mass-transfer-jloss, --mass-transfer-jloss-macleod-linear-fraction-degen, --mass-transfer-jloss-macleod-linear-fraction-non-degen, ---mass-transfer-rejuvenation-prescription, --mass-transfer-thermal-limit-accretor, --mass-transfer-thermal-limit-accretor-multiplier, --mass-transfer-thermal-limit-C, --retain-core-mass-during-caseA-mass-transfer, +--mass-transfer-rejuvenation-prescription, --mass-transfer-thermal-limit-accretor, --mass-transfer-thermal-limit-accretor-multiplier, --mass-transfer-thermal-limit-C, --stellar-zeta-prescription, --zeta-adiabatic-arbitrary, --zeta-main-sequence, --zeta-radiative-giant-star --circulariseBinaryDuringMassTransfer, --angular-momentum-conservation-during-circularisation, --tides-prescription diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index fbde7d6d8..2a4f409d4 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -2857,7 +2857,9 @@ double BaseStar::CalculateMassLossRate() { mDot = mDot * OPTIONS->OverallWindMassLossMultiplier(); // apply overall wind mass loss multiplier } - UpdateTotalMassLossRate(-mDot); // update mass loss rate + + UpdateTotalMassLossRate(-mDot); // update total mass loss rate + return mDot; } @@ -4715,6 +4717,7 @@ STELLAR_TYPE BaseStar::EvolveOnPhase(const double p_DeltaTime) { STELLAR_TYPE stellarType = m_StellarType; if (ShouldEvolveOnPhase()) { // evolve timestep on phase + UpdateMainSequenceCoreMass(p_DeltaTime, -m_Mdot); // update core mass, relevant for MS stars m_Tau = CalculateTauOnPhase(); @@ -4773,6 +4776,7 @@ STELLAR_TYPE BaseStar::ResolveEndOfPhase(const bool p_ResolveEnvelopeLoss) { if (p_ResolveEnvelopeLoss) stellarType = ResolveEnvelopeLoss(); // if required, resolve envelope loss if it occurs if (stellarType == m_StellarType) { // staying on phase? + m_Tau = CalculateTauAtPhaseEnd(); m_COCoreMass = CalculateCOCoreMassAtPhaseEnd(); diff --git a/src/BaseStar.h b/src/BaseStar.h index d01a1385e..1dbcf7355 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -357,9 +357,9 @@ class BaseStar { const double p_MassGainPerTimeStep, const double p_Epsilon) { } // Default is NO-OP - virtual void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // Set minimal core mass for Main Sequence stars losing mass through winds or mass transfer; default is NO-OP + virtual void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // Set core mass for Main Sequence stars; default is NO-OP - virtual void UpdateTotalMassLossRate(const double p_MassLossRate) { m_TotalMassLossRate = p_MassLossRate; } // m_TotalMassLossRate = m_Mdot in SSE, during a mass transfer episode m_TotalMassLossRate = m_MassLossRateInRLOF + virtual void UpdateTotalMassLossRate(const double p_MassLossRate) { m_TotalMassLossRate = p_MassLossRate; } // m_TotalMassLossRate = -m_Mdot in SSE, during a mass transfer episode m_TotalMassLossRate = m_MassLossRateInRLOF // printing functions bool PrintDetailedOutput(const int p_Id, const SSE_DETAILED_RECORD_TYPE p_RecordType) const { @@ -442,7 +442,7 @@ class BaseStar { double m_Tau; // Relative time double m_Temperature; // Current temperature (Tsol) double m_Time; // Current physical time the star has been evolved (Myr) - double m_TotalMassLossRate; // Current mass loss rate from winds or mass transfer (Msol per yr) + double m_TotalMassLossRate; // Current mass loss/gain rate from mass transfer or winds (Msol per yr) // Previous timestep variables double m_DtPrev; // Previous timestep @@ -621,7 +621,7 @@ class BaseStar { virtual double CalculateMassTransferRejuvenationFactor() { return 1.0; } double CalculateMaximumCoreMass(double p_Mass) const; - + double CalculateOmegaBreak() const; static double CalculateOpacity_Static(const double p_HeliumAbundanceSurface); diff --git a/src/CH.h b/src/CH.h index 4ce7a75e4..8b9cd79ee 100755 --- a/src/CH.h +++ b/src/CH.h @@ -90,8 +90,6 @@ class CH: virtual public BaseStar, public MS_gt_07 { void UpdateAgeAfterMassLoss(); - void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } - }; #endif // __CH_h__ diff --git a/src/GiantBranch.h b/src/GiantBranch.h index 2e47849b9..cd483f382 100755 --- a/src/GiantBranch.h +++ b/src/GiantBranch.h @@ -135,6 +135,8 @@ class GiantBranch: virtual public BaseStar, public MainSequence { void UpdateAgeAfterMassLoss() { } // NO-OP for most stellar types void UpdateInitialMass() { } // NO-OP for most stellar types + + void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // NO-OP for most stellar types }; diff --git a/src/HG.cpp b/src/HG.cpp index eebd79216..379564263 100755 --- a/src/HG.cpp +++ b/src/HG.cpp @@ -818,7 +818,7 @@ double HG::CalculateRadiusOnPhase(const double p_Mass, const double p_Tau, const double RTMS = MainSequence::CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); - // This ensures continuity of stellar tracks when Shikauchi+ core prescription is used + // This ensures continuity of stellar tracks when SHIKAUCHI core mass prescription is used if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) RTMS = MainSequence::CalculateRadiusAtPhaseEnd(m_Mass, p_RZAMS); diff --git a/src/HG.h b/src/HG.h index 127d18dfb..f1519d3e4 100755 --- a/src/HG.h +++ b/src/HG.h @@ -56,7 +56,7 @@ class HG: virtual public BaseStar, public GiantBranch { m_Age = m_Timescales[static_cast(TIMESCALE::tMS)]; // Set age appropriately // update effective "initial" mass (m_Mass0) so that the core mass is at least equal to the minimum core mass but no more than total mass - // (only relevant if Mandel or Shikauchi main sequence core mass prescription is used) + // (only relevant if MANDEL or SHIKAUCHI main sequence core mass prescription is used) if (utils::Compare(CalculateCoreMassOnPhase(m_Mass0, m_Age), std::min(m_Mass, MainSequenceCoreMass())) < 0) { double desiredCoreMass = std::min(m_Mass, MainSequenceCoreMass()); // desired core mass m_Mass0 = Mass0ToMatchDesiredCoreMass(this, desiredCoreMass); // use root finder to find new core mass estimate @@ -145,7 +145,6 @@ class HG: virtual public BaseStar, public GiantBranch { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 void UpdateInitialMass(); // Per Hurley et al. 2000, section 7.1 - void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { } // NO-OP for stellar types other than MS /* diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index 1f4250503..5203f3052 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -46,7 +46,7 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { m_InitialMixingCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); m_MainSequenceCoreMass = m_InitialMixingCoreMass; - m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_InitialHeliumAbundance); + m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_InitialHeliumAbundance, m_Age); m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); } diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index ac377b4fb..d1744b9db 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -295,10 +295,7 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl // If SHIKAUCHI core prescription is used, return Shikauchi luminosity if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { - if ((m_HeliumAbundanceCore == 1.0 - m_Metallicity) && (m_TotalMassLossRate == -m_Mdot)) // star in MS hook and mass transfer not ongoing? - return CalculateLuminosityShikauchiTransitionToHG(p_Time); - else - return CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore); + return CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore, p_Time); } const double epsilon = 0.01; @@ -331,48 +328,39 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl /* * Calculate luminosity on the Main Sequence when Shikauchi et al. (2024) core mass prescription is used * - * Shikauchi et al. 2024, eq (A5) + * During core hydrogen burning uses eq (A5) from Shikauchi et al. (2024) * + * When the Main Sequence hook starts (at age 0.99 * tMS) calculates luminosity that smoothly connects the last point + * of core hydrogen burning with the first point of the HG * - * double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) + * double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) * * @param [IN] p_CoreMass Main sequence core mass in Msol * @param [IN] p_HeliumAbundanceCore Central helium fraction + * @param [IN] p_Age Current age in Myr * @return Luminosity on the Main Sequence as a function of current core mass and central helium fraction */ -double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const { +double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const { DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); double centralHeliumFraction = p_HeliumAbundanceCore; double logMixingCoreMass = std::log10(p_CoreMass); double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; - - return PPOW(10.0, logL); -} - - -/* - * Calculate luminosity on the transition from the Main Sequence to the HG when Shikauchi et al. (2024) core mass prescription is used - * - * Shikauchi+ prescription cannot be used beyond the MS hook (at age 0.99 * tMS), and this smoothly connects luminosity between - * the beginning of the hook and the beginning of the HG - * - * double CalculateLuminosityShikauchiTransitionToHG(const double p_Age) - * - * @param [IN] p_Age Age in Myr - * @return Luminosity on the Main Sequence (for age between tHook and tMS) - */ -double MainSequence::CalculateLuminosityShikauchiTransitionToHG(const double p_Age) const { - HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); - double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) - delete clone; clone = nullptr; // Return the memory allocated for the clone + double luminosity = PPOW(10.0, logL); - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - double ageAtHookStart = 0.99 * tMS; - double luminosityAtHookStart = CalculateLuminosityShikauchi(m_MainSequenceCoreMass, 1.0 - m_Metallicity); + if ((p_HeliumAbundanceCore == 1.0 - m_Metallicity) && (m_TotalMassLossRate == -m_Mdot)) { // star in MS hook and mass transfer not ongoing? + HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); + double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) + delete clone; clone = nullptr; // Return the memory allocated for the clone + + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + double ageAtHookStart = 0.99 * tMS; + double luminosityAtHookStart = luminosity; + + // Linear interpolation + luminosity = (luminosityAtHookStart * (tMS - p_Age) + luminosityTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); + } - // Linear interpolation - double luminosity = (luminosityAtHookStart * (tMS - p_Age) + luminosityTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); return luminosity; } @@ -548,8 +536,7 @@ double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_ // If SHIKAUCHI core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { if ((m_HeliumAbundanceCore == 1.0 - m_Metallicity) && (m_TotalMassLossRate == -m_Mdot)) // star in MS hook and mass transfer not ongoing? - return CalculateRadiusShikauchiTransitionToHG(p_Mass, p_Time, p_RZAMS); - else { }; // this is where a new radius prescription would go + return CalculateRadiusTransitionToHG(p_Mass, p_Time, p_RZAMS); } const double epsilon = 0.01; @@ -643,14 +630,14 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double * Shikauchi+ prescription cannot be used beyond the MS hook (beyond age 0.99 * tMS), and this smoothly connects the radius between * the beginning of the hook and the beginning of the HG * - * double CalculateRadiusShikauchiTransitionToHG() + * double CalculateRadiusShikauchiTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) * @param [IN] p_Mass Mass in Msol * @param [IN] p_Age Age in Myr * @param [IN] p_RZAMS Zero Age Main Sequence (ZAMS) Radius * @return Radius on the Main Sequence (for age between tHook and tMS) */ -double MainSequence::CalculateRadiusShikauchiTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const { +double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const { double radiusTAMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; double tauAtHookStart = 0.99; @@ -830,7 +817,7 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) // Eq (A4) double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-totalMass / FMIX_COEFFICIENTS[2]); // Eq (A5) - double logL = std::log10(CalculateLuminosityShikauchi(mixingCoreMass, centralHeliumFraction)); + double logL = std::log10(CalculateLuminosityShikauchi(mixingCoreMass, centralHeliumFraction, 0.0)); // Eq (A2) double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * mixingCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; // Eq (A7) diff --git a/src/MainSequence.h b/src/MainSequence.h index 45eaa060c..060a40712 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -47,7 +47,7 @@ class MainSequence: virtual public BaseStar { double CalculateCOCoreMassAtPhaseEnd() const { return CalculateCOCoreMassOnPhase(); } // Same as on phase double CalculateCOCoreMassOnPhase() const { return 0.0; } // McCO(MS) = 0.0 - double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer + double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass @@ -73,8 +73,7 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityAtPhaseEnd() const { return CalculateLuminosityAtPhaseEnd(m_Mass0); } // Use class member variables double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables - double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const; - double CalculateLuminosityShikauchiTransitionToHG(const double p_Age) const; + double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const; double CalculateMainSequenceCoreMassMandel(); DBL_DBL CalculateMainSequenceCoreMassShikauchi(const double p_Dt); double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); @@ -90,10 +89,8 @@ class MainSequence: virtual public BaseStar { double CalculateRadiusAtPhaseEnd(const double p_Mass, const double p_RZAMS) const; double CalculateRadiusAtPhaseEnd() const { return CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); } // Use class member variables double CalculateRadiusOnPhase() const { return CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); } // Use class member variables - double CalculateRadiusShikauchiTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const; - - double CalculateRadiusShikauchiTransitionToHGTau(const double p_Mass, const double p_Tau) const; - + double CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const; + double CalculateTauAtPhaseEnd() const { return 1.0; } // tau = 1.0 at end of MS double CalculateTauOnPhase() const; diff --git a/src/constants.h b/src/constants.h index 25f6b8b66..1e9dceab6 100755 --- a/src/constants.h +++ b/src/constants.h @@ -3671,9 +3671,9 @@ const std::vector>> LOVERIDGE_COE } }; -// Coefficients for determining core mass for stars on the MS +// Coefficients for determining Main Sequence core mass // from Shikauchi et al. (2024), https://arxiv.org/abs/2409.00460 -// from Table 2 +// Table 2 const std::vector SHIKAUCHI_ALPHA_COEFFICIENTS = { // 0.1*Z_Sun {0.45, -0.0557105, -0.86589929}, @@ -3682,7 +3682,7 @@ const std::vector SHIKAUCHI_ALPHA_COEFFICIENTS = { // Solar metallicity Z_Sun {0.45, -0.05878711, -0.84646162} }; -// from Table 3 +// Table 3 const std::vector SHIKAUCHI_FMIX_COEFFICIENTS = { // 0.1*Z_Sun {0.86914766, -0.60815098, 37.20654856}, @@ -3691,7 +3691,7 @@ const std::vector SHIKAUCHI_FMIX_COEFFICIENTS = { // Solar metallicity Z_Sun {0.86605495, -0.64960375, 35.57019104} }; -// from Table 4 +// Table 4 const std::vector SHIKAUCHI_L_COEFFICIENTS = { // 0.1*Z_Sun {3.2555795, 1.84666823, -0.79986388, -0.75728099, -0.38831172, 0.08223542, 0.49543834, 0.31314176, -0.36705796, 1.72200581}, diff --git a/src/yaml.h b/src/yaml.h index 274288815..245ce7f93 100644 --- a/src/yaml.h +++ b/src/yaml.h @@ -108,7 +108,6 @@ namespace yaml { " --circularise-binary-during-mass-transfer", " --hmxr-binaries", " --use-mass-transfer", - " --retain-core-mass-during-caseA-mass-transfer", "", " ### COMMON ENVELOPE", " --common-envelope-allow-immediate-RLOF-post-CE-survive", @@ -299,6 +298,7 @@ namespace yaml { " --envelope-state-prescription", " --initial-mass-function", " --LBV-mass-loss-prescription", + " --main-sequence-core-mass-prescription", " --mass-loss-prescription", " --OB-mass-loss-prescription", " --RSG-mass-loss-prescription", From 6af1e0826c9e8a3909df69f0b587aa239e64cd7a Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Tue, 26 Nov 2024 14:58:23 +1100 Subject: [PATCH 17/25] Rejuvenation fix and code cleanup --- src/BaseBinaryStar.cpp | 2 +- src/MainSequence.cpp | 142 ++++++++++++++++++++--------------------- src/Options.h | 2 - src/changelog.h | 8 ++- 4 files changed, 78 insertions(+), 76 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index 4a37de583..c6ba517ac 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -2091,7 +2091,7 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { m_MassTransferTrackerHistory = m_Donor == m_Star1 ? MT_TRACKING::STABLE_1_TO_2_SURV : MT_TRACKING::STABLE_2_TO_1_SURV; // record what happened - for later printing double massGainAccretor = -massDiffDonor * m_FractionAccreted; // set accretor mass gain to mass loss * conservativeness - m_Accretor->UpdateTotalMassLossRate(massGainAccretor / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS accretor + m_Accretor->UpdateTotalMassLossRate(massGainAccretor / (p_Dt * MYR_TO_YEAR)); // update mass gain rate for MS accretor m_Accretor->UpdateMainSequenceCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); // update core mass for MS accretor m_Donor->SetMassTransferDiffAndResolveWDShellChange(massDiffDonor); // set new mass of donor diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 28dd2aef2..3269581a5 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -765,9 +765,6 @@ DBL_DBL MainSequence::CalculateConvectiveEnvelopeMass() const { * Calculate the minimum core mass of a main sequence star that loses mass through Case A mass transfer as * the core mass of a TAMS star, scaled by the fractional age. * - * The minimum core mass of the star is updated only if the retain-core-mass-during-caseA-mass-transfer - * option is specified, otherwise it is left unchanged. - * * double CalculateMainSequenceCoreMassMandel() * * @return Minimum convective core mass in Msol @@ -793,7 +790,7 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { * Calculate the convective core mass of a main sequence star that loses mass either through winds or * Case A mass transfer according to Shikauchi et al. (2024) * - * This function also accounts for mass gain by modeling rejuvenation and can modify the initial mixing core mass + * This function also accounts for mass gain by modeling rejuvenation and updates the initial mixing core mass * and the helium abundance just outside the core * * double CalculateMainSequenceCoreMassShikauchi(const double p_Dt) @@ -849,28 +846,33 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation if (newMixingCoreMass < m_InitialMixingCoreMass) { // New core mass less than initial core mass? + // Use boost adaptive ODE solver state_type x(1); x[0] = heliumFractionOut; - auto ode = [&](const state_type &x, state_type &dxdt, const double) { // Calculate the change in helium abundance just outside the core, assuming linear profile + auto ode = [&](const state_type &x, state_type &dxdt, const double) { + // Calculate the change in helium abundance just outside the core, assuming linear profile dxdt[0] = (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * (deltaCoreMass / currentTimestepInYrs); }; integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); - double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; // Calculate the change in helium abundance in the core, assuming linear profile between Yc and Y0, and that the gas mixed into the core has helium fraction Y0 + // Calculate the change in helium abundance in the core, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 + double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; + newCentralHeliumFraction = centralHeliumFraction + deltaY; - m_HeliumAbundanceCoreOut = x[0]; // Update the helium abundance just outside the core + m_HeliumAbundanceCoreOut = x[0]; } else { // New core mass greater or equal to the initial core mass? - if (mixingCoreMass != m_InitialMixingCoreMass) { - double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; - newCentralHeliumFraction = centralHeliumFraction + deltaY; - m_InitialMixingCoreMass = newMixingCoreMass; // Update initial core mass and we assume that helium abundance just outside the core does not change - } - else { - newCentralHeliumFraction = centralHeliumFraction; - m_InitialMixingCoreMass = newMixingCoreMass; // Update initial core mass and we assume that helium abundance just outside the core does not change - } + double firstTerm = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass; + // Second term is not valid if the initial core mass had been previously exceeded + double secondTerm = (m_InitialMixingCoreMass != CalculateMixingCoreMassAtZAMS(m_MZAMS)) ? 0.0 : 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; + + // Change in helium abundance + double deltaY = firstTerm + secondTerm; + + newCentralHeliumFraction = centralHeliumFraction + deltaY; + m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; + m_InitialMixingCoreMass = newMixingCoreMass; } } else @@ -1062,57 +1064,6 @@ double MainSequence::ChooseTimestep(const double p_Time) const { } -/* - * Linear interpolation/extrapolation for coefficients from Shikauchi et al. (2024), used for core mass calculations - * - * std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) - * - * @param [IN] p_Metallicity Metallicity - * @return Tuple containing vectors of coefficients for the specified metallicity - */ -std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) const { - double logZ = std::log10(p_Metallicity); - DBL_VECTOR alphaCoeff(3, 0.0); - DBL_VECTOR fmixCoeff(3, 0.0); - DBL_VECTOR lCoeff(10, 0.0); - - // Skip calculation if Shikauchi+ core prescription is not used - if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::SHIKAUCHI) - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); - - // Coefficients are given for these metallicities - double low = std::log10(0.1 * ZSOL_ASPLUND); - double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); - double high = std::log10(ZSOL_ASPLUND); - - if (logZ <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound - return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); - - else if ((logZ > low) && (logZ <= middle)) { // Linear interpolation between metallicity low and middle - for (int i = 0; i < 3; i++) - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); - } - - else if ((logZ > middle) && (logZ < high)) { // Linear interpolation between metallicity middle and high - for (int i = 0; i < 3; i++) - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); - } - - else // Linear extrapolation (constant) for metallicity equal to solar or higher - return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); -} - - /* * Resolve changes to the remnant after the star loses its envelope * @@ -1186,12 +1137,8 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { m_HydrogenAbundanceCore = 1.0 - m_Metallicity - m_HeliumAbundanceCore; if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (p_Mass >= 10.0)) { - DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); m_InitialMixingCoreMass = CalculateMixingCoreMassAtZAMS(p_Mass); // update initial mixing core mass - - double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * m_InitialMixingCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; - - m_MainSequenceCoreMass = m_InitialMixingCoreMass * (1 - alpha * m_HeliumAbundanceCore) / (1 - alpha * m_InitialHeliumAbundance); // derived from integrating eq. (4) in Shikauchi et al. (2024) + m_MainSequenceCoreMass = m_InitialMixingCoreMass; } UpdateAttributesAndAgeOneTimestep(0.0, 0.0, 0.0, true); @@ -1361,3 +1308,54 @@ double MainSequence::InterpolateGeEtAlQCrit(const QCRIT_PRESCRIPTION p_qCritPres return qCritPerMetallicity[1] + (m_Log10Metallicity - logZhi)*(qCritPerMetallicity[1] - qCritPerMetallicity[0])/(logZhi - logZlo); } + + +/* + * Linear interpolation/extrapolation for coefficients from Shikauchi et al. (2024), used for core mass calculations + * + * std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) + * + * @param [IN] p_Metallicity Metallicity + * @return Tuple containing vectors of coefficients for the specified metallicity + */ +std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) const { + double logZ = std::log10(p_Metallicity); + DBL_VECTOR alphaCoeff(3, 0.0); + DBL_VECTOR fmixCoeff(3, 0.0); + DBL_VECTOR lCoeff(10, 0.0); + + // Skip calculation if Shikauchi+ core prescription is not used + if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::SHIKAUCHI) + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + + // Coefficients are given for these metallicities + double low = std::log10(0.1 * ZSOL_ASPLUND); + double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); + double high = std::log10(ZSOL_ASPLUND); + + if (logZ <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound + return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); + + else if ((logZ > low) && (logZ <= middle)) { // Linear interpolation between metallicity low and middle + for (int i = 0; i < 3; i++) + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + for (int i = 0; i < 3; i++) + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + for (int i = 0; i < 10; i++) + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + } + + else if ((logZ > middle) && (logZ < high)) { // Linear interpolation between metallicity middle and high + for (int i = 0; i < 3; i++) + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + for (int i = 0; i < 3; i++) + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + for (int i = 0; i < 10; i++) + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + } + + else // Linear extrapolation (constant) for metallicity equal to solar or higher + return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); +} diff --git a/src/Options.h b/src/Options.h index 613d0085b..cfd9353d3 100755 --- a/src/Options.h +++ b/src/Options.h @@ -485,8 +485,6 @@ class Options { "logfile-rlof-parameters", "logfile-rlof-parameters-record-types", - "main-sequence-core-mass-prescription", - "mass-ratio", "q", "mass-ratio-max", "mass-ratio-min", diff --git a/src/changelog.h b/src/changelog.h index 40cdd9cdc..50f62e709 100644 --- a/src/changelog.h +++ b/src/changelog.h @@ -1399,6 +1399,12 @@ // - The nuclear timescale mass transfer rate is now set by the requirement that the star ends the time step just filling its Roche lobe (addresses issue #1285) // - Fix an issue with the root finder for fitting into the RL that led to artificial failures to find a root // - Fix issue (likely introduced in 03.08.00) with the accretor not gaining mass appropriately -const std::string VERSION_STRING = "03.09.00"; +// 03.10.00 AB - Nov 26, 2024 - Enhancement: +// - Added Shikauchi et al. (2024) core mass prescription, describing convective core evolution under mass loss/gain +// - Added new luminosity prescription from Shikauchi et al. (2024) +// - Added treatment for rejuvenation for main sequence accretors +// - New options: --main-sequence-core-mass-prescription SHIKAUCHI (new prescription), MANDEL (replaces --retain-core-mass-during-caseA-mass-transfer), +// NONE (no core mass treatment) +const std::string VERSION_STRING = "03.10.00"; # endif // __changelog_h__ From 50f4be5731d0f7ff865d367167860e666ad8fbe6 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Wed, 27 Nov 2024 11:38:40 +1100 Subject: [PATCH 18/25] Minor code cleanup --- src/CH.h | 2 +- src/MS_gt_07.h | 4 ++-- src/MainSequence.cpp | 32 ++++++++++++++++---------------- src/MainSequence.h | 10 +++++----- src/Options.cpp | 2 +- src/changelog.h | 4 ++-- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/CH.h b/src/CH.h index 3d3f9f91c..77741f7f7 100755 --- a/src/CH.h +++ b/src/CH.h @@ -89,7 +89,7 @@ class CH: virtual public BaseStar, public MS_gt_07 { bool ShouldEvolveOnPhase() const { return m_Age < m_Timescales[static_cast(TIMESCALE::tMS)] && (OPTIONS->OptimisticCHE() || Omega() >= m_OmegaCHE); } // Evolve on CHE phase if age in MS timescale and spinning at least as fast as CHE threshold void UpdateAgeAfterMassLoss(); - + }; #endif // __CH_h__ diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index 5203f3052..e1e05051a 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -44,8 +44,8 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { // Initialise core mass, luminosity, radius, and temperature if Shikauchi core mass prescription is used if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { - m_InitialMixingCoreMass = MainSequence::CalculateMixingCoreMassAtZAMS(m_MZAMS); - m_MainSequenceCoreMass = m_InitialMixingCoreMass; + m_InitialMainSequenceCoreMass = MainSequence::CalculateInitialMainSequenceCoreMass(m_MZAMS); + m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_InitialHeliumAbundance, m_Age); m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 3269581a5..0d9c507da 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -627,8 +627,9 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double /* * Calculate radius on the transition from the Main Sequence to the HG when Shikauchi et al. (2024) core mass prescription is used * - * Shikauchi+ prescription cannot be used beyond the MS hook (beyond age 0.99 * tMS), and this smoothly connects the radius between - * the beginning of the hook and the beginning of the HG + * SHIKAUCHI core mass prescription cannot be used beyond the MS hook (beyond age 0.99 * tMS), and this function smoothly connects + * the radius between the beginning of the hook and the beginning of the HG + * * * double CalculateRadiusShikauchiTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) @@ -845,34 +846,31 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) double deltaCoreMass = newMixingCoreMass - mixingCoreMass; // Difference in core mass if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation - if (newMixingCoreMass < m_InitialMixingCoreMass) { // New core mass less than initial core mass? + if (newMixingCoreMass < m_InitialMainSequenceCoreMass) { // New core mass less than initial core mass? // Use boost adaptive ODE solver state_type x(1); x[0] = heliumFractionOut; auto ode = [&](const state_type &x, state_type &dxdt, const double) { // Calculate the change in helium abundance just outside the core, assuming linear profile - dxdt[0] = (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * (deltaCoreMass / currentTimestepInYrs); + dxdt[0] = (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * (deltaCoreMass / currentTimestepInYrs); }; integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); // Calculate the change in helium abundance in the core, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 - double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; - + double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; newCentralHeliumFraction = centralHeliumFraction + deltaY; m_HeliumAbundanceCoreOut = x[0]; } else { // New core mass greater or equal to the initial core mass? double firstTerm = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass; - // Second term is not valid if the initial core mass had been previously exceeded - double secondTerm = (m_InitialMixingCoreMass != CalculateMixingCoreMassAtZAMS(m_MZAMS)) ? 0.0 : 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMixingCoreMass) * deltaCoreMass * deltaCoreMass; - - // Change in helium abundance - double deltaY = firstTerm + secondTerm; + // Second term is set to zero if the initial core mass had been previously exceeded + double secondTerm = (m_InitialMainSequenceCoreMass != CalculateInitialMainSequenceCoreMass(m_MZAMS)) ? 0.0 : 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; + double deltaY = firstTerm + secondTerm; // Change in helium abundance newCentralHeliumFraction = centralHeliumFraction + deltaY; m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; - m_InitialMixingCoreMass = newMixingCoreMass; + m_InitialMainSequenceCoreMass = newMixingCoreMass; } } else @@ -885,12 +883,13 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) /* * Calculate the initial core mass of a main sequence star using Equation (A3) from Shikauchi et al. (2024) * - * double CalculateMixingCoreMassAtZAMS(const double p_MZAMS) + * + * double CalculateInitialMainSequenceCoreMass(const double p_MZAMS) * * @param [IN] p_MZAMS Mass at ZAMS in Msol * @return Mass of the convective core in Msol at ZAMS */ -double MainSequence::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { +double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_MZAMS) { DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); double fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_MZAMS / fmixCoefficients[2]); return fmix * p_MZAMS; @@ -901,6 +900,7 @@ double MainSequence::CalculateMixingCoreMassAtZAMS(const double p_MZAMS) { * Update the core mass of a main sequence star that loses mass through winds or Case A mass transfer * When Shikauchi et al. (2024) core prescription is used, also update the core helium abundance and effective age * + * * void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) * * @param [IN] p_Dt Current timestep @@ -1137,8 +1137,8 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { m_HydrogenAbundanceCore = 1.0 - m_Metallicity - m_HeliumAbundanceCore; if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (p_Mass >= 10.0)) { - m_InitialMixingCoreMass = CalculateMixingCoreMassAtZAMS(p_Mass); // update initial mixing core mass - m_MainSequenceCoreMass = m_InitialMixingCoreMass; + m_InitialMainSequenceCoreMass = CalculateInitialMainSequenceCoreMass(p_Mass); // update initial mixing core mass + m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; } UpdateAttributesAndAgeOneTimestep(0.0, 0.0, 0.0, true); diff --git a/src/MainSequence.h b/src/MainSequence.h index 36c27807d..e748da822 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -24,8 +24,8 @@ class MainSequence: virtual public BaseStar { protected: - double m_InitialMixingCoreMass = 0.0; // Initial mass of the mixing core is properly initialised in MS_gt_07 class - double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, needed for Shikauchi+ core mass calculation (in case of mass gain) + double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class + double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, used for rejuvenation calculations // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; @@ -47,7 +47,7 @@ class MainSequence: virtual public BaseStar { double CalculateCOCoreMassAtPhaseEnd() const { return CalculateCOCoreMassOnPhase(); } // Same as on phase double CalculateCOCoreMassOnPhase() const { return 0.0; } // McCO(MS) = 0.0 - double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer + double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Same as on phase double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass @@ -76,7 +76,7 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const; double CalculateMainSequenceCoreMassMandel(); DBL_DBL CalculateMainSequenceCoreMassShikauchi(const double p_Dt); - double CalculateMixingCoreMassAtZAMS(const double p_MZAMS); + double CalculateInitialMainSequenceCoreMass(const double p_MZAMS); double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 double CalculatePerturbationMu() const { return 5.0; } // mu(MS) = 5.0 (Hurley et al. 2000, eqs 97 & 98) @@ -103,7 +103,7 @@ class MainSequence: virtual public BaseStar { void EvolveOneTimestepPreamble(); STELLAR_TYPE EvolveToNextPhase() { return STELLAR_TYPE::HERTZSPRUNG_GAP; } - + double InterpolateGeEtAlQCrit(const QCRIT_PRESCRIPTION p_qCritPrescription, const double p_massTransferEfficiencyBeta); // RTW do I need a const here? diff --git a/src/Options.cpp b/src/Options.cpp index 82d307bb6..a15e7acc7 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -2229,7 +2229,7 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(!found, "Unknown LBV Mass Loss Prescription"); } - if (!DEFAULTED("main-sequence-core-mass-prescription")) { // MS core mass prescription + if (!DEFAULTED("main-sequence-core-mass-prescription")) { // main sequence core mass prescription std::tie(found, m_MainSequenceCoreMassPrescription.type) = utils::GetMapKey(m_MainSequenceCoreMassPrescription.typeString, CORE_MASS_PRESCRIPTION_LABEL, m_MainSequenceCoreMassPrescription.type); COMPLAIN_IF(!found, "Unknown Main Sequence Core Mass Prescription"); } diff --git a/src/changelog.h b/src/changelog.h index 50f62e709..92ab203fd 100644 --- a/src/changelog.h +++ b/src/changelog.h @@ -1400,11 +1400,11 @@ // - Fix an issue with the root finder for fitting into the RL that led to artificial failures to find a root // - Fix issue (likely introduced in 03.08.00) with the accretor not gaining mass appropriately // 03.10.00 AB - Nov 26, 2024 - Enhancement: +// - New options: --main-sequence-core-mass-prescription SHIKAUCHI (new prescription), MANDEL (replaces --retain-core-mass-during-caseA-mass-transfer), +// NONE (no core mass treatment) // - Added Shikauchi et al. (2024) core mass prescription, describing convective core evolution under mass loss/gain // - Added new luminosity prescription from Shikauchi et al. (2024) // - Added treatment for rejuvenation for main sequence accretors -// - New options: --main-sequence-core-mass-prescription SHIKAUCHI (new prescription), MANDEL (replaces --retain-core-mass-during-caseA-mass-transfer), -// NONE (no core mass treatment) const std::string VERSION_STRING = "03.10.00"; # endif // __changelog_h__ From 214467fa28fadfa47f7807820135a5e24fff5837 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Thu, 28 Nov 2024 10:27:50 +1100 Subject: [PATCH 19/25] Minor code cleanup --- src/MS_gt_07.h | 8 ++-- src/MainSequence.cpp | 101 +++++++++++++++++++++---------------------- 2 files changed, 54 insertions(+), 55 deletions(-) diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index e1e05051a..76d56ce43 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -45,10 +45,10 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { // Initialise core mass, luminosity, radius, and temperature if Shikauchi core mass prescription is used if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { m_InitialMainSequenceCoreMass = MainSequence::CalculateInitialMainSequenceCoreMass(m_MZAMS); - m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; - m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_InitialHeliumAbundance, m_Age); - m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); - m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); + m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; + m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_InitialHeliumAbundance, m_Age); + m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); + m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); } } diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 0d9c507da..1249bfa3f 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -341,9 +341,9 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl * @return Luminosity on the Main Sequence as a function of current core mass and central helium fraction */ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const { - DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); + DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); double centralHeliumFraction = p_HeliumAbundanceCore; - double logMixingCoreMass = std::log10(p_CoreMass); + double logMixingCoreMass = std::log10(p_CoreMass); double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; double luminosity = PPOW(10.0, logL); @@ -353,8 +353,8 @@ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) delete clone; clone = nullptr; // Return the memory allocated for the clone - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - double ageAtHookStart = 0.99 * tMS; + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + double ageAtHookStart = 0.99 * tMS; double luminosityAtHookStart = luminosity; // Linear interpolation @@ -639,10 +639,10 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double * @return Radius on the Main Sequence (for age between tHook and tMS) */ double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const { - double radiusTAMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - double tauAtHookStart = 0.99; - double ageAtHookStart = tauAtHookStart * tMS; + double radiusTAMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + double tauAtHookStart = 0.99; + double ageAtHookStart = tauAtHookStart * tMS; double radiusAtHookStart = CalculateRadiusOnPhaseTau(p_Mass, tauAtHookStart); // Linear interpolation @@ -801,32 +801,32 @@ double MainSequence::CalculateMainSequenceCoreMassMandel() { */ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) { DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); - DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); - DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); + DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); + DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); - double totalMass = m_Mass; + double totalMass = m_Mass; double centralHeliumFraction = m_HeliumAbundanceCore; - double mixingCoreMass = m_MainSequenceCoreMass; - double lnMixingCoreMass = std::log(mixingCoreMass); - double heliumFractionOut = m_HeliumAbundanceCoreOut; + double mixingCoreMass = m_MainSequenceCoreMass; + double lnMixingCoreMass = std::log(mixingCoreMass); + double heliumFractionOut = m_HeliumAbundanceCoreOut; // Eq (A3) - double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); + double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); // Eq (A4) - double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-totalMass / FMIX_COEFFICIENTS[2]); + double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-totalMass / FMIX_COEFFICIENTS[2]); // Eq (A5) - double logL = std::log10(CalculateLuminosityShikauchi(mixingCoreMass, centralHeliumFraction, 0.0)); + double logL = std::log10(CalculateLuminosityShikauchi(mixingCoreMass, centralHeliumFraction, 0.0)); // Eq (A2) double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * mixingCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; // Eq (A7) - double g = -0.0044 * m_MZAMS + 0.27; + double g = -0.0044 * m_MZAMS + 0.27; // Eq (10) - double Yhat = (centralHeliumFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity); + double Yhat = (centralHeliumFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity); // Eq (A6) double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); double currentTimestepInYrs = p_Dt * MYR_TO_YEAR; - double mDot = m_TotalMassLossRate; // Positive for mass gain, negative for mass loss + double mDot = m_TotalMassLossRate; // Positive for mass gain, negative for mass loss // Use boost adaptive ODE solver controlled_stepper_type controlled_stepper; @@ -841,13 +841,12 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) }; integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); - double newMixingCoreMass = std::exp(x[1]); // New mixing core mass + double newMixingCoreMass = std::exp(x[1]); // New mixing core mass double newCentralHeliumFraction = x[0]; // New central helium fraction - double deltaCoreMass = newMixingCoreMass - mixingCoreMass; // Difference in core mass + double deltaCoreMass = newMixingCoreMass - mixingCoreMass; // Difference in core mass if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation - if (newMixingCoreMass < m_InitialMainSequenceCoreMass) { // New core mass less than initial core mass? - + if (newMixingCoreMass < m_InitialMainSequenceCoreMass) { // New core mass less than initial core mass? // Use boost adaptive ODE solver state_type x(1); x[0] = heliumFractionOut; @@ -858,18 +857,18 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); // Calculate the change in helium abundance in the core, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 - double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; + double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; newCentralHeliumFraction = centralHeliumFraction + deltaY; m_HeliumAbundanceCoreOut = x[0]; } else { // New core mass greater or equal to the initial core mass? - double firstTerm = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass; + double firstTerm = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass; // Second term is set to zero if the initial core mass had been previously exceeded double secondTerm = (m_InitialMainSequenceCoreMass != CalculateInitialMainSequenceCoreMass(m_MZAMS)) ? 0.0 : 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; - double deltaY = firstTerm + secondTerm; // Change in helium abundance - newCentralHeliumFraction = centralHeliumFraction + deltaY; - m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; + double deltaY = firstTerm + secondTerm; // Change in helium abundance + newCentralHeliumFraction = centralHeliumFraction + deltaY; + m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; m_InitialMainSequenceCoreMass = newMixingCoreMass; } } @@ -912,31 +911,31 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ m_MainSequenceCoreMass = 0.0; break; } - case CORE_MASS_PRESCRIPTION::MANDEL: { // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass - if ((p_Dt != 0.0) && (p_TotalMassLossRate != -m_Mdot) && (p_TotalMassLossRate < 0.0)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE and only applied to donors + case CORE_MASS_PRESCRIPTION::MANDEL: { // Set minimal core mass following Main Sequence mass transfer to MS age fraction of TAMS core mass + if ((p_Dt != 0.0) && (p_TotalMassLossRate != -m_Mdot) && (p_TotalMassLossRate < 0.0)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE and only applied to donors m_MainSequenceCoreMass = CalculateMainSequenceCoreMassMandel(); break; } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi et al. (2024) - if ((p_Dt == 0.0) || (p_TotalMassLossRate != m_TotalMassLossRate)) // Do not proceed with updating the core mass if time does not advance or calculation was done in binary evolution (total mass loss rate had been updated) + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi et al. (2024) + if ((p_Dt == 0.0) || (p_TotalMassLossRate != m_TotalMassLossRate)) // Do not proceed with updating the core mass if time does not advance or calculation was done in binary evolution (total mass loss rate had been updated) return; double mixingCoreMass; double centralHeliumFraction; std::tuple ShikauchiSolution; double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - if ((m_MZAMS >= 10.0) && (m_HeliumAbundanceCore < 1.0 - m_Metallicity)) { // MZAMS >= 10 Msol (SHIKAUCHI prescription) and no calculations past the MS hook (Yc = 1 - Z) - ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(p_Dt); - mixingCoreMass = std::get<0>(ShikauchiSolution); - centralHeliumFraction = std::get<1>(ShikauchiSolution); - m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance - m_MainSequenceCoreMass = mixingCoreMass; // Update the core mass - m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age based on central helium fraction + if ((m_MZAMS >= 10.0) && (m_HeliumAbundanceCore < 1.0 - m_Metallicity)) { // MZAMS >= 10 Msol (SHIKAUCHI prescription) and no calculations past the MS hook (Yc = 1 - Z) + ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(p_Dt); + mixingCoreMass = std::get<0>(ShikauchiSolution); + centralHeliumFraction = std::get<1>(ShikauchiSolution); + m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance + m_MainSequenceCoreMass = mixingCoreMass; // Update the core mass + m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age based on central helium fraction } - else if ((p_TotalMassLossRate < 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer -- does not take into account mass loss from winds) + else if ((p_TotalMassLossRate < 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer -- does not take into account mass loss from winds) m_MainSequenceCoreMass = CalculateMainSequenceCoreMassMandel(); } - else { }; // Other cases + else { }; // Other cases break; } } @@ -1132,13 +1131,13 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { m_Age = m_Tau * timescales(tMS); - m_HeliumAbundanceCore = 1.0 - m_Metallicity - p_HydrogenMass / p_Mass; + m_HeliumAbundanceCore = 1.0 - m_Metallicity - p_HydrogenMass / p_Mass; m_HydrogenAbundanceCore = 1.0 - m_Metallicity - m_HeliumAbundanceCore; if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (p_Mass >= 10.0)) { m_InitialMainSequenceCoreMass = CalculateInitialMainSequenceCoreMass(p_Mass); // update initial mixing core mass - m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; + m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; } UpdateAttributesAndAgeOneTimestep(0.0, 0.0, 0.0, true); @@ -1324,14 +1323,14 @@ std::tuple MainSequence::InterpolateShikauc DBL_VECTOR fmixCoeff(3, 0.0); DBL_VECTOR lCoeff(10, 0.0); - // Skip calculation if Shikauchi+ core prescription is not used + // Skip calculation if SHIKAUCHI core prescription is not used if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::SHIKAUCHI) return std::tuple (alphaCoeff, fmixCoeff, lCoeff); // Coefficients are given for these metallicities - double low = std::log10(0.1 * ZSOL_ASPLUND); + double low = std::log10(0.1 * ZSOL_ASPLUND); double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); - double high = std::log10(ZSOL_ASPLUND); + double high = std::log10(ZSOL_ASPLUND); if (logZ <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); @@ -1340,9 +1339,9 @@ std::tuple MainSequence::InterpolateShikauc for (int i = 0; i < 3; i++) alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } @@ -1350,9 +1349,9 @@ std::tuple MainSequence::InterpolateShikauc for (int i = 0; i < 3; i++) alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); for (int i = 0; i < 3; i++) - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); for (int i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } From 71f3b323fea10382aeb00ed6ad41cfa37da93412 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Fri, 20 Dec 2024 15:43:14 +1100 Subject: [PATCH 20/25] Added utils::Compare(), fixed Q_CNO definition, fixed redius at TAMS, other fixes and code cleanup --- src/BaseBinaryStar.cpp | 2 +- src/HG.cpp | 2 +- src/MS_gt_07.h | 2 +- src/MainSequence.cpp | 119 +++++++++++++++++++---------------------- src/constants.h | 7 ++- 5 files changed, 64 insertions(+), 68 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index e20824367..7a4bab9bb 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -1730,7 +1730,7 @@ void BaseBinaryStar::ResolveMainSequenceMerger() { m_SemiMajorAxis = std::numeric_limits::infinity(); // set separation to infinity to avoid subsequent fake interactions with a massless companion (RLOF, CE, etc.) - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_Star1->MZAMS() >= 10.0) && (m_Star2->MZAMS() >= 10.0)) { + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_Star1->MZAMS(), SHIKAUCHI_LOWER_MASS_LIMIT) >= 0) && (utils::Compare(m_Star2->MZAMS(), SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { double coreMass1 = m_Star1->MainSequenceCoreMass(); double coreMass2 = m_Star2->MainSequenceCoreMass(); diff --git a/src/HG.cpp b/src/HG.cpp index d6d635fd7..ae1d310fc 100755 --- a/src/HG.cpp +++ b/src/HG.cpp @@ -820,7 +820,7 @@ double HG::CalculateRadiusOnPhase(const double p_Mass, const double p_Tau, const double RTMS = MainSequence::CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); // This ensures continuity of stellar tracks when SHIKAUCHI core mass prescription is used - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) RTMS = MainSequence::CalculateRadiusAtPhaseEnd(m_Mass, p_RZAMS); double RGB = GiantBranch::CalculateRadiusOnPhase_Static(p_Mass, m_Luminosity, b); diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index 76d56ce43..62e685132 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -43,7 +43,7 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { // Age for MS_GT_07 is carried over from CH stars switching to MS after spinning down, so not set to 0.0 here // Initialise core mass, luminosity, radius, and temperature if Shikauchi core mass prescription is used - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { m_InitialMainSequenceCoreMass = MainSequence::CalculateInitialMainSequenceCoreMass(m_MZAMS); m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; m_Luminosity = MainSequence::CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_InitialHeliumAbundance, m_Age); diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 055e2cd43..d74e1879a 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -31,7 +31,7 @@ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const { // If SHIKAUCHI core mass prescription is used, core helium abundance is calculated with the core mass - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) return m_HeliumAbundanceCore; double heliumAbundanceCoreMax = 1.0 - m_Metallicity; @@ -56,7 +56,7 @@ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) con double MainSequence::CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) const { // If SHIKAUCHI core mass prescription is used, core helium abundance is calculated with the core mass - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) return 1.0 - m_HeliumAbundanceCore - m_Metallicity; return m_InitialHydrogenAbundance * (1.0 - p_Tau); @@ -294,7 +294,7 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function // If SHIKAUCHI core prescription is used, return Shikauchi luminosity - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { return CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore, p_Time); } @@ -342,16 +342,15 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl */ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const { DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); - double centralHeliumFraction = p_HeliumAbundanceCore; double logMixingCoreMass = std::log10(p_CoreMass); - double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * centralHeliumFraction + L_COEFFICIENTS[2] * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * centralHeliumFraction * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * centralHeliumFraction + L_COEFFICIENTS[8] * logMixingCoreMass * centralHeliumFraction * centralHeliumFraction + L_COEFFICIENTS[9]; + double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * p_HeliumAbundanceCore + L_COEFFICIENTS[2] * logMixingCoreMass * p_HeliumAbundanceCore + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * p_HeliumAbundanceCore * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * p_HeliumAbundanceCore + L_COEFFICIENTS[8] * logMixingCoreMass * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[9]; double luminosity = PPOW(10.0, logL); - if ((p_HeliumAbundanceCore == 1.0 - m_Metallicity) && (m_TotalMassLossRate == -m_Mdot)) { // star in MS hook and mass transfer not ongoing? + if ((utils::Compare(p_HeliumAbundanceCore, 1.0 - m_Metallicity) == 0) && (utils::Compare(m_TotalMassLossRate, -m_Mdot) == 0)) { // star in MS hook and mass transfer not ongoing? HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); - double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) - delete clone; clone = nullptr; // Return the memory allocated for the clone + double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) + delete clone; clone = nullptr; // Return the memory allocated for the clone double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; double ageAtHookStart = 0.99 * tMS; @@ -534,8 +533,8 @@ double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_ #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function // If SHIKAUCHI core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (m_MZAMS >= 10.0)) { - if ((m_HeliumAbundanceCore == 1.0 - m_Metallicity) && (m_TotalMassLossRate == -m_Mdot)) // star in MS hook and mass transfer not ongoing? + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { + if ((utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) == 0) && (utils::Compare(m_TotalMassLossRate, -m_Mdot) == 0)) // star in MS hook and mass transfer not ongoing? return CalculateRadiusTransitionToHG(p_Mass, p_Time, p_RZAMS); } @@ -640,7 +639,10 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double * @return Radius on the Main Sequence (for age between tHook and tMS) */ double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const { - double radiusTAMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); + HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); + double radiusTAMS = clone->Radius(); // Get radius from clone (with updated Mass0) + delete clone; clone = nullptr; // Return the memory allocated for the clone + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; double tauAtHookStart = 0.99; double ageAtHookStart = tauAtHookStart * tMS; @@ -767,70 +769,61 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); - double totalMass = m_Mass; - double centralHeliumFraction = m_HeliumAbundanceCore; - double mixingCoreMass = m_MainSequenceCoreMass; - double lnMixingCoreMass = std::log(mixingCoreMass); - double heliumFractionOut = m_HeliumAbundanceCoreOut; - // Eq (A3) double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); // Eq (A4) - double beta = 1.0 - FMIX_COEFFICIENTS[1] * totalMass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-totalMass / FMIX_COEFFICIENTS[2]); + double beta = 1.0 - FMIX_COEFFICIENTS[1] * m_Mass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-m_Mass / FMIX_COEFFICIENTS[2]); // Eq (A5) - double logL = std::log10(CalculateLuminosityShikauchi(mixingCoreMass, centralHeliumFraction, 0.0)); + double logL = std::log10(CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore, 0.0)); // Eq (A2) - double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * mixingCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; + double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * m_MainSequenceCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; // Eq (A7) double g = -0.0044 * m_MZAMS + 0.27; // Eq (10) - double Yhat = (centralHeliumFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity); + double Yhat = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity); // Eq (A6) double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); - double currentTimestepInYrs = p_Dt * MYR_TO_YEAR; - double mDot = m_TotalMassLossRate; // Positive for mass gain, negative for mass loss - // Use boost adaptive ODE solver controlled_stepper_type controlled_stepper; state_type x(2); - x[0] = centralHeliumFraction; - x[1] = lnMixingCoreMass; + x[0] = m_HeliumAbundanceCore; + x[1] = std::log(m_MainSequenceCoreMass); auto ode = [&](const state_type &x, state_type &dxdt, const double) { // Eq (13) - dxdt[0] = PPOW(10.0, logL) / (Q_CNO * mixingCoreMass); + dxdt[0] = PPOW(10.0, logL) / (Q_CNO * std::exp(x[1])); // Eq (12) - dxdt[1] = - alpha / (1.0 - alpha * centralHeliumFraction) * dxdt[0] + beta * delta * mDot / totalMass; + dxdt[1] = - alpha / (1.0 - alpha * x[0]) * dxdt[0] + beta * delta * m_TotalMassLossRate / (YEAR_TO_MYR * m_Mass); }; - integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); + integrate_adaptive(controlled_stepper, ode, x, 0.0, p_Dt, p_Dt); double newMixingCoreMass = std::exp(x[1]); // New mixing core mass double newCentralHeliumFraction = x[0]; // New central helium fraction - double deltaCoreMass = newMixingCoreMass - mixingCoreMass; // Difference in core mass + double deltaCoreMass = newMixingCoreMass - m_MainSequenceCoreMass; // Difference in core mass if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation - if (newMixingCoreMass < m_InitialMainSequenceCoreMass) { // New core mass less than initial core mass? + if (utils::Compare(newMixingCoreMass, m_InitialMainSequenceCoreMass) < 0) { // New core mass less than initial core mass? // Use boost adaptive ODE solver state_type x(1); - x[0] = heliumFractionOut; + x[0] = m_HeliumAbundanceCoreOut; auto ode = [&](const state_type &x, state_type &dxdt, const double) { // Calculate the change in helium abundance just outside the core, assuming linear profile - dxdt[0] = (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * (deltaCoreMass / currentTimestepInYrs); + dxdt[0] = (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * (deltaCoreMass / p_Dt); }; - integrate_adaptive(controlled_stepper, ode, x, 0.0, currentTimestepInYrs, currentTimestepInYrs/100.0); + integrate_adaptive(controlled_stepper, ode, x, 0.0, p_Dt, p_Dt); // Calculate the change in helium abundance in the core, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 - double deltaY = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; - newCentralHeliumFraction = centralHeliumFraction + deltaY; + double deltaY = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / (m_MainSequenceCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (m_MainSequenceCoreMass + deltaCoreMass) * (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; + newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; m_HeliumAbundanceCoreOut = x[0]; } else { // New core mass greater or equal to the initial core mass? - double firstTerm = (heliumFractionOut - centralHeliumFraction) / (mixingCoreMass + deltaCoreMass) * deltaCoreMass; + double firstTerm = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / (m_MainSequenceCoreMass + deltaCoreMass) * deltaCoreMass; // Second term is set to zero if the initial core mass had been previously exceeded - double secondTerm = (m_InitialMainSequenceCoreMass != CalculateInitialMainSequenceCoreMass(m_MZAMS)) ? 0.0 : 0.5 / (mixingCoreMass + deltaCoreMass) * (heliumFractionOut - m_InitialHeliumAbundance) / (mixingCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; + double secondTerm = (utils::Compare(m_InitialMainSequenceCoreMass, CalculateInitialMainSequenceCoreMass(m_MZAMS)) != 0) ? 0.0 : 0.5 / (m_MainSequenceCoreMass + deltaCoreMass) * (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; double deltaY = firstTerm + secondTerm; // Change in helium abundance - newCentralHeliumFraction = centralHeliumFraction + deltaY; + newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; m_InitialMainSequenceCoreMass = newMixingCoreMass; } @@ -874,31 +867,29 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ m_MainSequenceCoreMass = 0.0; break; } - case CORE_MASS_PRESCRIPTION::MANDEL: { // Calculate the minimum core mass of a main sequence star that loses mass through Case A mass transfer as the core mass of a TAMS star, scaled by the fractional age. - if ((p_Dt != 0.0) && (p_TotalMassLossRate != -m_Mdot) && (p_TotalMassLossRate < 0.0)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE and only applied to donors - m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass());; + case CORE_MASS_PRESCRIPTION::MANDEL: { // Calculate the minimum core mass of a main sequence star that loses mass through Case A mass transfer as the core mass of a TAMS star, scaled by the fractional age. + if ((utils::Compare(p_Dt, 0.0) != 0) && (utils::Compare(p_TotalMassLossRate, -m_Mdot) != 0) && (p_TotalMassLossRate < 0.0)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE and only applied to donors + m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); break; } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi et al. (2024) - if ((p_Dt == 0.0) || (p_TotalMassLossRate != m_TotalMassLossRate)) // Do not proceed with updating the core mass if time does not advance or calculation was done in binary evolution (total mass loss rate had been updated) + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi et al. (2024) + if ((utils::Compare(p_Dt, 0.0) == 0) || (utils::Compare(p_TotalMassLossRate, m_TotalMassLossRate) != 0)) // Do not proceed with updating the core mass if time does not advance or calculation was done in binary evolution (total mass loss rate had been updated) return; - double mixingCoreMass; double centralHeliumFraction; std::tuple ShikauchiSolution; double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - if ((m_MZAMS >= 10.0) && (m_HeliumAbundanceCore < 1.0 - m_Metallicity)) { // MZAMS >= 10 Msol (SHIKAUCHI prescription) and no calculations past the MS hook (Yc = 1 - Z) + if ((utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0) && (utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) < 0)) { // MZAMS >= 15 Msol (SHIKAUCHI prescription) and no calculations past the MS hook (Yc = 1 - Z) ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(p_Dt); - mixingCoreMass = std::get<0>(ShikauchiSolution); + m_MainSequenceCoreMass = std::get<0>(ShikauchiSolution); // Update the core mass centralHeliumFraction = std::get<1>(ShikauchiSolution); - m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance - m_MainSequenceCoreMass = mixingCoreMass; // Update the core mass - m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age based on central helium fraction + m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance + m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age based on central helium fraction } - else if ((p_TotalMassLossRate < 0.0) && (m_MZAMS < 10.0) && (p_TotalMassLossRate != -m_Mdot)) { // Mass loss and MZAMS < 10 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer -- does not take into account mass loss from winds) - m_MainSequenceCoreMass = CalculateMainSequenceCoreMassMandel(); + else if ((p_TotalMassLossRate < 0.0) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) < 0) && (utils::Compare(p_TotalMassLossRate, -m_Mdot) != 0)) { // Mass loss and MZAMS < 15 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer -- does not take into account mass loss from winds) + m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); } - else { }; // Other cases + else { }; // Other cases break; } } @@ -1123,7 +1114,7 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { m_HydrogenAbundanceCore = 1.0 - m_Metallicity - m_HeliumAbundanceCore; - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (p_Mass >= 10.0)) { + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { m_InitialMainSequenceCoreMass = CalculateInitialMainSequenceCoreMass(p_Mass); // update initial mixing core mass m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; } @@ -1320,29 +1311,29 @@ std::tuple MainSequence::InterpolateShikauc double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); double high = std::log10(ZSOL_ASPLUND); - if (logZ <= low) // Linear extrapolation (constant) for metallicity lower than the lowest bound + if (utils::Compare(logZ, low) <= 0) // Linear extrapolation (constant) for metallicity lower than the lowest bound return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); - else if ((logZ > low) && (logZ <= middle)) { // Linear interpolation between metallicity low and middle - for (int i = 0; i < 3; i++) + else if ((utils::Compare(logZ, low) > 0) && (utils::Compare(logZ, middle) <= 0)) { // Linear interpolation between metallicity low and middle + for (size_t i = 0; i < 3; i++) { alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - for (int i = 0; i < 3; i++) fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - for (int i = 0; i < 10; i++) + } + for (size_t i = 0; i < 10; i++) lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } - else if ((logZ > middle) && (logZ < high)) { // Linear interpolation between metallicity middle and high - for (int i = 0; i < 3; i++) + else if ((utils::Compare(logZ, middle) > 0) && (utils::Compare(logZ, high) < 0)) { // Linear interpolation between metallicity middle and high + for (size_t i = 0; i < 3; i++) { alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - for (int i = 0; i < 3; i++) fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - for (int i = 0; i < 10; i++) + } + for (size_t i = 0; i < 10; i++) lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } - else // Linear extrapolation (constant) for metallicity equal to solar or higher + else // Linear extrapolation (constant) for metallicity equal to solar or higher return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); } diff --git a/src/constants.h b/src/constants.h index ae5d7b3b5..dbe470705 100755 --- a/src/constants.h +++ b/src/constants.h @@ -290,7 +290,7 @@ constexpr double FARMER_PPISN_UPP_LIM_QUAD_REGIME = 60.0; constexpr double FARMER_PPISN_UPP_LIM_INSTABILLITY = 140.0; // Maximum CO core mass to result in PI (upper edge of PISN gap) from FARMER PPISN prescription constexpr double STARTRACK_PPISN_HE_CORE_MASS = 45.0; // Helium core mass remaining following PPISN as assumed in StarTrack (Belczynski et al. 2017 https://arxiv.org/abs/1607.03116) -constexpr double Q_CNO = 9.9073E10; // Energy released per unit mass by hydrogen fusion via the CNO cycle in Lsol Msol-1 +constexpr double Q_CNO = 9.9073E4; // Energy released per unit mass by hydrogen fusion via the CNO cycle in Lsol Myr Msol-1 // logging constants @@ -3733,6 +3733,11 @@ const std::vector>> LOVERIDGE_COE } }; + +// Initial mass of stars above which (including the limit) we allow +// convective core mass calculations from Shikauchi et al. (2024) +constexpr double SHIKAUCHI_LOWER_MASS_LIMIT = 15.0; + // Coefficients for determining Main Sequence core mass // from Shikauchi et al. (2024), https://arxiv.org/abs/2409.00460 // Table 2 From 536615aa878423b06232a0ab0231ed40ce7a7945 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Thu, 9 Jan 2025 11:09:00 +1100 Subject: [PATCH 21/25] Fixes based on comments --- .../preprocessing/compasConfigDefault.yaml | 1 + .../program-options-list-defaults.rst | 9 +- src/BaseBinaryStar.cpp | 4 +- src/MainSequence.cpp | 160 +++++++++--------- src/MainSequence.h | 6 +- src/Options.cpp | 8 +- src/Options.h | 26 +-- src/yaml.h | 1 + 8 files changed, 117 insertions(+), 98 deletions(-) diff --git a/compas_python_utils/preprocessing/compasConfigDefault.yaml b/compas_python_utils/preprocessing/compasConfigDefault.yaml index 4c5a536d8..d6d2b7143 100644 --- a/compas_python_utils/preprocessing/compasConfigDefault.yaml +++ b/compas_python_utils/preprocessing/compasConfigDefault.yaml @@ -43,6 +43,7 @@ booleanChoices: # --hmxr-binaries: False # Default: False # --mass-transfer: True # Default: True # --use-mass-transfer: True # Default: True +# --retain-core-mass-during-caseA-mass-transfer: True # Default: True ### COMMON ENVELOPE # --common-envelope-allow-immediate-RLOF-post-CE-survive: False # Default: False diff --git a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst index 5fab7a6f5..625c25782 100644 --- a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst +++ b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst @@ -1166,6 +1166,13 @@ Remnant mass prescription. |br| Options: { HURLEY2000, BELCZYNSKI2002, FRYER2012, FRYER2022, MULLER2016, MULLERMANDEL, SCHNEIDER2020, SCHNEIDER2020ALT, MALTSEV2024 } |br| Default = MULLERMANDEL +**--retain-core-mass-during-caseA-mass-transfer** |br| +If TRUE, preserve a larger donor core mass following case A mass transfer. |br| +The core is set equal to the expected core mass of a newly formed HG star with mass equal to that of the donor, +scaled by the fraction of the donor's MS lifetime at mass transfer. |br| +Default = TRUE |br| +DEPRECATION NOTICE: this option has been deprecated and will soon be removed. Please use ``--main-sequence-core-mass-prescription MANDEL`` in future. + **--revised-energy-formalism-nandez-ivanova** |br| Enable revised energy formalism of Nandez & Ivanova. |br| Default = FALSE @@ -1441,7 +1448,7 @@ Go to :ref:`the top of this page ` for the full alphabetical --critical-mass-ratio-helium-giant-degenerate-accretor, --critical-mass-ratio-helium-giant-non-degenerate-accretor, --critical-mass-ratio-white-dwarf-degenerate-accretor, --critical-mass-ratio-white-dwarf-non-degenerate-accretor, --eddington-accretion-factor, --mass-transfer, --use-mass-transfer, --mass-transfer-accretion-efficiency-prescription, --mass-transfer-angular-momentum-loss-prescription, --mass-transfer-fa, --mass-transfer-jloss, --mass-transfer-jloss-macleod-linear-fraction-degen, --mass-transfer-jloss-macleod-linear-fraction-non-degen, ---mass-transfer-rejuvenation-prescription, --mass-transfer-thermal-limit-accretor, --mass-transfer-thermal-limit-accretor-multiplier, --mass-transfer-thermal-limit-C, +--mass-transfer-rejuvenation-prescription, --mass-transfer-thermal-limit-accretor, --mass-transfer-thermal-limit-accretor-multiplier, --mass-transfer-thermal-limit-C, --retain-core-mass-during-caseA-mass-transfer, --stellar-zeta-prescription, --zeta-adiabatic-arbitrary, --zeta-main-sequence, --zeta-radiative-giant-star --circulariseBinaryDuringMassTransfer, --angular-momentum-conservation-during-circularisation, --tides-prescription diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index 7a4bab9bb..e7ee751f2 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -2089,7 +2089,7 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { else { // have required mass loss massDiffDonor = -massDiffDonor; // set mass difference m_Donor->UpdateTotalMassLossRate(massDiffDonor / (p_Dt * MYR_TO_YEAR)); // update mass loss rate for MS donor - m_Donor->UpdateMainSequenceCoreMass(p_Dt, m_Donor->TotalMassLossRate()); // update core mass for MS donor + m_Donor->UpdateMainSequenceCoreMass(p_Dt, massDiffDonor / (p_Dt * MYR_TO_YEAR)); // update core mass for MS donor } } @@ -2101,7 +2101,7 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { double omegaDonor_pre_MT = m_Donor->Omega(); // used if full donor envelope is removed m_Accretor->UpdateTotalMassLossRate(massGainAccretor / (p_Dt * MYR_TO_YEAR)); // update mass gain rate for MS accretor - m_Accretor->UpdateMainSequenceCoreMass(p_Dt, m_Accretor->TotalMassLossRate()); // update core mass for MS accretor + m_Accretor->UpdateMainSequenceCoreMass(p_Dt, massGainAccretor / (p_Dt * MYR_TO_YEAR)); // update core mass for MS accretor m_Donor->SetMassTransferDiffAndResolveWDShellChange(massDiffDonor); // set new mass of donor m_Accretor->SetMassTransferDiffAndResolveWDShellChange(massGainAccretor); // set new mass of accretor diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index d74e1879a..7815b938b 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -341,25 +341,24 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl * @return Luminosity on the Main Sequence as a function of current core mass and central helium fraction */ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const { - DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); - double logMixingCoreMass = std::log10(p_CoreMass); + DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); + double logMixingCoreMass = std::log10(p_CoreMass); + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * p_HeliumAbundanceCore + L_COEFFICIENTS[2] * logMixingCoreMass * p_HeliumAbundanceCore + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * p_HeliumAbundanceCore * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * p_HeliumAbundanceCore + L_COEFFICIENTS[8] * logMixingCoreMass * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[9]; double luminosity = PPOW(10.0, logL); - if ((utils::Compare(p_HeliumAbundanceCore, 1.0 - m_Metallicity) == 0) && (utils::Compare(m_TotalMassLossRate, -m_Mdot) == 0)) { // star in MS hook and mass transfer not ongoing? + if (utils::Compare(p_Age, 0.99 * tMS) >= 0) { // Star in MS hook? HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) delete clone; clone = nullptr; // Return the memory allocated for the clone - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; double ageAtHookStart = 0.99 * tMS; - double luminosityAtHookStart = luminosity; + double luminosityAtHookStart = luminosity; // In the hook, core helium abundance fixed at 1-Z and core mass is not changing - // Linear interpolation - luminosity = (luminosityAtHookStart * (tMS - p_Age) + luminosityTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); + luminosity = (luminosityAtHookStart * (tMS - p_Age) + luminosityTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); // Linear interpolation + } - return luminosity; } @@ -534,7 +533,8 @@ double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_ // If SHIKAUCHI core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { - if ((utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) == 0) && (utils::Compare(m_TotalMassLossRate, -m_Mdot) == 0)) // star in MS hook and mass transfer not ongoing? + double tMS = timescales(tMS); + if (utils::Compare(p_Time, 0.99 * tMS) >= 0) // star in MS hook? return CalculateRadiusTransitionToHG(p_Mass, p_Time, p_RZAMS); } @@ -648,8 +648,7 @@ double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const do double ageAtHookStart = tauAtHookStart * tMS; double radiusAtHookStart = CalculateRadiusOnPhaseTau(p_Mass, tauAtHookStart); - // Linear interpolation - double radius = (radiusAtHookStart * (tMS - p_Age) + radiusTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); + double radius = (radiusAtHookStart * (tMS - p_Age) + radiusTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); // Linear interpolation return radius; } @@ -753,49 +752,48 @@ DBL_DBL MainSequence::CalculateConvectiveEnvelopeMass() const { /* - * Calculate the convective core mass of a main sequence star that loses mass either through winds or - * Case A mass transfer according to Shikauchi et al. (2024) + * Calculate and update the convective core mass and central helium fraction of a main sequence star that loses + * mass either through winds or case A mass transfer according to Shikauchi et al. (2024) * * This function also accounts for mass gain by modeling rejuvenation and updates the initial mixing core mass * and the helium abundance just outside the core * - * double CalculateMainSequenceCoreMassShikauchi(const double p_Dt) + * void CalculateMainSequenceCoreMassShikauchi(const double p_Dt) * * @param [IN] p_Dt Current time step in Myr - * @return Tuple containing the mass of the convective core in Msol and core helium abundance + * @param [IN] p_MassLossRate Mass loss/gain rate in Msol yr-1 (negative for mass loss, positive for mass gain) */ -DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) { +void MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate) { DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); // Eq (A3) - double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); + double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); // Eq (A4) - double beta = 1.0 - FMIX_COEFFICIENTS[1] * m_Mass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-m_Mass / FMIX_COEFFICIENTS[2]); - // Eq (A5) - double logL = std::log10(CalculateLuminosityShikauchi(m_MainSequenceCoreMass, m_HeliumAbundanceCore, 0.0)); + auto beta = [&](double mass) { return 1.0 - FMIX_COEFFICIENTS[1] * mass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-mass / FMIX_COEFFICIENTS[2]); }; // Eq (A2) - double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * m_MainSequenceCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; + auto alpha = [&](double coreMass) { return PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * coreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; }; // Eq (A7) - double g = -0.0044 * m_MZAMS + 0.27; - // Eq (10) - double Yhat = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity); + double g = -0.0044 * m_MZAMS + 0.27; // Eq (A6) - double delta = std::min(PPOW(10.0, -Yhat + g), 1.0); + auto delta = [&](double centralHeFraction) { return std::min(PPOW(10.0, -(centralHeFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity) + g), 1.0); }; // Use boost adaptive ODE solver controlled_stepper_type controlled_stepper; - state_type x(2); + state_type x(3); x[0] = m_HeliumAbundanceCore; x[1] = std::log(m_MainSequenceCoreMass); + x[2] = m_Mass; auto ode = [&](const state_type &x, state_type &dxdt, const double) { // Eq (13) - dxdt[0] = PPOW(10.0, logL) / (Q_CNO * std::exp(x[1])); + dxdt[0] = CalculateLuminosityShikauchi(std::exp(x[1]), x[0], 0.0) / (Q_CNO * std::exp(x[1])); // Eq (12) - dxdt[1] = - alpha / (1.0 - alpha * x[0]) * dxdt[0] + beta * delta * m_TotalMassLossRate / (YEAR_TO_MYR * m_Mass); + dxdt[1] = - alpha(std::exp(x[1])) / (1.0 - alpha(std::exp(x[1])) * x[0]) * dxdt[0] + beta(x[2]) * delta(x[0]) * p_MassLossRate / (YEAR_TO_MYR * x[2]); + // Mass loss/gain rate + dxdt[2] = p_MassLossRate; }; - integrate_adaptive(controlled_stepper, ode, x, 0.0, p_Dt, p_Dt); + integrate_adaptive(controlled_stepper, ode, x, 0.0, p_Dt, p_Dt/100.0); double newMixingCoreMass = std::exp(x[1]); // New mixing core mass double newCentralHeliumFraction = x[0]; // New central helium fraction @@ -803,27 +801,17 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation if (utils::Compare(newMixingCoreMass, m_InitialMainSequenceCoreMass) < 0) { // New core mass less than initial core mass? - // Use boost adaptive ODE solver - state_type x(1); - x[0] = m_HeliumAbundanceCoreOut; - auto ode = [&](const state_type &x, state_type &dxdt, const double) { - // Calculate the change in helium abundance just outside the core, assuming linear profile - dxdt[0] = (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * (deltaCoreMass / p_Dt); - }; - integrate_adaptive(controlled_stepper, ode, x, 0.0, p_Dt, p_Dt); - - // Calculate the change in helium abundance in the core, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 - double deltaY = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / (m_MainSequenceCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (m_MainSequenceCoreMass + deltaCoreMass) * (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; + // Calculate the change in helium abundance just outside the core + double deltaYout = (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass; + // Calculate the change in core helium abundance, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 + double deltaY = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / (m_MainSequenceCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (m_MainSequenceCoreMass + deltaCoreMass) * (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; - m_HeliumAbundanceCoreOut = x[0]; + m_HeliumAbundanceCoreOut = m_HeliumAbundanceCoreOut + deltaYout; } else { // New core mass greater or equal to the initial core mass? - double firstTerm = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / (m_MainSequenceCoreMass + deltaCoreMass) * deltaCoreMass; - // Second term is set to zero if the initial core mass had been previously exceeded - double secondTerm = (utils::Compare(m_InitialMainSequenceCoreMass, CalculateInitialMainSequenceCoreMass(m_MZAMS)) != 0) ? 0.0 : 0.5 / (m_MainSequenceCoreMass + deltaCoreMass) * (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; - - double deltaY = firstTerm + secondTerm; // Change in helium abundance - newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; + double deltaCoreMass1 = m_InitialMainSequenceCoreMass - m_MainSequenceCoreMass; // Mass accreted up to the initial core mass + double deltaCoreMass2 = deltaCoreMass - deltaCoreMass1; // Remaining accreted mass + newCentralHeliumFraction = (m_MainSequenceCoreMass * m_HeliumAbundanceCore + 0.5 * (m_HeliumAbundanceCoreOut + m_InitialHeliumAbundance) * deltaCoreMass1 + deltaCoreMass2 * m_InitialHeliumAbundance) / (m_MainSequenceCoreMass + deltaCoreMass); m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; m_InitialMainSequenceCoreMass = newMixingCoreMass; } @@ -831,7 +819,8 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt) else m_HeliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c - return std::tuple (newMixingCoreMass, newCentralHeliumFraction); + m_MainSequenceCoreMass = newMixingCoreMass; // Update core mass + m_HeliumAbundanceCore = std::min(newCentralHeliumFraction, 1.0 - m_Metallicity); // Update core helium abundance } @@ -856,41 +845,43 @@ double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_MZAMS) * When Shikauchi et al. (2024) core prescription is used, also update the core helium abundance and effective age * * - * void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) + * void UpdateMainSequenceCoreMass(const double p_Dt, const double p_MassLossRate) * - * @param [IN] p_Dt Current timestep - * @param [IN] p_TotalMassLossRate Mass loss rate either from stellar winds or mass transfer (Msol/yr) + * @param [IN] p_Dt Current timestep in Myr + * @param [IN] p_MassLossRate Mass loss rate either from stellar winds or mass transfer in Msol yr-1 */ -void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { +void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_MassLossRate) { switch (OPTIONS->MainSequenceCoreMassPrescription()) { case CORE_MASS_PRESCRIPTION::NONE: { m_MainSequenceCoreMass = 0.0; break; } - case CORE_MASS_PRESCRIPTION::MANDEL: { // Calculate the minimum core mass of a main sequence star that loses mass through Case A mass transfer as the core mass of a TAMS star, scaled by the fractional age. - if ((utils::Compare(p_Dt, 0.0) != 0) && (utils::Compare(p_TotalMassLossRate, -m_Mdot) != 0) && (p_TotalMassLossRate < 0.0)) // Only update core mass if total mass loss rate was updated in binary evolution, not applied to SSE and only applied to donors + case CORE_MASS_PRESCRIPTION::MANDEL: { + // Calculate the minimum core mass of a main sequence star that loses mass through Case A mass transfer as the core mass of a TAMS star, scaled by the fractional age. + // Only applied to donors as part of binary evolution, not applied to SSE + if ((OPTIONS->RetainCoreMassDuringCaseAMassTransfer()) && (p_MassLossRate < 0.0) && (utils::Compare(p_MassLossRate, -m_Mdot) != 0)) m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); break; } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { // Set core mass following Shikauchi et al. (2024) - if ((utils::Compare(p_Dt, 0.0) == 0) || (utils::Compare(p_TotalMassLossRate, m_TotalMassLossRate) != 0)) // Do not proceed with updating the core mass if time does not advance or calculation was done in binary evolution (total mass loss rate had been updated) - return; - double centralHeliumFraction; - std::tuple ShikauchiSolution; - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - - if ((utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0) && (utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) < 0)) { // MZAMS >= 15 Msol (SHIKAUCHI prescription) and no calculations past the MS hook (Yc = 1 - Z) - ShikauchiSolution = CalculateMainSequenceCoreMassShikauchi(p_Dt); - m_MainSequenceCoreMass = std::get<0>(ShikauchiSolution); // Update the core mass - centralHeliumFraction = std::get<1>(ShikauchiSolution); - m_HeliumAbundanceCore = std::min(centralHeliumFraction, 1.0 - m_Metallicity); // Update the core helium abundance - m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // Update the effective age based on central helium fraction + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + // Set core mass following Shikauchi et al. (2024) + // MZAMS greater than the limit? SHIKAUCHI prescription valid + if (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0) { + // Only proceed with calculations if star is not in MS hook (Yc < 1-Z), time step is not zero, and when the mass loss rate argument is equal to the total mass loss rate (i.e. total mass loss rate was updated, this prevents the calculation in SSE if it was executed as part of BSE for the same time step) + if ((utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) < 0) && (utils::Compare(p_Dt, 0.0) != 0) && (utils::Compare(p_MassLossRate, m_TotalMassLossRate) == 0)) { + // Calculate and update the core mass and central helium fraction + CalculateMainSequenceCoreMassShikauchi(p_Dt, p_MassLossRate); + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + // Update the effective age based on central helium fraction + m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; + } } - else if ((p_TotalMassLossRate < 0.0) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) < 0) && (utils::Compare(p_TotalMassLossRate, -m_Mdot) != 0)) { // Mass loss and MZAMS < 15 Msol (Mandel core prescription used, which retains higher core mass after case A mass transfer -- does not take into account mass loss from winds) - m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); + // MZAMS less than the limit? MANDEL prescription used + else { + // Only applied to donors as part of binary evolution, not applied to SSE + if ((p_MassLossRate < 0.0) && (utils::Compare(p_MassLossRate, -m_Mdot) != 0)) + m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); } - else { }; // Other cases - break; } } } @@ -1311,29 +1302,36 @@ std::tuple MainSequence::InterpolateShikauc double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); double high = std::log10(ZSOL_ASPLUND); - if (utils::Compare(logZ, low) <= 0) // Linear extrapolation (constant) for metallicity lower than the lowest bound - return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[0], SHIKAUCHI_FMIX_COEFFICIENTS[0], SHIKAUCHI_L_COEFFICIENTS[0]); - - else if ((utils::Compare(logZ, low) > 0) && (utils::Compare(logZ, middle) <= 0)) { // Linear interpolation between metallicity low and middle + // Linear extrapolation (constant) for metallicity lower than the lowest bound + if (utils::Compare(logZ, low) <= 0) { + alphaCoeff = SHIKAUCHI_ALPHA_COEFFICIENTS[0]; + fmixCoeff = SHIKAUCHI_FMIX_COEFFICIENTS[0]; + lCoeff = SHIKAUCHI_L_COEFFICIENTS[0]; + } + // Linear interpolation between metallicity low and middle + else if ((utils::Compare(logZ, low) > 0) && (utils::Compare(logZ, middle) <= 0)) { for (size_t i = 0; i < 3; i++) { alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); } for (size_t i = 0; i < 10; i++) lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } - - else if ((utils::Compare(logZ, middle) > 0) && (utils::Compare(logZ, high) < 0)) { // Linear interpolation between metallicity middle and high + // Linear interpolation between metallicity middle and high + else if ((utils::Compare(logZ, middle) > 0) && (utils::Compare(logZ, high) < 0)) { for (size_t i = 0; i < 3; i++) { alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); } for (size_t i = 0; i < 10; i++) lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + } + // Linear extrapolation (constant) for metallicity equal to solar or higher + else { + alphaCoeff = SHIKAUCHI_ALPHA_COEFFICIENTS[2]; + fmixCoeff = SHIKAUCHI_FMIX_COEFFICIENTS[2]; + lCoeff = SHIKAUCHI_L_COEFFICIENTS[2]; } - else // Linear extrapolation (constant) for metallicity equal to solar or higher - return std::tuple (SHIKAUCHI_ALPHA_COEFFICIENTS[2], SHIKAUCHI_FMIX_COEFFICIENTS[2], SHIKAUCHI_L_COEFFICIENTS[2]); + return std::tuple (alphaCoeff, fmixCoeff, lCoeff); } diff --git a/src/MainSequence.h b/src/MainSequence.h index be6ce2d57..541919016 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -47,7 +47,7 @@ class MainSequence: virtual public BaseStar { double CalculateCOCoreMassAtPhaseEnd() const { return CalculateCOCoreMassOnPhase(); } // Same as on phase double CalculateCOCoreMassOnPhase() const { return 0.0; } // McCO(MS) = 0.0 - double CalculateCoreMassAtPhaseEnd() const { return 0.0; } // Same as on phase + double CalculateCoreMassAtPhaseEnd() const { return (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::MANDEL) ? MainSequenceCoreMass() : 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass @@ -74,7 +74,7 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const; - DBL_DBL CalculateMainSequenceCoreMassShikauchi(const double p_Dt); + void CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate); double CalculateInitialMainSequenceCoreMass(const double p_MZAMS) const; double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 @@ -124,7 +124,7 @@ class MainSequence: virtual public BaseStar { void UpdateAgeAfterMassLoss(); // Per Hurley et al. 2000, section 7.1 - void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate); + void UpdateMainSequenceCoreMass(const double p_Dt, const double p_MassLossRate); }; diff --git a/src/Options.cpp b/src/Options.cpp index a15e7acc7..c53199418 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -416,6 +416,7 @@ void Options::OptionValues::Initialise() { m_UseMassTransfer = true; m_CirculariseBinaryDuringMassTransfer = true; m_AngularMomentumConservationDuringCircularisation = false; + m_RetainCoreMassDuringCaseAMassTransfer = true; m_ConvectiveEnvelopeTemperatureThreshold = CONVECTIVE_BOUNDARY_TEMPERATURE_BELCZYNSKI; // Case BB/BC mass transfer stability prescription @@ -889,7 +890,12 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt po::value(&p_Options->m_Quiet)->default_value(p_Options->m_Quiet)->implicit_value(true), ("Suppress printing (default = " + std::string(p_Options->m_Quiet ? "TRUE" : "FALSE") + ")").c_str() ) - + + ( + "retain-core-mass-during-caseA-mass-transfer", + po::value(&p_Options->m_RetainCoreMassDuringCaseAMassTransfer)->default_value(p_Options->m_RetainCoreMassDuringCaseAMassTransfer)->implicit_value(true), + ("Retain approximate core mass of a case A donor as a minimum core at end of MS or HeMS (default = " + std::string(p_Options->m_RetainCoreMassDuringCaseAMassTransfer ? "TRUE" : "FALSE") + ")").c_str() + ) ( "revised-energy-formalism-nandez-ivanova", po::value(&p_Options->m_RevisedEnergyFormalismNandezIvanova)->default_value(p_Options->m_RevisedEnergyFormalismNandezIvanova)->implicit_value(true), diff --git a/src/Options.h b/src/Options.h index cfd9353d3..495c7ea62 100755 --- a/src/Options.h +++ b/src/Options.h @@ -211,16 +211,17 @@ class Options { // is only shown once per COMPAS run). std::vector> deprecatedOptionStrings = { - { "black-hole-kicks", "black-hole-kicks-mode", false }, - { "chemically-homogeneous-evolution", "chemically-homogeneous-evolution-mode", false }, - { "kick-direction", "kick-direction-distribution", false }, - { "luminous-blue-variable-prescription", "LBV-mass-loss-prescription", false }, - { "mass-transfer", "use-mass-transfer", false }, - { "mass-transfer-thermal-limit-accretor", "mass-transfer-thermal-limit-accretor-multiplier", false }, - { "OB-mass-loss", "OB-mass-loss-prescription", false }, - { "RSG-mass-loss", "RSG-mass-loss-prescription", false }, - { "VMS-mass-loss", "VMS-mass-loss-prescription", false }, - { "WR-mass-loss", "WR-mass-loss-prescription", false } + { "black-hole-kicks", "black-hole-kicks-mode", false }, + { "chemically-homogeneous-evolution", "chemically-homogeneous-evolution-mode", false }, + { "kick-direction", "kick-direction-distribution", false }, + { "luminous-blue-variable-prescription", "LBV-mass-loss-prescription", false }, + { "mass-transfer", "use-mass-transfer", false }, + { "mass-transfer-thermal-limit-accretor", "mass-transfer-thermal-limit-accretor-multiplier", false }, + { "OB-mass-loss", "OB-mass-loss-prescription", false }, + { "retain-core-mass-during-caseA-mass-transfer", "", false }, + { "RSG-mass-loss", "RSG-mass-loss-prescription", false }, + { "VMS-mass-loss", "VMS-mass-loss-prescription", false }, + { "WR-mass-loss", "WR-mass-loss-prescription", false } }; std::vector> deprecatedOptionValues = { @@ -992,6 +993,8 @@ class Options { bool m_ExpelConvectiveEnvelopeAboveLuminosityThreshold; // Whether to expel the convective envelope in a pulsation when log_10(L/M) reaches the threshold defined by m_LuminosityToMassThreshold double m_LuminosityToMassThreshold; // Threshold value of log_10(L/M) above which the convective envelope is expelled in a pulsation + + bool m_RetainCoreMassDuringCaseAMassTransfer; // Whether to retain the approximate core mass of a case A donor as a minimum core at end of MS or HeMS (default = false) ENUM_OPT m_MainSequenceCoreMassPrescription; // Which MS core prescription @@ -1621,6 +1624,9 @@ class Options { bool RequestedHelp() const { return m_CmdLine.optionValues.m_VM["help"].as(); } bool RequestedVersion() const { return m_CmdLine.optionValues.m_VM["version"].as(); } + + bool RetainCoreMassDuringCaseAMassTransfer() const { return m_CmdLine.optionValues.m_RetainCoreMassDuringCaseAMassTransfer; } + bool RLOFPrinting() const { return m_CmdLine.optionValues.m_RlofPrinting; } double RocketKickMagnitude1() const { return OPT_VALUE("rocket-kick-magnitude-1", m_RocketKickMagnitude1, true); } diff --git a/src/yaml.h b/src/yaml.h index 245ce7f93..ffacfd476 100644 --- a/src/yaml.h +++ b/src/yaml.h @@ -108,6 +108,7 @@ namespace yaml { " --circularise-binary-during-mass-transfer", " --hmxr-binaries", " --use-mass-transfer", + " --retain-core-mass-during-caseA-mass-transfer", "", " ### COMMON ENVELOPE", " --common-envelope-allow-immediate-RLOF-post-CE-survive", From f44666c4f06d3656fe7e5407861270cbd325ecfd Mon Sep 17 00:00:00 2001 From: Jeff Riley Date: Tue, 14 Jan 2025 10:50:48 +1100 Subject: [PATCH 22/25] Code cleanup; a few minor changes --- src/BaseBinaryStar.cpp | 25 ++--- src/BaseStar.h | 8 +- src/HG.cpp | 14 +-- src/HG.h | 4 +- src/MainSequence.cpp | 219 +++++++++++++++++++++++------------------ src/MainSequence.h | 7 +- src/constants.h | 36 +++---- src/typedefs.h | 6 +- 8 files changed, 169 insertions(+), 150 deletions(-) diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index e7ee751f2..f53d26483 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -1726,19 +1726,23 @@ void BaseBinaryStar::ResolveMainSequenceMerger() { double finalMass = (1.0 - phi) * (mass1 + mass2); double initialHydrogenFraction = m_Star1->InitialHydrogenAbundance(); - double finalHydrogenMass = finalMass * initialHydrogenFraction - tau1 * TAMSCoreMass1 * initialHydrogenFraction - tau2 * TAMSCoreMass2 * initialHydrogenFraction; - m_SemiMajorAxis = std::numeric_limits::infinity(); // set separation to infinity to avoid subsequent fake interactions with a massless companion (RLOF, CE, etc.) - - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_Star1->MZAMS(), SHIKAUCHI_LOWER_MASS_LIMIT) >= 0) && (utils::Compare(m_Star2->MZAMS(), SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { - double coreMass1 = m_Star1->MainSequenceCoreMass(); - double coreMass2 = m_Star2->MainSequenceCoreMass(); + double finalHydrogenMass; + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && + (utils::Compare(m_Star1->MZAMS(), SHIKAUCHI_LOWER_MASS_LIMIT) >= 0) && + (utils::Compare(m_Star2->MZAMS(), SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { + + double coreMass1 = m_Star1->MainSequenceCoreMass(); + double coreMass2 = m_Star2->MainSequenceCoreMass(); double coreHeliumMass1 = m_Star1->HeliumAbundanceCore() * coreMass1; double coreHeliumMass2 = m_Star2->HeliumAbundanceCore() * coreMass2; - finalHydrogenMass = finalMass * initialHydrogenFraction - coreHeliumMass1 - coreHeliumMass2; + finalHydrogenMass = finalMass * initialHydrogenFraction - coreHeliumMass1 - coreHeliumMass2; } + else finalHydrogenMass = finalMass * initialHydrogenFraction - tau1 * TAMSCoreMass1 * initialHydrogenFraction - tau2 * TAMSCoreMass2 * initialHydrogenFraction; + + m_SemiMajorAxis = std::numeric_limits::infinity(); // set separation to infinity to avoid subsequent fake interactions with a massless companion (RLOF, CE, etc.) m_Star1->UpdateAfterMerger(finalMass, finalHydrogenMass); @@ -2095,9 +2099,9 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { if (!m_CEDetails.CEEnow) { // CE flagged? // no - m_MassTransferTrackerHistory = m_Donor == m_Star1 ? MT_TRACKING::STABLE_1_TO_2_SURV : MT_TRACKING::STABLE_2_TO_1_SURV; // record what happened - for later printing - double massGainAccretor = -massDiffDonor * m_FractionAccreted; // set accretor mass gain to mass loss * conservativeness - + m_MassTransferTrackerHistory = m_Donor == m_Star1 ? MT_TRACKING::STABLE_1_TO_2_SURV : MT_TRACKING::STABLE_2_TO_1_SURV; // record what happened - for later printing + + double massGainAccretor = -massDiffDonor * m_FractionAccreted; // set accretor mass gain to mass loss * conservativeness double omegaDonor_pre_MT = m_Donor->Omega(); // used if full donor envelope is removed m_Accretor->UpdateTotalMassLossRate(massGainAccretor / (p_Dt * MYR_TO_YEAR)); // update mass gain rate for MS accretor @@ -2107,7 +2111,6 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { m_Accretor->SetMassTransferDiffAndResolveWDShellChange(massGainAccretor); // set new mass of accretor aFinal = CalculateMassTransferOrbit(m_Donor->Mass(), massDiffDonor, *m_Accretor, m_FractionAccreted); // calculate new orbit - m_aMassTransferDiff = aFinal - aInitial; // set change in orbit (semi-major axis) STELLAR_TYPE stellarTypeDonor = m_Donor->StellarType(); // donor stellar type before resolving envelope loss diff --git a/src/BaseStar.h b/src/BaseStar.h index decf4bfb1..5607bdaea 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -213,7 +213,7 @@ class BaseStar { // member functions - alphabetically - void ApplyMassTransferRejuvenationFactor() { m_Age *= CalculateMassTransferRejuvenationFactor(); } // Apply age rejuvenation factor + void ApplyMassTransferRejuvenationFactor() { m_Age *= CalculateMassTransferRejuvenationFactor(); } // Apply age rejuvenation factor void CalculateBindingEnergies(const double p_CoreMass, const double p_EnvMass, const double p_Radius); @@ -283,7 +283,7 @@ class BaseStar { virtual double CalculateRadiusOnPhaseTau(const double p_Mass, const double p_Tau) const { return 0.0; } // Only defined for MS stars - virtual double CalculateRemnantRadius() const { return Radius(); } // relevant for MS stars, over-written for GB stars + virtual double CalculateRemnantRadius() const { return Radius(); } // Relevant for MS stars, over-written for GB stars void CalculateSNAnomalies(const double p_Eccentricity); @@ -344,7 +344,7 @@ class BaseStar { void StashSupernovaDetails(const STELLAR_TYPE p_StellarType, const SSE_SN_RECORD_TYPE p_RecordType = SSE_SN_RECORD_TYPE::DEFAULT) { LOGGING->StashSSESupernovaDetails(this, p_StellarType, p_RecordType); } - virtual double TAMSCoreMass() const { return 0.0; } // except MS stars + virtual double TAMSCoreMass() const { return 0.0; } // Except MS stars virtual void UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { } // Default is NO-OP virtual void UpdateAgeAfterMassLoss() { } // Default is NO-OP @@ -381,7 +381,7 @@ class BaseStar { } bool PrintSwitchLog() const { - return OPTIONS->SwitchLog() ? (LOGGING->ObjectSwitchingPersistence() == OBJECT_PERSISTENCE::PERMANENT ? LOGGING->LogSSESwitchLog(this) : true) : true; // Write record to SSE Switchlog log file + return OPTIONS->SwitchLog() ? (LOGGING->ObjectSwitchingPersistence() == OBJECT_PERSISTENCE::PERMANENT ? LOGGING->LogSSESwitchLog(this) : true) : true; // Write record to SSE Switchlog log file } bool PrintSystemParameters(const SSE_SYSPARMS_RECORD_TYPE p_RecordType = SSE_SYSPARMS_RECORD_TYPE::DEFAULT) const { diff --git a/src/HG.cpp b/src/HG.cpp index ae1d310fc..892377999 100755 --- a/src/HG.cpp +++ b/src/HG.cpp @@ -817,15 +817,15 @@ double HG::CalculateRadiusOnPhase(const double p_Mass, const double p_Tau, const #define massCutoffs(x) m_MassCutoffs[static_cast(MASS_CUTOFF::x)] // for convenience and readability - undefined at end of function #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function - double RTMS = MainSequence::CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); - - // This ensures continuity of stellar tracks when SHIKAUCHI core mass prescription is used + double RTMS; if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) - RTMS = MainSequence::CalculateRadiusAtPhaseEnd(m_Mass, p_RZAMS); - - double RGB = GiantBranch::CalculateRadiusOnPhase_Static(p_Mass, m_Luminosity, b); + RTMS = MainSequence::CalculateRadiusAtPhaseEnd(m_Mass, p_RZAMS); // ensures continuity of stellar tracks when SHIKAUCHI core mass prescription is used + else + RTMS = MainSequence::CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); + + double RGB = GiantBranch::CalculateRadiusOnPhase_Static(p_Mass, m_Luminosity, b); - double rx = RGB; // Hurley sse terminlogy (rx) + double rx = RGB; // Hurley sse terminlogy (rx) if (utils::Compare(p_Mass, massCutoffs(MFGB)) > 0) { // mass above threshold for He ignition? // yes diff --git a/src/HG.h b/src/HG.h index 2368fbe6c..6c0c12241 100755 --- a/src/HG.h +++ b/src/HG.h @@ -55,8 +55,8 @@ class HG: virtual public BaseStar, public GiantBranch { // update effective "initial" mass (m_Mass0) so that the core mass is at least equal to the minimum core mass but no more than total mass // (only relevant if MANDEL or SHIKAUCHI main sequence core mass prescription is used) if (utils::Compare(CalculateCoreMassOnPhase(m_Mass0, m_Age), std::min(m_Mass, MainSequenceCoreMass())) < 0) { - double desiredCoreMass = std::min(m_Mass, MainSequenceCoreMass()); // desired core mass - m_Mass0 = Mass0ToMatchDesiredCoreMass(this, desiredCoreMass); // use root finder to find new core mass estimate + double desiredCoreMass = std::min(m_Mass, MainSequenceCoreMass()); // desired core mass + m_Mass0 = Mass0ToMatchDesiredCoreMass(this, desiredCoreMass); // use root finder to find new core mass estimate if (m_Mass0 <= 0.0) { // no root found - no solution for estimated core mass m_Mass0 = m_Mass; // if no root found we keep m_Mass0 equal to the total mass } diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 7815b938b..ea9a88ee5 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -25,17 +25,14 @@ * double CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) * * @param [IN] p_Tau Fraction of main sequence lifetime - * * @return Helium abundance in the core (Y_c) */ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) const { // If SHIKAUCHI core mass prescription is used, core helium abundance is calculated with the core mass - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) - return m_HeliumAbundanceCore; - - double heliumAbundanceCoreMax = 1.0 - m_Metallicity; - return ((heliumAbundanceCoreMax - m_InitialHeliumAbundance) * p_Tau) + m_InitialHeliumAbundance; + return ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) + ? m_HeliumAbundanceCore + : ((1.0 - m_Metallicity - m_InitialHeliumAbundance) * p_Tau) + m_InitialHeliumAbundance; } @@ -50,16 +47,14 @@ double MainSequence::CalculateHeliumAbundanceCoreOnPhase(const double p_Tau) con * double CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) * * @param [IN] p_Tau Fraction of main sequence lifetime - * * @return Hydrogen abundance in the core (X_c) */ double MainSequence::CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) const { // If SHIKAUCHI core mass prescription is used, core helium abundance is calculated with the core mass - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) - return 1.0 - m_HeliumAbundanceCore - m_Metallicity; - - return m_InitialHydrogenAbundance * (1.0 - p_Tau); + return ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) + ? 1.0 - m_HeliumAbundanceCore - m_Metallicity + : m_InitialHydrogenAbundance * (1.0 - p_Tau); } @@ -286,7 +281,7 @@ double MainSequence::CalculateLuminosityAtPhaseEnd(const double p_Mass) const { * * @param [IN] p_Time Time (after ZAMS) in Myr * @param [IN] p_Mass Mass in Msol - * @param [IN] p_LZAMS0 Zero Age Main Sequence (ZAMS) Luminosity + * @param [IN] p_LZAMS Zero Age Main Sequence (ZAMS) Luminosity * @return Luminosity on the Main Sequence as a function of time */ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const { @@ -599,11 +594,11 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double double deltaR = CalculateDeltaR(p_Mass); double gamma = CalculateGamma(p_Mass); - double mu = std::max(0.5, (1.0 - (0.01 * std::max((a[6] / PPOW(p_Mass, a[7])), (a[8] + (a[9] / PPOW(p_Mass, a[10]))))))); // Hurley et al. 2000, eq 7 - double tHook = mu * tBGB; // Hurley et al. 2000, just after eq 5 + double mu = std::max(0.5, (1.0 - (0.01 * std::max((a[6] / PPOW(p_Mass, a[7])), (a[8] + (a[9] / PPOW(p_Mass, a[10]))))))); // Hurley et al. 2000, eq 7 + double tHook = mu * tBGB; // ibid, just after eq 5 double time = tMS * p_Tau; - double tau1 = std::min(1.0, (time / tHook)); // Hurley et al. 2000, eq 14 - double tau2 = std::max(0.0, std::min(1.0, (time - ((1.0 - epsilon) * tHook)) / (epsilon * tHook))); // Hurley et al. 2000, eq 15 + double tau1 = std::min(1.0, (time / tHook)); // ibid, eq 14 + double tau2 = std::max(0.0, std::min(1.0, (time - ((1.0 - epsilon) * tHook)) / (epsilon * tHook))); // ibid, eq 15 // pow() is slow - use multipliaction where it makes sense double tau_3 = p_Tau * p_Tau * p_Tau; @@ -612,13 +607,13 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double double tau1_3 = tau1 * tau1 * tau1; double tau2_3 = tau2 * tau2 * tau2; - double logRMS_RZAMS = alphaR * p_Tau; // Hurley et al. 2000, eq 13, part 1 - logRMS_RZAMS += betaR * tau_10; // Hurley et al. 2000, eq 13, part 2 - logRMS_RZAMS += gamma * tau_40; // Hurley et al. 2000, eq 13, part 3 - logRMS_RZAMS += (log10(RTMS / RZAMS) - alphaR - betaR - gamma) * tau_3; // Hurley et al. 2000, eq 13, part 4 - logRMS_RZAMS -= deltaR * (tau1_3 - tau2_3); // Hurley et al. 2000, eq 13, part 5 + double logRMS_RZAMS = alphaR * p_Tau; // ibid, eq 13, part 1 + logRMS_RZAMS += betaR * tau_10; // ibid, eq 13, part 2 + logRMS_RZAMS += gamma * tau_40; // ibid, eq 13, part 3 + logRMS_RZAMS += (log10(RTMS / RZAMS) - alphaR - betaR - gamma) * tau_3; // ibid, eq 13, part 4 + logRMS_RZAMS -= deltaR * (tau1_3 - tau2_3); // ibid, eq 13, part 5 - return RZAMS * PPOW(10.0, logRMS_RZAMS); // rewrite Hurley et al. 2000, eq 13 for R(t) + return RZAMS * PPOW(10.0, logRMS_RZAMS); // rewrite Hurley et al. 2000, eq 13 for R(t) #undef a } @@ -640,16 +635,15 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double */ double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const { HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); - double radiusTAMS = clone->Radius(); // Get radius from clone (with updated Mass0) - delete clone; clone = nullptr; // Return the memory allocated for the clone + double radiusTAMS = clone->Radius(); // Get radius from clone (with updated Mass0) + delete clone; clone = nullptr; // Return the memory allocated for the clone double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; double tauAtHookStart = 0.99; double ageAtHookStart = tauAtHookStart * tMS; double radiusAtHookStart = CalculateRadiusOnPhaseTau(p_Mass, tauAtHookStart); - double radius = (radiusAtHookStart * (tMS - p_Age) + radiusTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); // Linear interpolation - return radius; + return (radiusAtHookStart * (tMS - p_Age) + radiusTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); // Linear interpolation } @@ -665,16 +659,20 @@ double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const do */ double MainSequence::CalculateRadialExtentConvectiveEnvelope() const { double radiusEnvelope0 = m_Radius; + if ( utils::Compare(m_Mass, 1.25) >= 0) radiusEnvelope0 = 0.0; else if (utils::Compare(m_Mass, 0.35) > 0) { - double radiusM035 = CalculateRadiusAtZAMS(0.35); // uses radius of a 0.35 solar mass star at ZAMS rather than at fractional age Tau, but such low-mass stars only grow by a maximum factor of 1.5 [just above Eq. (10) in Hurley, Pols, Tout (2000), so this is a reasonable approximation - radiusEnvelope0 = radiusM035 * std::sqrt((1.25 - m_Mass) / 0.9); + // uses radius of a 0.35 solar mass star at ZAMS rather than at fractional age Tau, + // but such low-mass stars only grow by a maximum factor of 1.5 + // [just above Eq. (10) in Hurley, Pols, Tout (2000)], so this is a reasonable approximation + radiusEnvelope0 = CalculateRadiusAtZAMS(0.35) * std::sqrt((1.25 - m_Mass) / 0.9); } return radiusEnvelope0 * std::sqrt(std::sqrt(1.0 - m_Tau)); } + /* * Calculate the radial extent of the star's convective core (if it has one) * @@ -729,6 +727,7 @@ double MainSequence::CalculateConvectiveCoreMass() const { return (initialConvectiveCoreMass - m_Tau * (initialConvectiveCoreMass - finalConvectiveCoreMass)); } + /* * Calculate the mass of the convective envelope * @@ -758,26 +757,26 @@ DBL_DBL MainSequence::CalculateConvectiveEnvelopeMass() const { * This function also accounts for mass gain by modeling rejuvenation and updates the initial mixing core mass * and the helium abundance just outside the core * - * void CalculateMainSequenceCoreMassShikauchi(const double p_Dt) + * DBL_DBL CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate) * * @param [IN] p_Dt Current time step in Myr * @param [IN] p_MassLossRate Mass loss/gain rate in Msol yr-1 (negative for mass loss, positive for mass gain) + * @return Tuple containing convective core mass and core helium abundance */ -void MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate) { +DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate) { + + static double heliumAbundanceCoreOut{ m_InitialHeliumAbundance }; // Helium abundance just outside the core - initially m_InitialHeliumAbundance + + // get Shikauchi coefficients DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); - - // Eq (A3) - double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); - // Eq (A4) - auto beta = [&](double mass) { return 1.0 - FMIX_COEFFICIENTS[1] * mass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-mass / FMIX_COEFFICIENTS[2]); }; - // Eq (A2) - auto alpha = [&](double coreMass) { return PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * coreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; }; - // Eq (A7) - double g = -0.0044 * m_MZAMS + 0.27; - // Eq (A6) - auto delta = [&](double centralHeFraction) { return std::min(PPOW(10.0, -(centralHeFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity) + g), 1.0); }; + + double fmix = FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-m_MZAMS / FMIX_COEFFICIENTS[2]); // Shikauchi et al. (2024), eq (A3) + auto beta = [&](double mass) { return 1.0 - FMIX_COEFFICIENTS[1] * mass / (FMIX_COEFFICIENTS[2] * fmix) * std::exp(-mass / FMIX_COEFFICIENTS[2]); }; // ibid, eq (A4) + auto alpha = [&](double coreMass) { return PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * coreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; }; // ibid, eq (A2) + double g = -0.0044 * m_MZAMS + 0.27; // ibid, eq (A7) + auto delta = [&](double centralHeFraction) { return std::min(PPOW(10.0, -(centralHeFraction - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity) + g), 1.0); }; // ibid, eq (A6) // Use boost adaptive ODE solver controlled_stepper_type controlled_stepper; @@ -786,41 +785,43 @@ void MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, con x[1] = std::log(m_MainSequenceCoreMass); x[2] = m_Mass; auto ode = [&](const state_type &x, state_type &dxdt, const double) { - // Eq (13) - dxdt[0] = CalculateLuminosityShikauchi(std::exp(x[1]), x[0], 0.0) / (Q_CNO * std::exp(x[1])); - // Eq (12) - dxdt[1] = - alpha(std::exp(x[1])) / (1.0 - alpha(std::exp(x[1])) * x[0]) * dxdt[0] + beta(x[2]) * delta(x[0]) * p_MassLossRate / (YEAR_TO_MYR * x[2]); - // Mass loss/gain rate - dxdt[2] = p_MassLossRate; + dxdt[0] = CalculateLuminosityShikauchi(std::exp(x[1]), x[0], 0.0) / (Q_CNO * std::exp(x[1])); // Shikauchi et al. (2024), eq (13) + dxdt[1] = -alpha(std::exp(x[1])) / (1.0 - alpha(std::exp(x[1])) * x[0]) * dxdt[0] + beta(x[2]) * delta(x[0]) * p_MassLossRate / (YEAR_TO_MYR * x[2]); // ibid, eq (12) + dxdt[2] = p_MassLossRate; // Mass loss/gain rate }; integrate_adaptive(controlled_stepper, ode, x, 0.0, p_Dt, p_Dt/100.0); - double newMixingCoreMass = std::exp(x[1]); // New mixing core mass - double newCentralHeliumFraction = x[0]; // New central helium fraction - double deltaCoreMass = newMixingCoreMass - m_MainSequenceCoreMass; // Difference in core mass - - if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation - if (utils::Compare(newMixingCoreMass, m_InitialMainSequenceCoreMass) < 0) { // New core mass less than initial core mass? - // Calculate the change in helium abundance just outside the core - double deltaYout = (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass; + double newMixingCoreMass = std::exp(x[1]); // New mixing core mass + double newCentralHeliumFraction = x[0]; // New central helium fraction + double deltaCoreMass = newMixingCoreMass - m_MainSequenceCoreMass; // Difference in core mass + + if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation + if (utils::Compare(newMixingCoreMass, m_InitialMainSequenceCoreMass) < 0) { // New core mass less than initial core mass? + // Common factors + double f1 = heliumAbundanceCoreOut - m_InitialHeliumAbundance; + double f2 = m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass; + double f3 = m_MainSequenceCoreMass + deltaCoreMass; + + // Calculate change in helium abundance just outside the core + double deltaYout = f1 / f2 * deltaCoreMass; + // Calculate the change in core helium abundance, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 - double deltaY = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / (m_MainSequenceCoreMass + deltaCoreMass) * deltaCoreMass + 0.5 / (m_MainSequenceCoreMass + deltaCoreMass) * (m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance) / (m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass) * deltaCoreMass * deltaCoreMass; + double deltaY = (heliumAbundanceCoreOut - m_HeliumAbundanceCore) / f3 * deltaCoreMass + 0.5 / f3 * f1 / f2 * deltaCoreMass * deltaCoreMass; newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; - m_HeliumAbundanceCoreOut = m_HeliumAbundanceCoreOut + deltaYout; + heliumAbundanceCoreOut += deltaYout; } - else { // New core mass greater or equal to the initial core mass? - double deltaCoreMass1 = m_InitialMainSequenceCoreMass - m_MainSequenceCoreMass; // Mass accreted up to the initial core mass - double deltaCoreMass2 = deltaCoreMass - deltaCoreMass1; // Remaining accreted mass - newCentralHeliumFraction = (m_MainSequenceCoreMass * m_HeliumAbundanceCore + 0.5 * (m_HeliumAbundanceCoreOut + m_InitialHeliumAbundance) * deltaCoreMass1 + deltaCoreMass2 * m_InitialHeliumAbundance) / (m_MainSequenceCoreMass + deltaCoreMass); - m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; + else { // New core mass greater or equal to the initial core mass? + double deltaCoreMass1 = m_InitialMainSequenceCoreMass - m_MainSequenceCoreMass; // Mass accreted up to the initial core mass + double deltaCoreMass2 = deltaCoreMass - deltaCoreMass1; // Remaining accreted mass + newCentralHeliumFraction = (m_MainSequenceCoreMass * m_HeliumAbundanceCore + 0.5 * (heliumAbundanceCoreOut + m_InitialHeliumAbundance) * deltaCoreMass1 + deltaCoreMass2 * m_InitialHeliumAbundance) / (m_MainSequenceCoreMass + deltaCoreMass); + heliumAbundanceCoreOut = m_InitialHeliumAbundance; m_InitialMainSequenceCoreMass = newMixingCoreMass; } } else - m_HeliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c + heliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c - m_MainSequenceCoreMass = newMixingCoreMass; // Update core mass - m_HeliumAbundanceCore = std::min(newCentralHeliumFraction, 1.0 - m_Metallicity); // Update core helium abundance + return std::tuple (newMixingCoreMass, std::min(newCentralHeliumFraction, 1.0 - m_Metallicity)); } @@ -835,7 +836,7 @@ void MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, con */ double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_MZAMS) const { DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); - double fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_MZAMS / fmixCoefficients[2]); + double fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_MZAMS / fmixCoefficients[2]); return fmix * p_MZAMS; } @@ -851,39 +852,52 @@ double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_MZAMS) * @param [IN] p_MassLossRate Mass loss rate either from stellar winds or mass transfer in Msol yr-1 */ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_MassLossRate) { + + double mainSequenceCoreMass = m_MainSequenceCoreMass; // default is no change + double heliumAbundanceCore = m_HeliumAbundanceCore; // default is no change + double age = m_Age; // default is no change + switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::NONE: { - m_MainSequenceCoreMass = 0.0; + case CORE_MASS_PRESCRIPTION::NONE: + mainSequenceCoreMass = 0.0; break; - } - case CORE_MASS_PRESCRIPTION::MANDEL: { + + case CORE_MASS_PRESCRIPTION::MANDEL: // Calculate the minimum core mass of a main sequence star that loses mass through Case A mass transfer as the core mass of a TAMS star, scaled by the fractional age. // Only applied to donors as part of binary evolution, not applied to SSE if ((OPTIONS->RetainCoreMassDuringCaseAMassTransfer()) && (p_MassLossRate < 0.0) && (utils::Compare(p_MassLossRate, -m_Mdot) != 0)) - m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); + mainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); break; - } - case CORE_MASS_PRESCRIPTION::SHIKAUCHI: { + + case CORE_MASS_PRESCRIPTION::SHIKAUCHI: // Set core mass following Shikauchi et al. (2024) // MZAMS greater than the limit? SHIKAUCHI prescription valid if (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0) { - // Only proceed with calculations if star is not in MS hook (Yc < 1-Z), time step is not zero, and when the mass loss rate argument is equal to the total mass loss rate (i.e. total mass loss rate was updated, this prevents the calculation in SSE if it was executed as part of BSE for the same time step) + // Only proceed with calculations if star is not in MS hook (Yc < 1-Z), time step is not zero, + // and when the mass loss rate argument is equal to the total mass loss rate + // (i.e. total mass loss rate was updated, this prevents the calculation in SSE if it was executed as part of BSE for the same time step) if ((utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) < 0) && (utils::Compare(p_Dt, 0.0) != 0) && (utils::Compare(p_MassLossRate, m_TotalMassLossRate) == 0)) { - // Calculate and update the core mass and central helium fraction - CalculateMainSequenceCoreMassShikauchi(p_Dt, p_MassLossRate); - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - // Update the effective age based on central helium fraction - m_Age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; + + std::tie(mainSequenceCoreMass, heliumAbundanceCore) = CalculateMainSequenceCoreMassShikauchi(p_Dt, p_MassLossRate); // calculate and update the core mass and central helium fraction + + double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; + age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // update the effective age based on central helium fraction } } // MZAMS less than the limit? MANDEL prescription used else { // Only applied to donors as part of binary evolution, not applied to SSE if ((p_MassLossRate < 0.0) && (utils::Compare(p_MassLossRate, -m_Mdot) != 0)) - m_MainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); + mainSequenceCoreMass = std::max(m_MainSequenceCoreMass, CalculateTauOnPhase() * TAMSCoreMass()); } - } + break; + + default: break; // do nothing } + + m_MainSequenceCoreMass = mainSequenceCoreMass; // update core mass + m_HeliumAbundanceCore = heliumAbundanceCore; // update core helium abundance + m_Age = age; // update age } @@ -1074,7 +1088,7 @@ double MainSequence::TAMSCoreMass() const { /* - * Sets the mass and age of a merge product of two main sequence stars + * Sets the mass and age of a merger product of two main sequence stars * (note: treats merger products as main-sequence stars, not CHE, and does not check for MS_lte_07) * * Uses prescription of Wang et al., 2022, https://www.nature.com/articles/s41550-021-01597-5 @@ -1097,7 +1111,7 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { CalculateTimescales(); CalculateGBParams(); - m_Tau = (initialHydrogenFraction - p_HydrogenMass / m_Mass) / initialHydrogenFraction; // assumes uniformly mixed merger product and a uniform rate of H fusion on main sequence + m_Tau = (initialHydrogenFraction - p_HydrogenMass / m_Mass) / initialHydrogenFraction; // assumes uniformly mixed merger product and a uniform rate of H fusion on main sequence m_Age = m_Tau * timescales(tMS); @@ -1106,8 +1120,8 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { m_HydrogenAbundanceCore = 1.0 - m_Metallicity - m_HeliumAbundanceCore; if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::SHIKAUCHI) && (utils::Compare(m_MZAMS, SHIKAUCHI_LOWER_MASS_LIMIT) >= 0)) { - m_InitialMainSequenceCoreMass = CalculateInitialMainSequenceCoreMass(p_Mass); // update initial mixing core mass - m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; + m_InitialMainSequenceCoreMass = CalculateInitialMainSequenceCoreMass(p_Mass); // update initial mixing core mass + m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; // update core mass } UpdateAttributesAndAgeOneTimestep(0.0, 0.0, 0.0, true); @@ -1116,7 +1130,6 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { } - /* * Interpolate Ge+ Critical Mass Ratios, for H-rich stars * @@ -1288,7 +1301,7 @@ double MainSequence::InterpolateGeEtAlQCrit(const QCRIT_PRESCRIPTION p_qCritPres * @return Tuple containing vectors of coefficients for the specified metallicity */ std::tuple MainSequence::InterpolateShikauchiCoefficients(const double p_Metallicity) const { - double logZ = std::log10(p_Metallicity); + DBL_VECTOR alphaCoeff(3, 0.0); DBL_VECTOR fmixCoeff(3, 0.0); DBL_VECTOR lCoeff(10, 0.0); @@ -1297,40 +1310,50 @@ std::tuple MainSequence::InterpolateShikauc if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::SHIKAUCHI) return std::tuple (alphaCoeff, fmixCoeff, lCoeff); + double logZ = std::log10(p_Metallicity); + // Coefficients are given for these metallicities double low = std::log10(0.1 * ZSOL_ASPLUND); - double middle = std::log10(1.0/3.0 * ZSOL_ASPLUND); + double middle = std::log10(1.0 / 3.0 * ZSOL_ASPLUND); double high = std::log10(ZSOL_ASPLUND); + + // common factors + double middle_logZ = middle - logZ; + double middle_low = middle - low; + double high_logZ = high - logZ; + double high_middle = high - middle; + double logZ_low = logZ - low; + double logZ_middle = logZ - middle; // Linear extrapolation (constant) for metallicity lower than the lowest bound if (utils::Compare(logZ, low) <= 0) { alphaCoeff = SHIKAUCHI_ALPHA_COEFFICIENTS[0]; - fmixCoeff = SHIKAUCHI_FMIX_COEFFICIENTS[0]; - lCoeff = SHIKAUCHI_L_COEFFICIENTS[0]; + fmixCoeff = SHIKAUCHI_FMIX_COEFFICIENTS[0]; + lCoeff = SHIKAUCHI_L_COEFFICIENTS[0]; } // Linear interpolation between metallicity low and middle else if ((utils::Compare(logZ, low) > 0) && (utils::Compare(logZ, middle) <= 0)) { for (size_t i = 0; i < 3; i++) { - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * middle_logZ + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * logZ_low) / middle_low; + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * middle_logZ + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * logZ_low) / middle_low; } for (size_t i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * (middle - logZ) + SHIKAUCHI_L_COEFFICIENTS[1][i] * (logZ - low)) / (middle - low); + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * middle_logZ + SHIKAUCHI_L_COEFFICIENTS[1][i] * logZ_low) / middle_low; } // Linear interpolation between metallicity middle and high else if ((utils::Compare(logZ, middle) > 0) && (utils::Compare(logZ, high) < 0)) { for (size_t i = 0; i < 3; i++) { - alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); - fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * high_logZ + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * logZ_middle) / high_middle; + fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * high_logZ + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * logZ_middle) / high_middle; } for (size_t i = 0; i < 10; i++) - lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * (high - logZ) + SHIKAUCHI_L_COEFFICIENTS[2][i] * (logZ - middle)) / (high - middle); + lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * high_logZ + SHIKAUCHI_L_COEFFICIENTS[2][i] * logZ_middle) / high_middle; } // Linear extrapolation (constant) for metallicity equal to solar or higher else { alphaCoeff = SHIKAUCHI_ALPHA_COEFFICIENTS[2]; - fmixCoeff = SHIKAUCHI_FMIX_COEFFICIENTS[2]; - lCoeff = SHIKAUCHI_L_COEFFICIENTS[2]; + fmixCoeff = SHIKAUCHI_FMIX_COEFFICIENTS[2]; + lCoeff = SHIKAUCHI_L_COEFFICIENTS[2]; } return std::tuple (alphaCoeff, fmixCoeff, lCoeff); diff --git a/src/MainSequence.h b/src/MainSequence.h index 541919016..4ef3b6586 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -24,8 +24,9 @@ class MainSequence: virtual public BaseStar { protected: - double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class - double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, used for rejuvenation calculations + // member variables + + double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; @@ -74,7 +75,7 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Use class member variables double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore, const double p_Age) const; - void CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate); + DBL_DBL CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate); double CalculateInitialMainSequenceCoreMass(const double p_MZAMS) const; double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 diff --git a/src/constants.h b/src/constants.h index dbe470705..c7c5f2796 100755 --- a/src/constants.h +++ b/src/constants.h @@ -292,6 +292,10 @@ constexpr double STARTRACK_PPISN_HE_CORE_MASS = 45.0; constexpr double Q_CNO = 9.9073E4; // Energy released per unit mass by hydrogen fusion via the CNO cycle in Lsol Myr Msol-1 +// Initial mass of stars above which (including the limit) we allow convective core mass calculations from Shikauchi et al. (2024) +// Note that this value should always be > 0.7 Msol +constexpr double SHIKAUCHI_LOWER_MASS_LIMIT = 15.0; + // logging constants const LOGFILETYPE DEFAULT_LOGFILE_TYPE = LOGFILETYPE::HDF5; // Default logfile type @@ -422,6 +426,7 @@ constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_0 = -9.21757267; constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_1 = 3.57319872; constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_2 = -1.2137735; + // coefficients for the calculation of initial angular frequency for Chemically Homogeneous Evolution // Mandel from Butler 2018 const DBL_VECTOR CHE_Coefficients = { 5.7914E-04, -1.9196E-06, -4.0602E-07, 1.0150E-08, -9.1792E-11, 2.9051E-13 }; @@ -3734,38 +3739,25 @@ const std::vector>> LOVERIDGE_COE }; -// Initial mass of stars above which (including the limit) we allow -// convective core mass calculations from Shikauchi et al. (2024) -constexpr double SHIKAUCHI_LOWER_MASS_LIMIT = 15.0; - // Coefficients for determining Main Sequence core mass // from Shikauchi et al. (2024), https://arxiv.org/abs/2409.00460 // Table 2 const std::vector SHIKAUCHI_ALPHA_COEFFICIENTS = { - // 0.1*Z_Sun - {0.45, -0.0557105, -0.86589929}, - // 1/3*Z_Sun - {0.45, -0.06968022, -0.73688164}, - // Solar metallicity Z_Sun - {0.45, -0.05878711, -0.84646162} + {0.45, -0.0557105, -0.86589929}, // 0.1*Z_Sun + {0.45, -0.06968022, -0.73688164}, // 1/3*Z_Sun + {0.45, -0.05878711, -0.84646162} // Solar metallicity Z_Sun }; // Table 3 const std::vector SHIKAUCHI_FMIX_COEFFICIENTS = { - // 0.1*Z_Sun - {0.86914766, -0.60815098, 37.20654856}, - // 1/3*Z_Sun - {0.86269445, -0.62623353, 35.74630996}, - // Solar metallicity Z_Sun - {0.86605495, -0.64960375, 35.57019104} + {0.86914766, -0.60815098, 37.20654856}, // 0.1*Z_Sun + {0.86269445, -0.62623353, 35.74630996}, // 1/3*Z_Sun + {0.86605495, -0.64960375, 35.57019104} // Solar metallicity Z_Sun }; // Table 4 const std::vector SHIKAUCHI_L_COEFFICIENTS = { - // 0.1*Z_Sun - {3.2555795, 1.84666823, -0.79986388, -0.75728099, -0.38831172, 0.08223542, 0.49543834, 0.31314176, -0.36705796, 1.72200581}, - // 1/3*Z_Sun - {3.35622529, 1.96904931, -0.88894808, -0.81112488, -0.47925922, 0.09056925, 0.53094768, 0.33971972, -0.35581284, 1.65390003}, - // Solar metallicity Z_Sun - {3.27883249, 1.79370338, -0.71413866, -0.77019351, -0.3898752, 0.07499563, 0.5920458, 0.33846556, -0.49649838, 1.71263853} + {3.2555795, 1.84666823, -0.79986388, -0.75728099, -0.38831172, 0.08223542, 0.49543834, 0.31314176, -0.36705796, 1.72200581}, // 0.1*Z_Sun + {3.35622529, 1.96904931, -0.88894808, -0.81112488, -0.47925922, 0.09056925, 0.53094768, 0.33971972, -0.35581284, 1.65390003}, // 1/3*Z_Sun + {3.27883249, 1.79370338, -0.71413866, -0.77019351, -0.3898752, 0.07499563, 0.5920458, 0.33846556, -0.49649838, 1.71263853} // Solar metallicity Z_Sun }; #endif // __constants_h__ diff --git a/src/typedefs.h b/src/typedefs.h index 7241c2290..66a9fd1b1 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -387,9 +387,9 @@ const COMPASUnorderedMap CHE_MODE_LABEL = { // main sequence core mass prescription enum class CORE_MASS_PRESCRIPTION: int { NONE, MANDEL, SHIKAUCHI }; const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { - { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, - { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, - { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } + { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, + { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, + { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } }; // logfile delimiters From 1deda5da619745b7856b0f59ef51cfb9be6166d9 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Thu, 16 Jan 2025 11:53:56 +1100 Subject: [PATCH 23/25] A few minor changes --- .../preprocessing/compasConfigDefault.yaml | 2 +- .../program-options-list-defaults.rst | 4 +-- online-docs/pages/whats-new.rst | 6 ++++ src/MainSequence.cpp | 30 +++++++++---------- src/MainSequence.h | 3 +- src/typedefs.h | 4 +-- 6 files changed, 27 insertions(+), 22 deletions(-) diff --git a/compas_python_utils/preprocessing/compasConfigDefault.yaml b/compas_python_utils/preprocessing/compasConfigDefault.yaml index d6d2b7143..fd0cc2a56 100644 --- a/compas_python_utils/preprocessing/compasConfigDefault.yaml +++ b/compas_python_utils/preprocessing/compasConfigDefault.yaml @@ -236,7 +236,7 @@ stringChoices: # --initial-mass-function: 'KROUPA' # Default: 'KROUPA' # Options: ['KROUPA','UNIFORM','POWERLAW','SALPETER'] # --LBV-mass-loss-prescription: 'HURLEY_ADD' # Default: 'HURLEY_ADD' # Options: ['BELCZYNSKI','HURLEY','HURLEY_ADD','ZERO','NONE'] # --luminous-blue-variable-prescription: 'HURLEY_ADD' # Default: 'HURLEY_ADD' # Options: ['BELCZYNSKI','HURLEY','HURLEY_ADD','ZERO','NONE'] -# --main-sequence-core-mass-prescription: 'MANDEL' # Default: 'MANDEL' # Options: ['SHIKAUCHI','MANDEL','NONE'] +# --main-sequence-core-mass-prescription: 'MANDEL' # Default: 'MANDEL' # Options: ['SHIKAUCHI','MANDEL','ZERO'] # --mass-loss-prescription: 'MERRITT2024' # Default: 'MERRITT2024' # Options: ['MERRITT2024','BELCZYNSKI2010','HURLEY','ZERO','NONE'] # --OB-mass-loss: 'VINK2021' # Default: 'VINK2021' # Options: ['KRTICKA2018','BJORKLUND2022','VINK2021','VINK2001','ZERO','NONE'] # --OB-mass-loss-prescription: 'VINK2021' # Default: 'VINK2021' # Options: ['KRTICKA2018','BJORKLUND2022','VINK2021','VINK2001','ZERO','NONE'] diff --git a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst index e58a9c23a..5210923e7 100644 --- a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst +++ b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst @@ -797,8 +797,8 @@ Default = 4.2 **--main-sequence-core-mass-prescription** |br| Main sequence core mass prescription. |br| -Options: {NONE, MANDEL, SHIKAUCHI} |br| -``NONE`` : No core mass treatment |br| +Options: {ZERO, MANDEL, SHIKAUCHI} |br| +``ZERO`` : No core mass treatment, set to zero |br| ``MANDEL`` : The core following case A mass transfer is set equal to the expected core mass of a newly formed HG star with mass equal to that of the donor, scaled by the fraction of the donor's MS lifetime at mass transfer |br| ``SHIKAUCHI`` : Core mass according to Shikauchi et al. (2024) |br| diff --git a/online-docs/pages/whats-new.rst b/online-docs/pages/whats-new.rst index 3912bde9c..596f8e811 100644 --- a/online-docs/pages/whats-new.rst +++ b/online-docs/pages/whats-new.rst @@ -3,6 +3,12 @@ What's new Following is a brief list of important updates to the COMPAS code. A complete record of changes can be found in the file ``changelog.h``. +**03.12.00 Jan 16, 2025** +* Added convective core mass prescription for main sequence stars from Shikauchi+ (2024), describing how the core mass evolves under mass loss and mass gain. +* New command line option ``--main-sequence-core-mass-prescription`` with arguments ``SHIKAUCHI`` (new prescription), ``MANDEL`` (replaces the functionality of ``--retain-core-mass-during-caseA-mass-transfer``), and ``ZERO`` (core mass set to zero, no treatment). +* Added new luminosity prescription for main sequence stars from Shikauchi+ (2024). +* Added treatment for rejuvenation of main sequence accretors when the new prescription is used. + **03.10.00 Nov 29, 2024** Added functionality to log stellar mergers in the BSE switchlog file. diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index ea9a88ee5..45b417808 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -734,9 +734,9 @@ double MainSequence::CalculateConvectiveCoreMass() const { * Based on section 7.2 (after Eq. 111) of Hurley, Pols, Tout (2000) * * - * double CalculateConvectiveEnvelopeMass() const + * DBL_DBL CalculateConvectiveEnvelopeMass() const * - * @return Mass of convective envelope in Msol + * @return Tuple containing current mass of convective envelope and mass at ZAMS in Msol */ DBL_DBL MainSequence::CalculateConvectiveEnvelopeMass() const { if (utils::Compare(m_Mass, 1.25) > 0) return std::tuple (0.0, 0.0); @@ -765,8 +765,6 @@ DBL_DBL MainSequence::CalculateConvectiveEnvelopeMass() const { */ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, const double p_MassLossRate) { - static double heliumAbundanceCoreOut{ m_InitialHeliumAbundance }; // Helium abundance just outside the core - initially m_InitialHeliumAbundance - // get Shikauchi coefficients DBL_VECTOR ALPHA_COEFFICIENTS = std::get<0>(SHIKAUCHI_COEFFICIENTS); DBL_VECTOR FMIX_COEFFICIENTS = std::get<1>(SHIKAUCHI_COEFFICIENTS); @@ -798,7 +796,7 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation if (utils::Compare(newMixingCoreMass, m_InitialMainSequenceCoreMass) < 0) { // New core mass less than initial core mass? // Common factors - double f1 = heliumAbundanceCoreOut - m_InitialHeliumAbundance; + double f1 = m_HeliumAbundanceCoreOut - m_InitialHeliumAbundance; double f2 = m_MainSequenceCoreMass - m_InitialMainSequenceCoreMass; double f3 = m_MainSequenceCoreMass + deltaCoreMass; @@ -806,33 +804,33 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, double deltaYout = f1 / f2 * deltaCoreMass; // Calculate the change in core helium abundance, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 - double deltaY = (heliumAbundanceCoreOut - m_HeliumAbundanceCore) / f3 * deltaCoreMass + 0.5 / f3 * f1 / f2 * deltaCoreMass * deltaCoreMass; + double deltaY = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / f3 * deltaCoreMass + 0.5 / f3 * f1 / f2 * deltaCoreMass * deltaCoreMass; newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; - heliumAbundanceCoreOut += deltaYout; + m_HeliumAbundanceCoreOut += deltaYout; } else { // New core mass greater or equal to the initial core mass? double deltaCoreMass1 = m_InitialMainSequenceCoreMass - m_MainSequenceCoreMass; // Mass accreted up to the initial core mass double deltaCoreMass2 = deltaCoreMass - deltaCoreMass1; // Remaining accreted mass - newCentralHeliumFraction = (m_MainSequenceCoreMass * m_HeliumAbundanceCore + 0.5 * (heliumAbundanceCoreOut + m_InitialHeliumAbundance) * deltaCoreMass1 + deltaCoreMass2 * m_InitialHeliumAbundance) / (m_MainSequenceCoreMass + deltaCoreMass); - heliumAbundanceCoreOut = m_InitialHeliumAbundance; + newCentralHeliumFraction = (m_MainSequenceCoreMass * m_HeliumAbundanceCore + 0.5 * (m_HeliumAbundanceCoreOut + m_InitialHeliumAbundance) * deltaCoreMass1 + deltaCoreMass2 * m_InitialHeliumAbundance) / (m_MainSequenceCoreMass + deltaCoreMass); + m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; m_InitialMainSequenceCoreMass = newMixingCoreMass; } } else - heliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c + m_HeliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c return std::tuple (newMixingCoreMass, std::min(newCentralHeliumFraction, 1.0 - m_Metallicity)); } /* - * Calculate the initial core mass of a main sequence star using Equation (A3) from Shikauchi et al. (2024) - * + * Calculate the initial convective core mass of a main sequence star using Equation (A3) from Shikauchi et al. (2024), + * also used for calculating core mass after MS merger * * double CalculateInitialMainSequenceCoreMass(const double p_MZAMS) * - * @param [IN] p_MZAMS Mass at ZAMS in Msol - * @return Mass of the convective core in Msol at ZAMS + * @param [IN] p_MZAMS Mass at ZAMS or after merger in Msol + * @return Mass of the convective core at ZAMS or after merger in Msol */ double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_MZAMS) const { DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); @@ -858,7 +856,7 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ double age = m_Age; // default is no change switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::NONE: + case CORE_MASS_PRESCRIPTION::ZERO: mainSequenceCoreMass = 0.0; break; @@ -881,7 +879,7 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ std::tie(mainSequenceCoreMass, heliumAbundanceCore) = CalculateMainSequenceCoreMassShikauchi(p_Dt, p_MassLossRate); // calculate and update the core mass and central helium fraction double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - age = (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // update the effective age based on central helium fraction + age = (heliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // update the effective age based on central helium fraction } } // MZAMS less than the limit? MANDEL prescription used diff --git a/src/MainSequence.h b/src/MainSequence.h index 4ef3b6586..6e2b636f7 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -20,13 +20,14 @@ class MainSequence: virtual public BaseStar { MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::A; } // Always case A - const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); // Interpolate Shikauchi coefficients for the given metallicity; + const std::tuple SHIKAUCHI_COEFFICIENTS = InterpolateShikauchiCoefficients(m_Metallicity); // Interpolate Shikauchi coefficients for the given metallicity protected: // member variables double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class + double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, used for rejuvenation calculations // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; diff --git a/src/typedefs.h b/src/typedefs.h index 66a9fd1b1..251b7bf31 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -385,9 +385,9 @@ const COMPASUnorderedMap CHE_MODE_LABEL = { }; // main sequence core mass prescription -enum class CORE_MASS_PRESCRIPTION: int { NONE, MANDEL, SHIKAUCHI }; +enum class CORE_MASS_PRESCRIPTION: int { ZERO, MANDEL, SHIKAUCHI }; const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { - { CORE_MASS_PRESCRIPTION::NONE, "NONE" }, + { CORE_MASS_PRESCRIPTION::ZERO, "ZERO" }, { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, { CORE_MASS_PRESCRIPTION::SHIKAUCHI, "SHIKAUCHI" } }; From 9fcb88cfbc8ab7634da55cc8dafc310102f10053 Mon Sep 17 00:00:00 2001 From: Adam Brcek Date: Thu, 16 Jan 2025 12:11:03 +1100 Subject: [PATCH 24/25] Small change in MainSequence.h --- src/MainSequence.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MainSequence.h b/src/MainSequence.h index 6e2b636f7..8c95b5d99 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -25,9 +25,9 @@ class MainSequence: virtual public BaseStar { protected: // member variables - - double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class + double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, used for rejuvenation calculations + double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; From 13c10b2b3c437ad2869b46e6c7f13cdbc891feeb Mon Sep 17 00:00:00 2001 From: Jeff Riley Date: Thu, 16 Jan 2025 14:11:16 +1100 Subject: [PATCH 25/25] Minor cleanup --- src/MainSequence.cpp | 8 ++++---- src/MainSequence.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 45b417808..4f73b506e 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -804,15 +804,15 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassShikauchi(const double p_Dt, double deltaYout = f1 / f2 * deltaCoreMass; // Calculate the change in core helium abundance, assuming linear profile between Yc and Y0, and that the the accreted gas has helium fraction Y0 - double deltaY = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / f3 * deltaCoreMass + 0.5 / f3 * f1 / f2 * deltaCoreMass * deltaCoreMass; - newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; - m_HeliumAbundanceCoreOut += deltaYout; + double deltaY = (m_HeliumAbundanceCoreOut - m_HeliumAbundanceCore) / f3 * deltaCoreMass + 0.5 / f3 * f1 / f2 * deltaCoreMass * deltaCoreMass; + newCentralHeliumFraction = m_HeliumAbundanceCore + deltaY; + m_HeliumAbundanceCoreOut += deltaYout; } else { // New core mass greater or equal to the initial core mass? double deltaCoreMass1 = m_InitialMainSequenceCoreMass - m_MainSequenceCoreMass; // Mass accreted up to the initial core mass double deltaCoreMass2 = deltaCoreMass - deltaCoreMass1; // Remaining accreted mass newCentralHeliumFraction = (m_MainSequenceCoreMass * m_HeliumAbundanceCore + 0.5 * (m_HeliumAbundanceCoreOut + m_InitialHeliumAbundance) * deltaCoreMass1 + deltaCoreMass2 * m_InitialHeliumAbundance) / (m_MainSequenceCoreMass + deltaCoreMass); - m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; + m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; m_InitialMainSequenceCoreMass = newMixingCoreMass; } } diff --git a/src/MainSequence.h b/src/MainSequence.h index 8c95b5d99..c035345e5 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -26,7 +26,7 @@ class MainSequence: virtual public BaseStar { // member variables - double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, used for rejuvenation calculations + double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, used for rejuvenation calculations double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class // member functions - alphabetically