"""Merging and isolated evolution step."""
__authors__ = [
"Emmanouil Zapartas <ezapartas@gmail.com>",
"Simone Bavera <Simone.Bavera@unige.ch>",
"Konstantinos Kovlakas <Konstantinos.Kovlakas@unige.ch>",
"Max Briel <max.briel@gmail.com>",
]
import numpy as np
from posydon.binary_evol.DT.step_isolated import IsolatedStep
from posydon.binary_evol.flow_chart import (
STAR_STATES_H_RICH,
STAR_STATES_HE_RICH,
STAR_STATES_NOT_CO,
)
from posydon.binary_evol.singlestar import (
STARPROPERTIES,
convert_star_to_massless_remnant,
)
from posydon.config import PATH_TO_POSYDON_DATA
from posydon.utils.common_functions import check_state_of_star
from posydon.utils.posydonerror import ModelError
from posydon.utils.posydonwarning import Pwarn
LIST_ACCEPTABLE_STATES_FOR_HMS = ["H-rich_Core_H_burning"]
LIST_ACCEPTABLE_STATES_FOR_HeMS = ["stripped_He_Core_He_burning"]
LIST_ACCEPTABLE_STATES_FOR_POSTMS = STAR_STATES_H_RICH.copy()
[LIST_ACCEPTABLE_STATES_FOR_POSTMS.remove(x) for x in LIST_ACCEPTABLE_STATES_FOR_HMS]
LIST_ACCEPTABLE_STATES_FOR_POSTHeMS = STAR_STATES_HE_RICH.copy()
[LIST_ACCEPTABLE_STATES_FOR_POSTHeMS.remove(x) for x in LIST_ACCEPTABLE_STATES_FOR_HeMS]
[docs]
class MergedStep(IsolatedStep):
"""
Prepare a merging star to do an an IsolatedStep
"""
def __init__(
self,
grid_name_Hrich=None,
grid_name_strippedHe=None,
path=PATH_TO_POSYDON_DATA,
merger_critical_rot = 0.4,
rel_mass_lost_HMS_HMS = 0.1,
list_for_matching_HMS = [
["mass", "center_h1", "he_core_mass"],
[20.0, 1.0, 10.0],
["log_min_max", "min_max", "min_max"],
#[m_min_H, m_max_H], [0, None]
[None, None], [0, None]
],
list_for_matching_postMS = [
["mass", "center_he4", "he_core_mass"],
[20.0, 1.0, 10.0],
["log_min_max", "min_max", "min_max"],
#[m_min_H, m_max_H], [0, None]
[None, None], [0, None]
],
list_for_matching_HeStar = [
["he_core_mass", "center_he4"],
[10.0, 1.0],
["min_max" , "min_max"],
#[[m_min_He, m_max_He], [0, None]],
[None, None], [0, None]
],
*args,
**kwargs
):
self.merger_critical_rot = merger_critical_rot
self.rel_mass_lost_HMS_HMS = rel_mass_lost_HMS_HMS
super().__init__(
grid_name_Hrich=grid_name_Hrich,
grid_name_strippedHe=grid_name_strippedHe,
list_for_matching_HMS = list_for_matching_HMS,
list_for_matching_postMS = list_for_matching_postMS,
list_for_matching_HeStar = list_for_matching_HeStar,
*args,
**kwargs)
def __call__(self,binary):
merged_star_properties = self.merged_star_properties
if self.verbose:
print("Before Merger:\n"
f"_____________\n")
print(f"M1 = {binary.star_1.mass}\n"
f"M2 = {binary.star_2.mass}\n"
f"he_core_mass1 = {binary.star_1.he_core_mass}\n"
f"he_core_mass2 = {binary.star_2.he_core_mass}\n"
f"co_core_mass1 = {binary.star_1.co_core_mass}\n"
f"co_core_mass2 = {binary.star_2.co_core_mass}\n")
print(f"star_1.center_h1 = {binary.star_1.center_h1}\n"
f"star_2.center_h1 = {binary.star_2.center_h1}\n"
f"star_1.center_he4 = {binary.star_1.center_he4}\n"
f"star_2.center_he4 = {binary.star_2.center_he4}\n"
f"star_1.center_c12 = {binary.star_1.center_c12}\n"
f"star_2.center_c12 = {binary.star_2.center_c12}\n"
f"star_1.surface_he4 = {binary.star_1.surface_he4}\n"
f"star_2.surface_he4 = {binary.star_2.surface_he4}\n"
f"_____________\n")
if binary.state == "merged":
if binary.event == 'oMerging1':
binary.star_1, binary.star_2 = merged_star_properties(binary.star_1, binary.star_2)
elif binary.event == 'oMerging2':
binary.star_2, binary.star_1 = merged_star_properties(binary.star_2, binary.star_1)
else:
raise ValueError("binary.state='merged' but binary.event != 'oMerging1/2'")
## assume that binaries in RLO with two He-rich stars always merge
elif binary.star_1.state in STAR_STATES_HE_RICH and binary.star_2.state in STAR_STATES_HE_RICH:
binary.state = "merged"
if binary.event == 'oRLO1':
binary.star_1, binary.star_2 = merged_star_properties(binary.star_1, binary.star_2)
elif binary.event == 'oRLO2':
binary.star_2, binary.star_1 = merged_star_properties(binary.star_2, binary.star_1)
else:
raise ValueError("step_merged initiated for He stars but RLO not initiated")
else:
raise ValueError("step_merged initiated but binary is not in valid merging state!")
#binary.event = None
if self.verbose:
print("After Merger:\n"
f"_____________\n"
f"star_1.state = {binary.star_1.state}\n"
f"star_2.state = {binary.star_2.state}\n"
f"binary.state = {binary.state}\n"
f"binary.event = {binary.event}\n")
if binary.event == 'oMerging1':
print(f"M_merged = {binary.star_1.mass}\n"
f"he_core_mass merged = {binary.star_1.he_core_mass}\n"
f"co_core_mass merged = {binary.star_1.co_core_mass}\n")
print(f"star_1.center_h1 = {binary.star_1.center_h1}\n"
f"star_1.center_he4 = {binary.star_1.center_he4}\n"
f"star_1.center_c12 = {binary.star_1.center_c12}\n"
f"star_1.surface_he4 = {binary.star_1.surface_he4}\n")
elif binary.event=='oMerging2':
print(f"M_merged = {binary.star_2.mass}\n"
f"he_core_mass merged = {binary.star_2.he_core_mass}\n"
f"co_core_mass merged = {binary.star_2.co_core_mass}\n")
print(f"star_2.center_h1 = {binary.star_2.center_h1}\n"
f"star_2.center_he4 = {binary.star_2.center_he4}\n"
f"star_2.center_c12 = {binary.star_2.center_c12}\n"
f"star_2.surface_he4 = {binary.star_2.surface_he4}\n")
super().__call__(binary)
[docs]
def mass_weighted_avg(self, star1, star2, abundance_name, mass_weight1="mass", mass_weight2='mass'):
"""Compute the mass-weighted average of an abundance between two stars.
Parameters
----------
star1 : SingleStar
Primary star
star2 : SingleStar
Companion star
abundance_name : str
Name of the SingleStar attribute to average.
mass_weight1 : str
Mass attribute to use as weight for star1. Special values
``"H-rich_envelope_mass"`` and ``"He-rich_envelope_mass"`` are
computed on the fly; any other value is looked up directly on the
star object.
mass_weight2 : str or None
Mass attribute for star2.
"""
# give NaN if the abundance is not defined for one of the stars
A1 = getattr(star1, abundance_name, np.nan)
A2 = getattr(star2, abundance_name, np.nan)
if A1 is None:
A1 = np.nan
if A2 is None:
A2 = np.nan
if mass_weight1 == "mass":
M1 = getattr(star1, "mass", np.nan)
elif mass_weight1 == "H-rich_envelope_mass":
M1 = getattr(star1, "mass", np.nan) - getattr(star1, "he_core_mass", np.nan)
elif mass_weight1 == "He-rich_envelope_mass":
M1 = getattr(star1, "he_core_mass", np.nan) - getattr(star1, "co_core_mass", np.nan)
else:
M1 = getattr(star1, mass_weight1, np.nan)
if M1 is None:
M1 = np.nan
if mass_weight2 == "mass":
M2 = getattr(star2, "mass", np.nan)
elif mass_weight2 == "H-rich_envelope_mass":
M2 = getattr(star2, "mass", np.nan) - getattr(star2, "he_core_mass", np.nan)
elif mass_weight2 == "He-rich_envelope_mass":
M2 = getattr(star2, "he_core_mass", np.nan) - getattr(star2, "co_core_mass", np.nan)
else:
M2 = getattr(star2, mass_weight2, np.nan)
if M2 is None:
M2 = np.nan
den = M1 + M2
if not (np.isfinite(A1) and np.isfinite(A2) and np.isfinite(M1) and np.isfinite(M2)):
mass_weighted_avg_value = np.nan
elif den == 0:
mass_weighted_avg_value = np.nan
else:
mass_weighted_avg_value = (A1 * M1 + A2 * M2) / den
if self.verbose:
print(f"Mass weighted average for {abundance_name}:\n"
f"weight 1: {mass_weight1}\n"
f"weight 2: {mass_weight2}\n"
f"A_base = {A1}\n"
f"M_base_abundance = {M1}\n"
f"A_comp = {A2}\n"
f"M_comp_abundance = {M2}\n"
f"mass_weighted_avg = {mass_weighted_avg_value}\n")
return mass_weighted_avg_value
def _apply_nan_attributes(self, star, extra_nan_keys=None):
"""Set to np.nan the attributes of the star that are not expected to be meaningful after a merger event.
Parameters set to np.nan are:
- all attributes containing the substrings
{"log_", "lg_", "surf_", "conv_", "lambda", "profile"}
- all attributes in the set:
{"c12_c12", "total_moment_of_inertia", "spin",}
Parameters
----------
star: SingleStar
The star for which the attributes will be set to np.nan
extra_nan_keys: set of str
Set of keys to be set to np.nan in addition to the default ones.
"""
if extra_nan_keys is None:
extra_nan_keys = set()
_NAN_SUBSTRINGS = ("log_", "lg_", "surf_", "conv_", "lambda", "profile")
_NAN_KEYS = set(["c12_c12", "total_moment_of_inertia", "spin"])
for key in STARPROPERTIES:
if any(sub in key for sub in _NAN_SUBSTRINGS) or (key in extra_nan_keys) or (key in _NAN_KEYS):
setattr(star, key, np.nan)
[docs]
def merged_star_properties(self, star_base, comp):
"""Set the properties of the merged star after a merger event.
Generally, the Roche lobe overflowing star becomes star_base, except
when the companion is further evolved than star_base, in which
case the comp becomes the base for the merged star.
Abundances are mass-weighted averages of the two stars, with the
weights depending on the type of merger and the abundance considered.
star_base: Single Star
The star that engulfs its companion.
(generally the base for the merged_star)
comp: Single Star
The star that is engulfed by star_base.
"""
# By default the stellar attributes are kept from star_base.
merged_star = star_base
s1 = star_base.state
s2 = comp.state
# MS + MS
if ( s1 in LIST_ACCEPTABLE_STATES_FOR_HMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_HMS):
# Mix central and surface abundances of the two stars with
# mass-weighted averages based on the total masses of the two stars.
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16",
"surface_h1", "surface_he4", "surface_c12", "surface_n14", "surface_o16"]
for abundance_name in parameters_to_mix:
#TODO: should I check if the abundaces above end up in ~1 (?)
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="mass",
mass_weight2='mass'))
# The change of masses occurs after the calculation of weighted averages
merged_star.mass = (star_base.mass + comp.mass) * (1.-self.rel_mass_lost_HMS_HMS)
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star,
extra_nan_keys=set(["center_gamma",
"avg_c_in_c_core",
"envelope_binding_energy"]))
# Set companion to a massless remnant.
massless_remnant = convert_star_to_massless_remnant(comp)
# postMS + MS
elif (s1 in LIST_ACCEPTABLE_STATES_FOR_POSTMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_HMS):
# weighted mixing on the surface abundances of the whole
# companion with the envelope of star_base
parameters_to_mix = ["surface_h1", "surface_he4", "surface_c12", "surface_n14", "surface_o16"]
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="H-rich_envelope_mass",
mass_weight2="mass"))
# The change of masses occurs after the calculation of weighted averages
# Note that the he core mass is unchanged, which means that
# adding the mass changes the envelope mass only.
merged_star.mass = star_base.mass + comp.mass
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# Set burning luminosities and star state.
merged_star.log_LHe = star_base.log_LHe
merged_star.log_LZ = star_base.log_LZ
merged_star.state = check_state_of_star(merged_star, star_CO=False) # TODO for sure this needs testing!
massless_remnant = convert_star_to_massless_remnant(comp)
# MS + postMS (the opposite of above)
elif (s1 in LIST_ACCEPTABLE_STATES_FOR_HMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_POSTMS):
merged_star = comp
parameters_to_mix = ["surface_h1", "surface_he4", "surface_c12", "surface_n14", "surface_o16"]
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="mass",
mass_weight2="H-rich_envelope_mass",))
# The change of masses occurs after the calculation of weighted averages
merged_star.mass = star_base.mass + comp.mass
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# Set burning luminosities and star state.
merged_star.log_LHe = comp.log_LHe
merged_star.log_LZ = comp.log_LZ
merged_star.state = check_state_of_star(merged_star, star_CO=False)
massless_remnant = convert_star_to_massless_remnant(star_base)
# postMS + postMS
elif (s1 in LIST_ACCEPTABLE_STATES_FOR_POSTMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_POSTMS):
# Weighted central abundances if merging cores.
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16",
'avg_c_in_c_core']
# two stars with Helium cores
if star_base.co_core_mass == 0 and comp.co_core_mass == 0:
mass_weight1 = 'he_core_mass'
mass_weight2 = 'he_core_mass'
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name = abundance_name,
mass_weight1 = mass_weight1,
mass_weight2 = mass_weight2))
setattr(merged_star, "center_gamma", np.nan)
# star_base with CO core and the comp has a He core
elif (star_base.co_core_mass > 0 and comp.co_core_mass == 0):
pass # the central abundances are kept as the ones of star_base
# Companion with CO core and the star_base has a He core
elif (comp.co_core_mass > 0 and star_base.co_core_mass == 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, getattr(comp, abundance_name))
setattr(merged_star, "center_gamma", comp.center_gamma)
# both stars have CO cores
elif (star_base.co_core_mass > 0 and comp.co_core_mass > 0):
#TODO : maybe he_core_mass makes more sense?
mass_weight1='co_core_mass'
mass_weight2='co_core_mass'
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1=mass_weight1,
mass_weight2=mass_weight2))
setattr(merged_star, "center_gamma", np.nan)
else:
Pwarn("Unexpected combination of CO core masses during merging", "EvolutionWarning")
additional_parameter_to_mix = ['surface_h1', 'surface_he4', 'surface_c12', 'surface_n14', 'surface_o16']
# Weighted mixing on the surface abundances based on the envelopes of the two stars
for abundance_name in additional_parameter_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="H-rich_envelope_mass",
mass_weight2="H-rich_envelope_mass"))
# Add total and core masses after calculations of weighted average
for key in ["mass", "he_core_mass", "c_core_mass", "o_core_mass", "co_core_mass"]:
current = getattr(star_base, key) + getattr(comp, key)
setattr(merged_star, key,current)
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# TODO for sure this needs testing!
merged_star.state = check_state_of_star(merged_star, star_CO=False)
massless_remnant = convert_star_to_massless_remnant(comp)
# postMS + HeMS
elif (s1 in LIST_ACCEPTABLE_STATES_FOR_POSTMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_HeMS):
# Keep surface abundances from star_base.
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16", "avg_c_in_c_core"]
# Weighted central abundances if merging cores.
# two stars with only Helium cores, not CO cores
if star_base.co_core_mass == 0 and comp.co_core_mass == 0:
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="he_core_mass",
mass_weight2="he_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
# star_base with CO core and the comp has just a He core (is a HeMS star)
# Keep central abundances as those from star_base.
elif (star_base.co_core_mass > 0 and comp.co_core_mass == 0):
pass
else:
Pwarn("Unexpected combination of CO core masses during merging", "EvolutionWarning")
# add total and core masses after abundance mass weighted calculations
for key in ["mass", "he_core_mass", "c_core_mass", "o_core_mass", "co_core_mass"]:
current = getattr(star_base, key) + getattr(comp, key)
setattr(merged_star, key, current)
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# TODO for sure this needs testing!
merged_star.state = check_state_of_star(merged_star, star_CO=False)
massless_remnant = convert_star_to_massless_remnant(comp)
# HeMS + postMS (the opposite of above)
elif (s1 in LIST_ACCEPTABLE_STATES_FOR_HeMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_POSTMS):
# surface abundances from comp
merged_star = comp
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16", "avg_c_in_c_core"]
# Weighted central abundances if merging cores. Else only from comp
if star_base.co_core_mass == 0 and comp.co_core_mass == 0: # two stars with only Helium cores, not CO cores
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="he_core_mass",
mass_weight2="he_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
# star_base is the HeMS Star and comp has a CO core
# the central abundances are kept from comp
elif (star_base.co_core_mass == 0 and comp.co_core_mass > 0):
pass
else:
Pwarn("Unexpected combination of CO core masses during merging", "EvolutionWarning")
# add total and core masses after weighted averages above
for key in ["mass", "he_core_mass", "c_core_mass", "o_core_mass", "co_core_mass"]:
current = getattr(star_base, key) + getattr(comp, key)
setattr(merged_star, key, current)
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# TODO for sure this needs testing!
merged_star.state = check_state_of_star(merged_star, star_CO=False)
massless_remnant = convert_star_to_massless_remnant(star_base)
# postMS + postHeMS
elif (s1 in LIST_ACCEPTABLE_STATES_FOR_POSTMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_POSTHeMS):
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16", "avg_c_in_c_core"]
# Weighted central abundances if merging cores
# Two stars with Helium cores
if star_base.co_core_mass == 0 and comp.co_core_mass == 0:
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name,
self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="he_core_mass",
mass_weight2="he_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
# Star_base with CO core and the comp has a He core
# the central abundances are kept as the ones of star_base
elif (star_base.co_core_mass > 0 and comp.co_core_mass == 0):
pass
# comp with CO core and the star_base has a He core
elif (comp.co_core_mass > 0 and star_base.co_core_mass == 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, getattr(comp, abundance_name))
setattr(merged_star, "center_gamma", comp.center_gamma)
# both stars have CO cores
elif (star_base.co_core_mass > 0 and comp.co_core_mass > 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name,
self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="co_core_mass",
mass_weight2="co_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
else:
Pwarn("Unexpected combination of CO core masses during merging", "EvolutionWarning")
# add total and core masses
for key in ["mass", "he_core_mass", "c_core_mass", "o_core_mass", "co_core_mass"]:
current = getattr(star_base, key) + getattr(comp, key)
setattr(merged_star, key, current)
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# TODO for sure this needs testing!
merged_star.state = check_state_of_star(merged_star, star_CO=False)
massless_remnant = convert_star_to_massless_remnant(comp)
# postHeMS + postMS (the opposite of above)
elif (s1 in LIST_ACCEPTABLE_STATES_FOR_POSTHeMS
and s2 in LIST_ACCEPTABLE_STATES_FOR_POSTMS):
merged_star = comp
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16", "avg_c_in_c_core"]
# weighted central abundances if merging cores
# two stars with Helium cores
if star_base.co_core_mass == 0 and comp.co_core_mass == 0:
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="he_core_mass",
mass_weight2="he_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
# star_base with CO core and the comp has a He core
elif (star_base.co_core_mass > 0 and comp.co_core_mass == 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, getattr(star_base, abundance_name))
setattr(merged_star, "center_gamma", star_base.center_gamma)
# comp with CO core and the star_base has a He core
# the central abundances are kept as the ones of comp
elif (comp.co_core_mass > 0 and star_base.co_core_mass == 0):
pass
# both stars have CO cores
elif (star_base.co_core_mass > 0 and comp.co_core_mass > 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base, comp, abundance_name=abundance_name, mass_weight1="co_core_mass", mass_weight2="co_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
else:
Pwarn("Unexpected combination of CO core masses during merging", "EvolutionWarning")
# add total and core masses
for key in ["mass", "he_core_mass", "c_core_mass", "o_core_mass", "co_core_mass"]:
current = getattr(star_base, key) + getattr(comp, key)
setattr(merged_star, key,current)
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# TODO for sure this needs testing!
merged_star.state = check_state_of_star(merged_star, star_CO=False)
massless_remnant = convert_star_to_massless_remnant(star_base)
# He star + He star
elif (s1 in STAR_STATES_HE_RICH
and s2 in STAR_STATES_HE_RICH):
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16", "avg_c_in_c_core"]
# weighted central abundances if merging cores. Else only from star_base
# two stars with Helium cores
if star_base.co_core_mass == 0 and comp.co_core_mass == 0:
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="he_core_mass",
mass_weight2="he_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
# star_base with CO core and the comp has a He core
# the central abundances are kept as the ones of star_base
elif (star_base.co_core_mass > 0 and comp.co_core_mass == 0):
pass
# comp with CO core and the star_base has a He core
elif (comp.co_core_mass > 0 and star_base.co_core_mass == 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, getattr(comp, abundance_name))
setattr(merged_star, "center_gamma", comp.center_gamma)
# both stars have CO cores
elif (star_base.co_core_mass > 0 and comp.co_core_mass > 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="co_core_mass",
mass_weight2="co_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
else:
Pwarn("Unexpected combination of CO core masses during merging", "EvolutionWarning")
# add total and core masses
for key in ["mass", "he_core_mass", "c_core_mass", "o_core_mass", "co_core_mass"]:
current = getattr(star_base, key) + getattr(comp, key)
setattr(merged_star, key,current)
# weigheted mixing on the surface abundances based on the He-rich envelopes of the two stars
additional_parameter_to_mix = ['surface_h1', 'surface_he4', 'surface_c12', 'surface_n14', 'surface_o16']
for abundance_name in additional_parameter_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="He-rich_envelope_mass",
mass_weight2="He-rich_envelope_mass"))
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
# TODO for sure this needs testing!
merged_star.state = check_state_of_star(merged_star, star_CO=False)
massless_remnant = convert_star_to_massless_remnant(comp)
# Star + WD
elif (s1 in STAR_STATES_NOT_CO
and s2 in ["WD"]):
parameters_to_mix = ["center_h1", "center_he4", "center_c12", "center_n14", "center_o16", "avg_c_in_c_core"]
# WD is considered a stripped CO core
# WD would always be the comp, it cannot be the engulfing star (so no need to do the opposite stars case below)
# weighted central abundances if merging cores. Else only from star_base
# comp with CO core and the star_base has not
if (comp.co_core_mass is not None and star_base.co_core_mass == 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, getattr(comp, abundance_name))
setattr(merged_star, "center_gamma", comp.center_gamma)
# Star_base with CO core and the comp is a WD (so has a CO core)
elif (comp.co_core_mass is not None and star_base.co_core_mass > 0):
for abundance_name in parameters_to_mix:
setattr(merged_star, abundance_name, self.mass_weighted_avg(star_base,
comp,
abundance_name=abundance_name,
mass_weight1="co_core_mass",
mass_weight2="co_core_mass"))
setattr(merged_star, "center_gamma", np.nan)
else:
Pwarn("Unexpected combination of CO core masses during merging", "EvolutionWarning")
# add total and core masses
for key in ["mass", "he_core_mass", "c_core_mass", "o_core_mass", "co_core_mass"]:
current = getattr(star_base, key) + getattr(comp, "mass")
setattr(merged_star, key,current)
# Set parameters that are not expected to be meaningful after a merger to np.nan
self._apply_nan_attributes(merged_star)
merged_star.state = check_state_of_star(merged_star, star_CO=False) # TODO for sure this needs testing!
massless_remnant = convert_star_to_massless_remnant(comp)
# Star + NS/BH
elif (s1 in STAR_STATES_NOT_CO
and s2 in ["NS", "BH"]):
# TODO: potentially flag a Thorne-Zytkov object
massless_remnant = convert_star_to_massless_remnant(star_base)
## in this case, want CO companion object to stay the same, and base star to be assigned massless remnant
return massless_remnant, comp
else:
raise ModelError(f"Combination of merging star states not expected: {s1} {s2}")
# ad hoc spin of merged star to be used in the detached step
merged_star.surf_avg_omega_div_omega_crit = self.merger_critical_rot
return merged_star, massless_remnant