Skip to content

Commit dd587bf

Browse files
author
Ilya Mandel
committed
Updating mass transfer physics
1 parent ce30ae6 commit dd587bf

File tree

5 files changed

+32
-33
lines changed

5 files changed

+32
-33
lines changed

online-docs/pages/whats-new.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ What's new
44
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``.
55

66
**03.21.00 July 6, 2025**
7-
* - Changed default values of --enhance-CHE-lifetimes-luminosities and --scale-CHE-mass-loss-with-surface-helium-abundance to true
7+
* Changed default values of --enhance-CHE-lifetimes-luminosities and --scale-CHE-mass-loss-with-surface-helium-abundance to true
88
* Added options to set beta and gamma prescription for second stage of 2-stage CE (``--common-envelope-second-stage-beta``, ``--common-envelope-second-stage-gamma-prescription``)
9+
* Fixed a bug in the calculation of zeta_equilibrium, which impacts when mass transfer is declared to proceed on a nuclear timescale (and hence how conservative it is)
910

1011
**03.20.06 June 25, 2025**
1112

src/BaseBinaryStar.cpp

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,27 +2058,22 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) {
20582058
}
20592059

20602060
m_MassTransferTimescale = MT_TIMESCALE::NONE; // initial reset
2061-
double betaThermal = 0.0; // fraction of mass accreted if accretion proceeds on thermal timescale
2062-
double maximumAccretionRate = 0.0; // accretion rate if accretion proceeds on thermal timescale
2061+
20632062
double donorMassLossRateThermal = m_Donor->CalculateThermalMassLossRate();
20642063

2065-
std::tie(maximumAccretionRate, betaThermal) = m_Accretor->CalculateMassAcceptanceRate(donorMassLossRateThermal,
2066-
m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius),
2067-
donorIsHeRich);
2068-
double massDiffDonor = 0.0;
2069-
2064+
std::tie(std::ignore, m_FractionAccreted) = m_Accretor->CalculateMassAcceptanceRate(donorMassLossRateThermal,
2065+
m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius), donorIsHeRich);
2066+
double massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, m_FractionAccreted, 0.0); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, fixed beta
2067+
20702068
// can the mass transfer happen on a nuclear timescale?
20712069
if (m_Donor->IsOneOf(NON_COMPACT_OBJECTS)) {
2072-
// technically, we do not know how much mass the accretor should gain until we do the calculation,
2073-
// which impacts the RL size, so we will check whether a nuclear timescale MT was feasible later
2074-
double maximumAccretedMass = maximumAccretionRate * m_Dt;
2070+
// if MT_ACCRETION_EFFICIENCY_PRESCRIPTION::FIXED_FRACTION, then CalculateMassAcceptanceRate() already computed the correct m_FractionAccreted and massDiffDonor (no difference between nuclear and thermal timescale MT)
20752071
if (OPTIONS->MassTransferAccretionEfficiencyPrescription() == MT_ACCRETION_EFFICIENCY_PRESCRIPTION::THERMALLY_LIMITED) {
2076-
massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, -1.0, maximumAccretedMass); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, fixed accretion amount
2077-
m_FractionAccreted = std::min(maximumAccretedMass, massDiffDonor) / massDiffDonor;
2078-
}
2079-
else {
2080-
m_FractionAccreted = maximumAccretionRate / donorMassLossRateThermal; // relevant for MT_ACCRETION_EFFICIENCY_PRESCRIPTION::FIXED_FRACTION
2081-
massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, m_FractionAccreted, 0.0); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, fixed beta
2072+
// technically, we do not know how much mass the accretor should gain until we do the calculation,
2073+
// which impacts the RL size, so we will check whether a nuclear timescale MT was feasible later
2074+
massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, -1.0, m_Dt); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, estimating accretion efficiency based on a mass donation rate of massDiffDonor/m_Dt for self-consistency
2075+
std::tie(std::ignore, m_FractionAccreted) = m_Accretor->CalculateMassAcceptanceRate(massDiffDonor/m_Dt,
2076+
m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius), donorIsHeRich);
20822077
}
20832078

20842079
// check that the star really would have consistently fit into the Roche lobe
@@ -2091,13 +2086,11 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) {
20912086
m_ZetaLobe = zetaLobe;
20922087
}
20932088
}
2094-
if (m_MassTransferTimescale != MT_TIMESCALE::NUCLEAR) { // thermal timescale mass transfer (we will check for dynamically unstable / CE mass transfer later)
2095-
m_ZetaLobe = CalculateZetaRocheLobe(jLoss, betaThermal);
2089+
if (m_MassTransferTimescale != MT_TIMESCALE::NUCLEAR) { // thermal timescale mass transfer (we will check for dynamically unstable / CE mass transfer later); m_FractionAccreted and massDiffDonor already computed for this case
2090+
m_ZetaLobe = CalculateZetaRocheLobe(jLoss, m_FractionAccreted);
20962091
m_ZetaStar = m_Donor->CalculateZetaAdiabatic();
20972092
m_MassLossRateInRLOF = donorMassLossRateThermal;
2098-
m_FractionAccreted = betaThermal;
20992093
m_MassTransferTimescale = MT_TIMESCALE::THERMAL;
2100-
massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, betaThermal, 0.0); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe
21012094
}
21022095

21032096
double aInitial = m_SemiMajorAxis; // semi-major axis in default units, AU, current timestep

