def test_get_all_phases_ML_phases(self, base_path): phase_names = ['init', 'MLstart', 'ML', 'HeIgnition'] # -- 2 Mass loss phases both with wind and rlof mass loss -- data, _ = fileio.read_compressed_track(base_path / 'test_data/M2.341_M1.782_P8.01_Z0.01412.h5', return_profiles=False) # when asking for n_ml_phases = 0, only 1 ML phase should be returned, directly as a np.where structure. phases = evolution_phases.get_all_phases(phase_names, data, 0) assert len(phases) == 4 for name in phase_names: assert name in phases assert type(phases['ML']) == tuple, \ "When passing n_ml_phases=0, the result should be a np.where type structure" assert type(phases['ML'][0]) == np.ndarray,\ "When passing n_ml_phases=0, the result should be a np.where type structure" # when asking for n_ml_phases = 1, only 1 ML phase should be returned, BUT as a list phases = evolution_phases.get_all_phases(phase_names, data, 1) assert type(phases['ML']) == list assert len(phases['ML']) == 1 assert type(phases['ML'][0]) == tuple assert type(phases['ML'][0][0]) == np.ndarray # when asking for n_ml_phases = 2, 2 ML phases should be returned as a list phases = evolution_phases.get_all_phases(phase_names, data, 2) assert type(phases['ML']) == list assert len(phases['ML']) == 2 assert type(phases['ML'][0]) == tuple assert type(phases['ML'][0][0]) == np.ndarray assert type(phases['ML'][1]) == tuple assert type(phases['ML'][1][0]) == np.ndarray
def test_count_ml_phases(self, base_path): # 1 ML phase data, _ = fileio.read_compressed_track( base_path / 'test_data/M0.789_M0.304_P20.58_Z0.h5') n_ml_phases = extract_mesa.count_ml_phases(data) assert n_ml_phases == 1 # 2 separate ML phases data, _ = fileio.read_compressed_track( base_path / 'test_data/M2.341_M1.782_P8.01_Z0.01412.h5') n_ml_phases = extract_mesa.count_ml_phases(data) assert n_ml_phases == 2 # 1 ML phases and 3 other ML phases due to wind mass loss which should not get counted. data, _ = fileio.read_compressed_track( base_path / 'test_data/M1.276_M1.140_P333.11_Z0.h5') n_ml_phases = extract_mesa.count_ml_phases(data) assert n_ml_phases == 1
def data(): data, _, profiles = fileio.read_compressed_track( base_path / 'test_data/M1.080_M0.502_P192.67_Z0.01129.h5', return_profiles=True) stable, ce_age = common_envelope.is_stable(data, criterion='Mdot', value=-2) data = data[data['age'] <= ce_age] return data
def test_apply_ce(): data, _ = fileio.read_compressed_track( base_path / 'test_data/M1.205_M0.413_P505.12_Z0.h5') stable, ce_age = common_envelope.is_stable(data, criterion='J_div_Jdot_div_P', value=10) data = data[data['age'] <= ce_age] data = common_envelope.apply_ce(data, ce_formalism='iben_tutukov1984') assert data['period_days'][-1] == pytest.approx(25.62, abs=0.01) assert data['binary_separation'][-1] == pytest.approx(38.2451, abs=1e-4) assert data['star_1_mass'][-1] == pytest.approx(0.4477, abs=1e-4) assert data['envelope_mass'][-1] == 0 assert data['star_2_mass'][-1] == pytest.approx(0.4278, abs=1e-4) assert data['mass_ratio'][-1] == pytest.approx(1.0465, abs=1e-4) assert data['rl_1'][-1] == pytest.approx(14.6426, abs=1e-4) assert data['rl_2'][-1] == pytest.approx(14.3415, abs=1e-4) assert data['CE_phase'][-1] == 1 assert data['CE_phase'][-2] == 1 assert data['CE_phase'][-3] == 0 data, _, profiles = fileio.read_compressed_track( base_path / 'test_data/M1.080_M0.502_P192.67_Z0.01129.h5', return_profiles=True) stable, ce_age = common_envelope.is_stable(data, criterion='Mdot', value=-2) data = data[data['age'] <= ce_age] data = common_envelope.apply_ce(data, profiles=profiles, ce_formalism='dewi_tauris2000', a_th=0.5) assert data['binary_separation'][-1] == pytest.approx(6.394841, abs=0.000001) assert data['star_1_mass'][-1] == pytest.approx(0.38572328, abs=0.000001) assert data['envelope_mass'][-1] > 0 with pytest.raises(ValueError): data = common_envelope.apply_ce(data, ce_formalism='uk_formalism')
def test_check_error_flags(base_path): # check no errors data, _ = fileio.read_compressed_track( base_path / 'test_data/error_models/M0.851_M0.305_P358.92_Z0.00129_no_problems.h5') error_flags = evolution_errors.check_error_flags(data, 'log_g_upper_limit') assert error_flags == [] # check max_model error error_flags = evolution_errors.check_error_flags(data, 'max_model_number') assert 1 in error_flags # check accretor overflow error error_flags = evolution_errors.check_error_flags( data, 'accretor_overflow_terminate') assert 2 in error_flags # check ML and He ignition error data, _ = fileio.read_compressed_track( base_path / 'test_data/error_models/M2.407_M0.432_P1.72_Z0.00706_He_ignition_problem.h5' ) error_flags = evolution_errors.check_error_flags(data, '') assert 4 in error_flags # Check HeIgnition but no HeCoreBurning error data, _ = fileio.read_compressed_track( base_path / 'test_data/error_models/M1.490_M1.380_P144.55_Z0.01427_Jz1.000_HeIgnition_but_no_Coreburning.h5' ) error_flags = evolution_errors.check_error_flags(data, '') assert 4 in error_flags # check CO core formation error data, _ = fileio.read_compressed_track( base_path / 'test_data/error_models/M1.699_M1.401_P260.07_Z0.02489_Jz1.000_HeCoreBurning_doesnt_finish.h5' ) error_flags = evolution_errors.check_error_flags(data, '') assert 5 in error_flags
def test_is_stable(): data, _ = fileio.read_compressed_track( base_path / 'test_data/M1.205_M0.413_P505.12_Z0.h5') stable, ce_age, ce_mn = common_envelope.is_stable(data, criterion='Mdot', value=-3, return_model_number=True) assert stable is False assert ce_age == pytest.approx(5179376593.6, abs=0.1) assert ce_mn == 11928 stable, ce_age, ce_mn = common_envelope.is_stable(data, criterion='delta', value=0.03, return_model_number=True) assert stable is False assert ce_age == pytest.approx(5179376616.1, abs=0.1) assert ce_mn == 12057 stable, ce_age, ce_mn = common_envelope.is_stable( data, criterion='J_div_Jdot_div_P', value=10, return_model_number=True) assert stable is False assert ce_age == pytest.approx(5179376617.0, abs=0.1) assert ce_mn == 12108 stable, ce_age, ce_mn = common_envelope.is_stable( data, criterion='M_div_Mdot_div_P', value=100, return_model_number=True) assert stable is False assert ce_age == pytest.approx(5179376614.6, abs=0.1) assert ce_mn == 12021 stable, ce_age, ce_mn = common_envelope.is_stable(data, criterion='R_div_SMA', value=0.5, return_model_number=True) assert stable is False assert ce_age == pytest.approx(5179376602.9, abs=0.1) assert ce_mn == 11946 with pytest.raises(ValueError): stable, ce_age = common_envelope.is_stable(data, criterion='uk_criterion', value=0.5)
def test_extract_parameters(self, base_path): #TODO: improve this test case and add more checks # HB star with core and shell He burning data, _ = fileio.read_compressed_track( base_path / 'test_data/M1.276_M1.140_P333.11_Z0.h5') parameters = [ 'star_1_mass__init', 'period_days__final', 'rl_1__max', 'rl_1__HeIgnition', 'age__ML__diff', 'he_core_mass__ML__rate', 'star_1_mass__lg_mstar_dot_1_max' ] res = extract_mesa.extract_parameters(data, parameters) res = {k: v for k, v in zip(parameters, res)} assert res['star_1_mass__init'] == data['star_1_mass'][0] assert res['period_days__final'] == data['period_days'][-1] assert res['rl_1__max'] == np.max(data['rl_1']) #assert np.isnan(res['rl_1__HeIgnition']) # a1 = data['age'][data['lg_mstar_dot_1'] > -10][0] # a2 = data['age'][(data['age'] > a1) & (data['lg_mstar_dot_1'] <= -10)][0] # s = np.where((data['age'] >= a1) & (data['age'] <= a2)) # assert res['age__ML__diff'] == data['age'][s][-1] - data['age'][s][0] # assert res['he_core_mass__ML__rate'] == (data['he_core_mass'][s][-1] - data['he_core_mass'][s][0]) / \ # (data['age'][s][-1] - data['age'][s][0]) assert res['rl_1__HeIgnition'] == pytest.approx(152.8606, abs=0.0001) assert res['star_1_mass__lg_mstar_dot_1_max'] == pytest.approx( 0.5205, abs=0.0001) phase_flags = ['ML', 'HeCoreBurning', 'He-WD'] res = extract_mesa.extract_parameters(data, parameters, phase_flags=phase_flags) res = {k: v for k, v in zip(parameters + phase_flags, res)} assert res['ML'] is True assert res['HeCoreBurning'] is True assert res['He-WD'] is False
def test_dewi_tauris2000(data): data, _, profiles = fileio.read_compressed_track( base_path / 'test_data/M1.080_M0.502_P192.67_Z0.01129.h5', return_profiles=True) stable, ce_age = common_envelope.is_stable(data, criterion='Mdot', value=-2) data = data[data['age'] <= ce_age] profile = profiles['profile_1_mdot-2.0'] af, M1_final = common_envelope.dewi_tauris2000(data, profile, a_th=0) assert af == pytest.approx(4.8597844860, abs=0.00001) assert M1_final == pytest.approx(0.38321120, abs=0.00001) af, M1_final = common_envelope.dewi_tauris2000(data, profile, a_th=1.0) assert af == pytest.approx(9.17843738836, abs=0.00001) assert M1_final == pytest.approx(0.39115816046997015, abs=0.00001)
def test_extract_parameters_ml(self, base_path): """ Test that the ML phases are correctly returned. n_ml_phases = 0: result = [<val>, <val>] n_ml_phases = 1: result = [<val>, [<val>]] n_ml_phases = 2: result = [<val>, [<val>, <val>]] """ parameters = ['period_days__init', 'period_days__ML'] data, _ = fileio.read_compressed_track( base_path / 'test_data/M2.341_M1.782_P8.01_Z0.01412.h5') result = extract_mesa.extract_parameters(data, parameters=parameters, phase_flags=[], n_ml_phases=0) assert len(result) == 2 assert type(result[1]) != list result = extract_mesa.extract_parameters(data, parameters=parameters, phase_flags=[], n_ml_phases=1) assert len(result) == 2 assert type(result[1]) == list assert len(result[1]) == 1 result = extract_mesa.extract_parameters(data, parameters=parameters, phase_flags=[], n_ml_phases=2) assert len(result) == 2 assert type(result[1]) == list assert len(result[1]) == 2
def test_ml_phase(self, base_path): # -- 1 Mass loss phase consisting of a long wind ml phase and a short RLOF phase -- data, _ = fileio.read_compressed_track(base_path / 'test_data/M0.814_M0.512_P260.18_Z0.h5', return_profiles=False) # test returned ages a1, a2 = evolution_phases.ML(data, mltype='rlof', return_age=True) assert a1 == pytest.approx(12316067393.354174, abs=0.001) assert a2 == pytest.approx(12316911998.086807, abs=0.001) a1, a2 = evolution_phases.ML(data, mltype='wind', return_age=True) assert a1 == pytest.approx(12283352829.773027, abs=0.001) assert a2 == pytest.approx(12316994026.181108, abs=0.001) a1, a2 = evolution_phases.ML(data, mltype='total', return_age=True) assert a1 == pytest.approx(12283352829.773027, abs=0.001) assert a2 == pytest.approx(12316994026.181108, abs=0.001) # check that ML returns same start and end as MLstart and MLend a1_ml, a2_ml = evolution_phases.ML(data, mltype='rlof', return_age=True) a1_start = evolution_phases.MLstart(data, mltype='rlof', return_age=True) a2_end = evolution_phases.MLend(data, mltype='rlof', return_age=True) assert a1_ml == a1_start assert a2_ml == a2_end s_ml = evolution_phases.ML(data, mltype='rlof', return_age=False) s_start = evolution_phases.MLstart(data, mltype='rlof', return_age=False) s_end = evolution_phases.MLend(data, mltype='rlof', return_age=False) assert data['model_number'][s_ml][0] == data['model_number'][s_start][0] assert data['model_number'][s_ml][-1] == data['model_number'][s_end][0] # -- 4 Mass loss phases: first with both rlof and wind, the other 3 only wind -- data, _ = fileio.read_compressed_track(base_path / 'test_data/M1.276_M1.140_P333.11_Z0.h5', return_profiles=False) # test returned ages ages = evolution_phases.ML(data, mltype='rlof', return_age=True, return_multiple=True) assert len(ages) == 1 a1, a2 = ages[0] assert a1 == pytest.approx(3461120558.9109983, abs=0.001) assert a2 == pytest.approx(3462394022.0863924, abs=0.001) ages = evolution_phases.ML(data, mltype='wind', return_age=True, return_multiple=True) assert len(ages) == 4 a1, a2 = ages[0] assert a1 == pytest.approx(3440285863.557928, abs=0.001) assert a2 == pytest.approx(3462498981.825389, abs=0.001) ages = evolution_phases.ML(data, mltype='total', return_age=True, return_multiple=True) assert len(ages) == 4 a1, a2 = ages[1] assert a1 == pytest.approx(3462515931.0189767, abs=0.001) assert a2 == pytest.approx(3463059499.121973, abs=0.001) a1, a2 = ages[2] assert a1 == pytest.approx(3572455931.1686773, abs=0.001) assert a2 == pytest.approx(3576048979.7216263, abs=0.001) a1, a2 = ages[3] assert a1 == pytest.approx(3576114881.085102, abs=0.001) assert a2 == pytest.approx(3576115123.911062, abs=0.001) # -- 2 Mass loss phases both with wind and rlof mass loss -- data, _ = fileio.read_compressed_track(base_path / 'test_data/M2.341_M1.782_P8.01_Z0.01412.h5', return_profiles=False) # RLOF ages = evolution_phases.ML(data, mltype='rlof', return_age=True, return_multiple=True) assert len(ages) == 2 a1, a2 = ages[0] assert a1 == pytest.approx(607563161.616631, abs=0.001) assert a2 == pytest.approx(617932607.0240884, abs=0.001) a1, a2 = ages[1] assert a1 == pytest.approx(970323465.489477, abs=0.001) assert a2 == pytest.approx(975582471.8171717, abs=0.001) # Wind ages = evolution_phases.ML(data, mltype='wind', return_age=True, return_multiple=True) assert len(ages) == 2 a1, a2 = ages[0] assert a1 == pytest.approx(612221727.4877452, abs=0.001) assert a2 == pytest.approx(619258765.4160738, abs=0.001) a1, a2 = ages[1] assert a1 == pytest.approx(971574716.1957985, abs=0.001) assert a2 == pytest.approx(976109951.0074742, abs=0.001) # Total ages = evolution_phases.ML(data, mltype='total', return_age=True, return_multiple=True) assert len(ages) == 2 a1, a2 = ages[0] assert a1 == pytest.approx(607563161.616631, abs=0.001) assert a2 == pytest.approx(619258765.4160738, abs=0.001) a1, a2 = ages[1] assert a1 == pytest.approx(970323465.489477, abs=0.001) assert a2 == pytest.approx(976109951.0074742, abs=0.001)
def test_get_all_phases(self, base_path): phase_names = ['init', 'final', 'MS', 'MSstart', 'MSend', 'RGB', 'RGBstart', 'RGBend', 'MLstart', 'MLend', 'ML', 'CE', 'CEstart', 'CEend', 'HeIgnition', 'HeCoreBurning', 'HeShellBurning'] # test checking if all parameters are available. data, _ = fileio.read_compressed_track(base_path / 'test_data/M0.789_M0.304_P20.58_Z0.h5', return_profiles=False) data = data[['age', 'period_days']] with pytest.raises(ValueError): phases = evolution_phases.get_all_phases(['MS'], data) # stable model without He ignition and struggles at the end # age of the last 1470 time steps doesn't change! data, _ = fileio.read_compressed_track(base_path / 'test_data/M0.789_M0.304_P20.58_Z0.h5', return_profiles=False) phases = evolution_phases.get_all_phases(phase_names, data) assert data['model_number'][phases['init']][0] == 3 assert data['model_number'][phases['final']][0] == 30000 assert data['model_number'][phases['MS']][0] == 3 assert data['model_number'][phases['MS']][-1] == 114 assert data['model_number'][phases['MSstart']][0] == 3 assert data['model_number'][phases['MSend']][0] == 114 assert data['model_number'][phases['RGB']][0] == 114 assert data['model_number'][phases['RGB']][-1] == 948 assert data['model_number'][phases['RGBstart']][0] == 114 assert data['model_number'][phases['RGBend']][0] == 948 assert data['model_number'][phases['MLstart']][0] == 936 assert data['model_number'][phases['MLend']][0] == 30000 assert data['model_number'][phases['ML']][0] == 936 assert data['model_number'][phases['ML']][-1] == 30000 assert phases['HeIgnition'] is None # stable model without He ignition data, _ = fileio.read_compressed_track(base_path / 'test_data/M0.814_M0.512_P260.18_Z0.h5', return_profiles=False) phases = evolution_phases.get_all_phases(phase_names, data) assert data['model_number'][phases['RGB']][0] == 111 assert data['model_number'][phases['RGB']][-1] == 6570 assert data['model_number'][phases['RGBstart']][0] == 111 assert data['model_number'][phases['RGBend']][0] == 6570 assert data['model_number'][phases['ML']][0] == 6006 assert data['model_number'][phases['ML']][-1] == 7098 assert phases['HeIgnition'] is None assert phases['HeCoreBurning'] is None assert phases['HeShellBurning'] is None # stable model with degenerate He ignition but issues in the He burning phase, and a double ML phase data, _ = fileio.read_compressed_track(base_path / 'test_data/M1.125_M0.973_P428.86_Z0.h5', return_profiles=False) phases = evolution_phases.get_all_phases(phase_names, data) assert data['model_number'][phases['ML']][0] == 8970 assert data['model_number'][phases['ML']][-1] == 14406 assert data['model_number'][phases['HeIgnition']][0] == 19947 assert phases['HeCoreBurning'] is None assert phases['HeShellBurning'] is None assert phases['CE'] is None assert phases['CEstart'] is None assert phases['CEend'] is None # CE model data, _ = fileio.read_compressed_track(base_path / 'test_data/M1.205_M0.413_P505.12_Z0.h5', return_profiles=False) data = data[data['model_number'] <= 12111] data['CE_phase'][-1] = 1 data['CE_phase'][-2] = 1 phases = evolution_phases.get_all_phases(phase_names, data) assert data['model_number'][phases['ML']][0] == 11295 assert data['model_number'][phases['ML']][-1] == 12111 assert data['model_number'][phases['CE']][0] == 12108 assert data['model_number'][phases['CE']][1] == 12111 assert data['model_number'][phases['CEstart']][0] == 12108 assert data['model_number'][phases['CEend']][0] == 12111 assert phases['HeIgnition'] is None assert phases['HeCoreBurning'] is None assert phases['HeShellBurning'] is None # HB star with core and shell He burning data, _ = fileio.read_compressed_track(base_path / 'test_data/M1.276_M1.140_P333.11_Z0.h5', return_profiles=False) phases = evolution_phases.get_all_phases(phase_names, data) assert data['model_number'][phases['ML']][0] == 8823 assert data['model_number'][phases['ML']][-1] == 11892 assert data['model_number'][phases['HeIgnition']][0] == 11709 assert data['model_number'][phases['HeCoreBurning']][0] == 12492 assert data['model_number'][phases['HeCoreBurning']][-1] == 12594 assert data['model_number'][phases['HeShellBurning']][0] == 12597 assert data['model_number'][phases['HeShellBurning']][-1] == 14268 # sdB star with core and shell He burning data, _ = fileio.read_compressed_track(base_path / 'test_data/M1.269_M1.229_P133.46_Z0.00320.h5', return_profiles=False) phases = evolution_phases.get_all_phases(['sdA', 'sdB', 'sdO'], data) assert phases['sdA'] is None assert data['model_number'][phases['sdB']][0] == 22608 assert data['model_number'][phases['sdB']][-1] == 22689 assert phases['sdO'] is None a1, a2 = evolution_phases.HeCoreBurning(data, return_age=True) assert a1 == pytest.approx(3232213210.6798477, abs=0.001) assert a2 == pytest.approx(3316814816.4952917, abs=0.001)
def extract_mesa(file_list, stability_criterion='J_div_Jdot_div_P', stability_limit=10, n_ml_phases=0, ce_formalism='iben_tutukov1984', ce_parameters={'al': 1}, ce_profile_name=None, parameters=[], phase_flags=[], extra_info_parameters=[], add_setup_pars_to_result=True, verbose=False, flatten_output=False, **kwargs): parameters, column_names = _process_parameters(parameters) extra_info_parameters, extra_names = _process_parameters( extra_info_parameters) columns = ['path', 'stability', 'n_ML_phases' ] + extra_names + column_names + phase_flags if add_setup_pars_to_result: columns += [ 'stability_criterion', 'stability_limit', 'ce_profile_name', 'ce_formalism', 'ce_parameters' ] columns += ['error_flags'] results = [] # check if the same extraction parameters are used for all models, or if specific parameters are already # provided in the files list file_list = process_file_list(file_list, stability_criterion=stability_criterion, stability_limit=stability_limit, ce_formalism=ce_formalism, ce_parameters=ce_parameters, ce_profile_name=ce_profile_name, verbose=verbose) for i, model in file_list.iterrows(): if verbose: print(i, model['path']) # 1: Get the data try: data, extra_info, profiles = fileio.read_compressed_track( model['path'], return_profiles=True) except Exception as e: if verbose: print(e) continue # 2: check for stability and cut data at start of CE stable, ce_age = common_envelope.is_stable( data, criterion=model['stability_criterion'], value=model['stability_limit']) stability = 'stable' if not stable: # if the model is not stable, cut of the evolution at the start of the CE as anything after than # is non physical anyway. data = data[data['age'] <= ce_age] if ce_profile_name is not None: try: profiles = profiles[model['ce_profile_name']] except Exception: # todo: deal correctly with the missing profile! print('CE: profile missing') data = common_envelope.apply_ce(data, profiles=profiles, ce_formalism=model['ce_formalism'], **model['ce_parameters']) # check if CE is ejected or if the system is a merger or a contact binary s = np.where((data['star_2_radius'] >= 0.99 * data['rl_2']) & (data['star_1_radius'] >= 0.99 * data['rl_1'])) if data['binary_separation'][-1] <= 0: stability = 'merger' print('CE: Merged') elif len(data['model_number'][s]) > 0: stability = 'contact' print('CE: Contact') else: stability = 'CE' # 3: extract some standard parameters: Path, stability and nr of ML phases. pars = [model['path'].split('/')[-1]] pars += [stability, count_ml_phases(data)] # 4: add the extra info to the output for p in extra_info_parameters: pars.append(extra_info[p]) # 5: extract the requested parameters & 6: add the requested phase flags extracted_pars = extract_parameters(data, parameters, phase_flags, n_ml_phases=n_ml_phases) pars += extracted_pars # 7: Add the extraction setup parameters if requested if add_setup_pars_to_result: setup_pars = [ model['stability_criterion'], model['stability_limit'], model['ce_profile_name'], model['ce_formalism'], model['ce_parameters'] ] pars += setup_pars # 8: todo: check for some possible errors and flag them error_flags = evolution_errors.check_error_flags( data, extra_info['termination_code']) pars.append(error_flags) results.append(pars) results = pd.DataFrame(results, columns=columns) if flatten_output: # go over all columns. If a column contains a list instead of a value flatten that column results = _flatten_dataframe(results, n_ml_phases) return results