src/BaseBinaryStar.h

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -577,13 +577,13 @@ class BaseBinaryStar {
577577
*
578578
*
579579
* Constructor: initialise the class
580-
* template <class T> RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_MaximumAccretedMass, ERROR *p_Error)
580+
* template <class T> RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_Dt, ERROR *p_Error)
581581
*
582582
* @param [IN] p_Binary (Pointer to) The binary star under examination
583583
* @param [IN] p_Donor (Pointer to) The star donating mass
584584
* @param [IN] p_Accretor (Pointer to) The star accreting mass
585-
* @param [IN] p_FractionAccreted The fraction of the donated mass accreted by the accretor (for thermal timescale accretion)
586-
* @param [IN] p_MaximumAccretedMass The total amount of mass that can be accreted (for nuclear timescale accretion, p_FractionAccreted should be negative for this to be used)
585+
* @param [IN] p_FractionAccreted The fraction of the donated mass accreted by the accretor (if known in advance, otherwise zero)
586+
* @param [IN] p_Dt Time step duration (relevant for nuclear timescale mass transfer)
587587
* @param [IN] p_Error (Address of variable to record) Error encountered in functor
588588
*
589589
* Function: calculate radius difference after mass loss
@@ -594,13 +594,13 @@ class BaseBinaryStar {
594594
*/
595595
template <class T>
596596
struct RadiusEqualsRocheLobeFunctor {
597-
RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_MaximumAccretedMass, ERROR *p_Error) {
597+
RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_Dt, ERROR *p_Error) {
598598
m_Binary = p_Binary;
599599
m_Donor = p_Donor;
600600
m_Accretor = p_Accretor;
601601
m_Error = p_Error;
602602
m_FractionAccreted = p_FractionAccreted;
603-
m_MaximumAccretedMass = p_MaximumAccretedMass;
603+
m_Dt = p_Dt;
604604
}
605605
T operator()(double const& p_dM) {
606606

@@ -611,12 +611,16 @@ class BaseBinaryStar {
611611

612612
double donorMass = m_Donor->Mass();
613613
double accretorMass = m_Accretor->Mass();
614+
// use stale value of accretor RL radius -- this is only relevant for nuclear timescale MT, when the change in accretor RL radius should be small
615+
double accretorRLradius = CalculateRocheLobeRadius_Static(accretorMass, donorMass) * AU_TO_RSOL * m_Binary->SemiMajorAxis() * (1.0 - m_Binary->Eccentricity());
614616

615617
// beta is the actual accretion efficiency; if p_FractionAccreted is negative (placeholder
616618
// for nuclear timescale accretion efficiency, for which the total accretion mass over the
617-
// duration of the timestep is known), then the ratio of the maximum allowed accreted
618-
// mass / donated mass is used
619-
double beta = (utils::Compare(m_FractionAccreted, 0.0) >=0 ) ? m_FractionAccreted : std::min(m_MaximumAccretedMass/p_dM, 1.0);
619+
// duration of the timestep is known), then must estimate it on the fly for consistency
620+
double beta = m_FractionAccreted;
621+
if(utils::Compare(beta, 0.0) < 0)
622+
std::tie(std::ignore, beta) = m_Accretor->CalculateMassAcceptanceRate(p_dM / m_Dt,
623+
m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius), m_Donor->IsOneOf(He_RICH_TYPES));
620624

621625
double semiMajorAxis = m_Binary->CalculateMassTransferOrbit(donorMass, -p_dM , *m_Accretor, beta, false);
622626
double RLRadius = semiMajorAxis * (1.0 - m_Binary->Eccentricity()) * CalculateRocheLobeRadius_Static(donorMass - p_dM, accretorMass + (beta * p_dM)) * AU_TO_RSOL;
@@ -631,7 +635,7 @@ class BaseBinaryStar {
631635
BinaryConstituentStar *m_Accretor;
632636
ERROR *m_Error;
633637
double m_FractionAccreted;
634-
double m_MaximumAccretedMass;
638+
double m_Dt;
635639
};
636640

637641

src/BaseStar.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ class BaseStar {
302302
double CalculateZetaAdiabatic();
303303
virtual double CalculateZetaConstantsByEnvelope(ZETA_PRESCRIPTION p_ZetaPrescription) { return 0.0; } // Use inheritance hierarchy
304304

305-
double CalculateZetaEquilibrium() { return 0.0; }
305+
virtual double CalculateZetaEquilibrium() { return 0.0; }
306306

307307
void ClearCurrentSNEvent() { m_SupernovaDetails.events.current = SN_EVENT::NONE; } // Clear supernova event/state for current timestep
308308

src/changelog.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,10 +1599,11 @@
15991599
// - Fixed error in MainSequence::CalculateInitialMainSequenceCoreMass()
16001600
// 03.20.09 RTW - Jun 30, 2025 - Enhancement:
16011601
// - Added individual velocity components for stars to the LogTypedefs file so they can be included in the output (as ANY_STAR_PROPERTY::VELOCITY_X, or Y, Z)
1602-
// 03.21.00 IM - July 6, 2025 - Enhancements:
1602+
// 03.21.00 IM - July 6, 2025 - Enhancements, defect repair:
16031603
// - Changed default values of --enhance-CHE-lifetimes-luminosities and --scale-CHE-mass-loss-with-surface-helium-abundance to true
16041604
// - Added options to set beta and gamma prescription for second stage of 2-stage CE (--common-envelope-second-stage-beta, --common-envelope-second-stage-gamma-prescription)
1605-
1605+
// - Fixed a bug in CalculateZetaEquilibrium(), which impacted when mass transfer is declared nuclear (and how conservative it is)
1606+
// - Now calculate mass accretion rate for nuclear timescale mass transfer on the fly to match with donor mass loss rate set by donor mass loss (required to fit into Roche lobe) divided by time step
16061607

16071608

16081609
const std::string VERSION_STRING = "03.21.00";

0 commit comments

Comments
 (0)