Skip to content

mors.star

star

Module holding the Star class and related functions.

MstarMax = 1.25 module-attribute

MstarMin = 0.1 module-attribute

log = logging.getLogger('fwl.' + __name__) module-attribute

Star(Mstar=None, Age=None, percentile=None, Omega=None, Prot=None, OmegaEnv=None, OmegaCore=None, AgesOut=None, starEvoDir=None, evoModels=None, params=params.paramsDefault)

A class for star objects that hold all information about a star.

This is the main function that is run when creating an instance of the Star class and it sets up all the things needed including calculating evolutionary tracks for the star.

The function requires that the arguments Mstar (the star's mass in Msun) and Omega (in OmegaSun=2.67e-6 rad s^-1) are specified in the call. Alternatively, OmegaEnv and OmegaCore can be set, in which case Omega does not need to be specified. If the argument Age (in Myr) is also specified, then the code will find the evolutionary track that passes through this rotation rate at this age, otherwise if Age is not set then it will calculate evolutionary tracks assuming this Omega as the initial (1 Myr) rotation rate.

The user should not specify OmegaCore and Age simultaneously, and if Age is set then either Omega or OmegaEnv can be used to specify the surface rotation rate.

The user can also specify the initial rotation rate using using a percentile of the 1 Myr rotation distribution via the percentile keyword argument.

Parameters:

Name Type Description Default
Mstar float

Stellar mass in Msun.

None
Age float

Age in Myr of the input rotation rate (default = 1 Myr).

None
Omega float

Rotation rate of star at Age in OmegaSun.

None
Prot float

Rotation period of star at Age in days.

None
OmegaEnv float

Envelope rotation rate of star at Age in OmegaSun.

None
OmegaCore float

Core rotation rate of star at Age in OmegaSun.

None
percentile float

Percentile in 1 Myr distribution (between 0 and 100).

None
params dict

Parameter dictionary used for getting width of bin to use for distribution.

paramsDefault

Returns:

Type Description
None

None

Source code in src/mors/star.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def __init__(self,Mstar=None,Age=None,percentile=None,Omega=None,Prot=None,OmegaEnv=None,OmegaCore=None,AgesOut=None,starEvoDir=None,evoModels=None,params=params.paramsDefault):
    """Initialises instance of Star class.

    This is the main function that is run when creating an instance of the Star class and it sets up all
    the things needed including calculating evolutionary tracks for the star. 

    The function requires that the arguments Mstar (the star's mass in Msun) and Omega (in OmegaSun=2.67e-6 rad s^-1) are specified
    in the call. Alternatively, OmegaEnv and OmegaCore can be set, in which case Omega does not need to
    be specified. If the argument Age (in Myr) is also specified, then the code will find the evolutionary
    track that passes through this rotation rate at this age, otherwise if Age is not set then it will
    calculate evolutionary tracks assuming this Omega as the initial (1 Myr) rotation rate. 

    The user should not specify OmegaCore and Age simultaneously, and if Age is set then either Omega or OmegaEnv can be
    used to specify the surface rotation rate. 

    The user can also specify the initial rotation rate using using a percentile of the 1 Myr rotation distribution via the ``percentile`` keyword argument.

    Parameters
    ----------
    Mstar : float
        Stellar mass in Msun.
    Age : float , optional
        Age in Myr of the input rotation rate (default = 1 Myr).
    Omega : float , optional
        Rotation rate of star at Age in OmegaSun.
    Prot : float , optional
        Rotation period of star at Age in days.
    OmegaEnv : float , optional
        Envelope rotation rate of star at Age in OmegaSun.
    OmegaCore : float , optional
        Core rotation rate of star at Age in OmegaSun.
    percentile : float , optional
        Percentile in 1 Myr distribution (between 0 and 100).

    params : dict , optional
        Parameter dictionary used for getting width of bin to use for distribution.

    Returns
    ----------
    None
        None

    """

    # Make sure Mstar has good values
    _CheckInputMstar(Mstar)

    # Make sure Omega, OmegaEnv, and OmegaCore are well set
    # (if Omega is set, OmegaEnv and OmegaCore will both be set to that value)
    Omega , OmegaEnv , OmegaCore = _InputRotation(Mstar,Age,Omega,OmegaEnv,OmegaCore,Prot,percentile,params)

    # Set parameters
    self.params = params

    # Set the ExtendedTracks parameter to True so we get all parameters
    self.params['ExtendedTracks'] = True

    # Set Mstar
    self.Mstar = Mstar

    # Set stellar evo model
    self.starEvoDir = starEvoDir
    self.evoModels = evoModels

    # Load evo tracks if Mstar is set
    self._LoadStarEvo()

    # Get evolutionary tracks
    self._LoadEvoTracks(Age,OmegaEnv,OmegaCore,AgesOut=AgesOut)

    # Get HZ boundaries
    self.aOrbHZ = phys.aOrbHZ(Mstar=self.Mstar,params=self.params)

    return

ActivityLifetime(Quantity=None, Threshold=None, AgeMax=None)

Takes threshold value, returns age at which a star last drops below this threshold.

This function can be used to determine when a star's emission crosses a given threshold value for a few activity quantities. These are Lx, Fx, Rx, and FxHZ for X-rays, and similar values for EUV1, EUV2, EUV, XUV, and Ly-alpha. If the star crosses the threshold (from above it to below it) multiple times, this will find the final time it will cross the threshold. If the user wants to set a maximum age so that the code only looks for crossings of the threshold below this age then this can be done using the AgeMax keyword argument.

Parameters:

Name Type Description Default
Quantity str

Gives which quantity to consider (e.g. 'Lx').

None
Threshold float or str

Value for threshold in units of quantity or string of 'sat'.

None
AgeMax float

End age of track to consider in Myr.

None

Returns:

Name Type Description
AgeActive float

Activity lifetime in Myr.

Source code in src/mors/star.py
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
def ActivityLifetime(self,Quantity=None,Threshold=None,AgeMax=None):
    """Takes threshold value, returns age at which a star last drops below this threshold.

    This function can be used to determine when a star's emission crosses a given threshold value for a few
    activity quantities. These are Lx, Fx, Rx, and FxHZ for X-rays, and similar values for EUV1, EUV2, EUV,
    XUV, and Ly-alpha. If the star crosses the threshold (from above it to below it) multiple times, this
    will find the final time it will cross the threshold. If the user wants to set a maximum age so that the
    code only looks for crossings of the threshold below this age then this can be done using the AgeMax
    keyword argument.

    Parameters
    ----------
    Quantity : str
        Gives which quantity to consider (e.g. 'Lx').
    Threshold : float or str
        Value for threshold in units of quantity or string of 'sat'.
    AgeMax : float , optional
        End age of track to consider in Myr.

    Returns
    ----------
    AgeActive : float
        Activity lifetime in Myr.

    """

    # Allowed quantities
    QuantitiesAllowed = [ 'Lx' , 'Fx' , 'Rx' , 'FxHZ' , 'Leuv1' , 'Feuv1' , 'Reuv1' , 'Feuv1HZ' , 'Leuv2' , 'Feuv2' , 'Reuv2' , 'Feuv2HZ' ,
                        'Leuv' , 'Feuv' , 'Reuv' , 'FeuvHZ' , 'Lly' , 'Fly' , 'Rly' , 'FlyHZ' ]

    # Make sure Quantity is set
    if Quantity is None:
        raise Exception("Quantity not set in call to function")

    # Make sure Quantity is string
    if not isinstance(Quantity,str):
        raise Exception("Quantity must be string")

    # Check input Quantity is valid
    if not Quantity in QuantitiesAllowed:
        QuantitiesString = ''
        for quantity in QuantitiesAllowed:
            QuantitiesString += quantity + " , "
        raise Exception("invalid quantity "+Quantity+"\n valid options are "+QuantitiesString[0:-3])

    # Set tracks
    AgeTrack = self.AgeTrack
    Track = self.Tracks[Quantity]

    # If Threshold is 'sat' then normalise track to saturation value and set threshold to unity
    if ( Threshold == 'sat' ):

        # Get saturation rotation rate as function of age
        OmegaSatTrack = phys.OmegaSat( Mstar=self.Mstar , Age=self.AgeTrack , param='XUV' , params=self.params , StarEvo=self.StarEvo )

        # Make track just OmegaEnv/OmegaSat
        Track = self.OmegaEnvTrack / OmegaSatTrack

        # Set threshold to unity
        Threshold = 1.0

    # Get result
    AgeActive = misc.ActivityLifetime( Age=AgeTrack , Track=Track , Threshold=Threshold , AgeMax=AgeMax )

    return AgeActive

IntegrateEmission(AgeMin=None, AgeMax=None, Band=None, aOrb=None)

Takes age range, calculates integrated emission in band within that range.

This code can be used to integrate a star's luminosity between two ages. This can be applied to any wavelength band and the result is a total energy emitted in this time in erg. If the user also specifies an orbital distance using the aOrb keyword argument, the code integrates the flux at this obital distance returns a fluence in erg cm^-2. The user can specify aOrb as a string to get the fluences at various habitable zone boundaries (using the HZ calculated at the age defined in params when creating this cluster). Options are 'RecentVenus', 'RunawayGreenhouse', 'MoistGreenhouse', 'MaximumGreenhouse', 'EarlyMars', and 'HZ'.

Parameters:

Name Type Description Default
AgeMin float

Start of time period to integrate in Myr.

None
AgeMax float

End of time period to integrate in Myr.

None
Band str

Gives which wavelength band to consider (options are 'XUV', 'Xray', 'EUV1', 'EUV2', 'EUV', 'Lyman', 'bol').

None
aOrb float or str

Orbital distance to get fluence at in AU or string identifying HZ boundary.

None

Returns:

Name Type Description
Energy float

Integrated luminosity or flux in erg or erg cm^-2.

Source code in src/mors/star.py
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
def IntegrateEmission(self,AgeMin=None,AgeMax=None,Band=None,aOrb=None):
    """Takes age range, calculates integrated emission in band within that range.

    This code can be used to integrate a star's luminosity between two ages. This can be applied to any wavelength band
    and the result is a total energy emitted in this time in erg. If the user also specifies an orbital distance using
    the aOrb keyword argument, the code integrates the flux at this obital distance returns a fluence in erg cm^-2. The
    user can specify aOrb as a string to get the fluences at various habitable zone boundaries (using the HZ calculated
    at the age defined in params when creating this cluster). Options are 'RecentVenus', 'RunawayGreenhouse', 'MoistGreenhouse',
    'MaximumGreenhouse', 'EarlyMars', and 'HZ'.

    Parameters
    ----------
    AgeMin : float
        Start of time period to integrate in Myr.
    AgeMax : float
        End of time period to integrate in Myr.
    Band : str
        Gives which wavelength band to consider (options are 'XUV', 'Xray', 'EUV1', 'EUV2', 'EUV', 'Lyman', 'bol').
    aOrb : float or str , optional
        Orbital distance to get fluence at in AU or string identifying HZ boundary.

    Returns
    ----------
    Energy : float
        Integrated luminosity or flux in erg or erg cm^-2.

    """

    # Allowed quantities
    BandsAllowed = [ 'XUV', 'Xray', 'EUV1', 'EUV2', 'EUV', 'Lyman', 'bol' ]

    # Make sure Band is set
    if Band is None:
        raise Exception("Band not set in call to function")

    # Make sure Band is string
    if not isinstance(Band,str):
        raise Exception("Band must be string")

    # Check input Band is valid
    if not Band in BandsAllowed:
        BandsString = ''
        for band in BandsAllowed:
            BandsString += band + " , "
        raise Exception("invalid Band "+Band+"\n valid options are "+BandsString[0:-3])

    # Get luminosity track to use
    if Band == 'XUV':
        Luminosity = self.LxTrack + self.LeuvTrack
    elif Band == 'Xray':
        Luminosity = self.LxTrack
    elif Band == 'EUV1':
        Luminosity = self.Leuv1Track
    elif Band == 'EUV2':
        Luminosity = self.Leuv2Track
    elif Band == 'EUV':
        Luminosity = self.LeuvTrack
    elif Band == 'Lyman':
        Luminosity = self.LlyTrack
    elif Band == 'bol':
        Luminosity = self.LbolTrack
    else:
        raise Exception("did not find right luminosity track")

    # If aOrb was set to a string, do necessary work to get useable aOrb (given by aOrbUse)
    if isinstance(aOrb,str):

        # Valid options
        aOrbAllowed = [ 'RecentVenus' , 'RunawayGreenhouse' , 'MoistGreenhouse' , 'MaximumGreenhouse' , 'EarlyMars' , 'HZ' ]

        # Check input is valid
        if not aOrb in aOrbAllowed:
            aOrbString = ''
            for a in aOrbAllowed:
                aOrbString += a + " , "
            raise Exception("invalid aOrb "+aOrb+"\n valid options are a value in AU or "+aOrbString[0:-3])

        # Get orbital distance
        aOrbUse = self.aOrbHZ[aOrb]

    else:

        # Just set to input value
        aOrbUse = aOrb

    # Do calculation
    Energy = misc.IntegrateEmission( AgeMin=AgeMin , AgeMax=AgeMax , Age=self.AgeTrack , Luminosity=Luminosity , aOrb=aOrbUse )

    return Energy

PrintAvailableTracks()

Prints all quantities that have evolutionary tracks available.

Source code in src/mors/star.py
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
def PrintAvailableTracks(self):
    """Prints all quantities that have evolutionary tracks available."""

    # Print header
    log.info("")
    log.info("EVOLUTIONARY TRACKS AVAILABLE FOR:-")

    # Loop over each element in Tracks and print them but only if they are numpy arrays
    for track in self.Tracks:
        if ( type(self.Tracks[track]).__module__ == 'numpy' ):

            # Start string with small space and name of quantity
            outString = "   "+track

            # If the units are given, add that in parentheses
            if not ( self.Units[track] == '' ):
                outString += " ("+self.Units[track]+")"

            # Print the string
            log.info(outString)

    # A free line at the end to look better
    log.info("")

    return

Save(filename='star.pickle')

Takes filename (default is 'star.pickle'), saves cluster to this file using pickle.

Source code in src/mors/star.py
408
409
410
411
412
def Save(self,filename='star.pickle'):
    """Takes filename (default is 'star.pickle'), saves cluster to this file using pickle."""
    with open(filename,'wb') as f:
        pickle.dump(self,f)
    return

Track(Quantity=None)

Takes a quantity string, return evolutionary track for that quantity.

Source code in src/mors/star.py
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
def Track(self,Quantity=None):
    """Takes a quantity string, return evolutionary track for that quantity."""

    # Make sure Quantity is set
    if Quantity is None:
        raise Exception("Quantity not set in call to function")

    # Make sure Quantity is string
    if not isinstance(Quantity,str):
        raise Exception("Quantity must be string")

    # Make sure Quantity is a valid quantity with a track
    if not Quantity in self.Tracks:
        raise Exception("no available track for "+Quantity)

    return self.Tracks[Quantity]

Value(Age=None, Quantity=None)

Takes age in Myr and a string with name of quantity to output, returns value of that quantity at specified age.

Source code in src/mors/star.py
357
358
359
360
361
362
363
def Value(self,Age=None,Quantity=None):
    """Takes age in Myr and a string with name of quantity to output, returns value of that quantity at specified age."""

    # Do 1D linear interpolation to get value
    value = SE._Interpolate1D( self.Tracks['Age'] , self.Tracks[Quantity] , Age )

    return value

save(filename='star.pickle')

Same as Save().

Source code in src/mors/star.py
414
415
416
417
def save(self,filename='star.pickle'):
    """Same as Save()."""
    self.Save(filename=filename)
    return

Percentile(Mstar=None, Omega=None, Prot=None, percentile=None, MstarDist=None, OmegaDist=None, ProtDist=None, params=params.paramsDefault)

Gets rotation rate of percentile or percentile of rotation rate in the model rotation distribution.

This function can be used for two purposes 1. to determine the percentile in a rotation distribution of a star given its mass and rotation rate 2. to determine the rotation rate of a star in a rotation distribution given its mass and percentile In the first case, the user should specify the rotation rate using either the Omega or Prot keyword arguments (given in OmegaSun and days respectively) and the mass. In the second case, the user should specify the percentile using the percentile keyword argument (in the range 0 to 100) and the mass. The mass should be specified in Msun using the Mstar keyword argument. These should only be given as floats. The user can also specify the rotation distribution using the MstarDist and the OmegaDist or ProtDist keyword arguments. If this is not done, then the code will assume the 1 Myr rotation distribution from the empirical model distribution used in Johnstone et al. (2020).

Parameters:

Name Type Description Default
Mstar float

Stellar mass in Msun.

None
Omega float

Rotation rate of star in OmegaSun.

None
Prot float

Rotation period of star in days.

None
percentile float

percentile in distribution (between 0 and 100).

None
MstarDist ndarray

Array of masses of stars in distribution.

None
OmegaDist ndarray

Array of rotation rates of stars in distribution in OmegaSun.

None
ProtDist ndarray

Array of rotation periods of stars in distribution in days.

None
params dict

Parameter dictionary used for getting width of bin to use for distribution.

paramsDefault

Returns:

Name Type Description
result float

Either rotation rate for percentile or percentile for rotation rate.

Source code in src/mors/star.py
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
def Percentile(Mstar=None,Omega=None,Prot=None,percentile=None,MstarDist=None,OmegaDist=None,ProtDist=None,params=params.paramsDefault):
    """Gets rotation rate of percentile or percentile of rotation rate in the model rotation distribution.

    This function can be used for two purposes
        1. to determine the percentile in a rotation distribution of a star given its mass and rotation rate
        2. to determine the rotation rate of a star in a rotation distribution given its mass and percentile
    In the first case, the user should specify the rotation rate using either the Omega or Prot keyword arguments (given
    in OmegaSun and days respectively) and the mass. In the second case, the user should specify the percentile using the
    percentile keyword argument (in the range 0 to 100) and the mass. The mass should be specified in Msun using the Mstar
    keyword argument. These should only be given as floats. The user can also specify the rotation distribution using the
    MstarDist and the OmegaDist or ProtDist keyword arguments. If this is not done, then the code will assume the 1 Myr
    rotation distribution from the empirical model distribution used in Johnstone et al. (2020).

    Parameters
    ----------
    Mstar : float
        Stellar mass in Msun.
    Omega : float , optional
        Rotation rate of star in OmegaSun.
    Prot : float , optional
        Rotation period of star in days.
    percentile : float , optional
        percentile in distribution (between 0 and 100).
    MstarDist : numpy.ndarray , optional
        Array of masses of stars in distribution.
    OmegaDist : numpy.ndarray , optional
        Array of rotation rates of stars in distribution in OmegaSun.
    ProtDist : numpy.ndarray , optional
        Array of rotation periods of stars in distribution in days.
    params : dict , optional
        Parameter dictionary used for getting width of bin to use for distribution.

    Returns
    ----------
    result : float
        Either rotation rate for percentile or percentile for rotation rate.

    """

    # Make sure Mstar was set
    if Mstar is None:
        raise Exception( "keyword argument Mstar must be set" )

    # If percentile is not set, make sure rotation rate is set
    if percentile is None:
        # Not both
        if not Omega is None:
            if not Prot is None:
                raise Exception( "keyword arguments Omega and Prot cannot both be set" )
        # At least one
        if ( Omega is None ) and ( Prot is None ):
            raise Exception( "must set either Omega, Prot, or percentile keyword arguments" )
        # Set Omega is not set then set it
        if Omega is None:
            Omega = phys._Omega(Prot)

    # Setup the distribution
    if MstarDist is None:
        MstarDist , OmegaDist = misc.ModelCluster()
    else:

        # Check that one of OmegaDist and ProtDist are set
        if ( OmegaDist is None ) and ( ProtDist is None ):
            raise Exception( "one of keyword arguments OmegaDist and ProtDist must be set" )

        # Check that OmegaDist and ProtDist are not both set
        if ( not OmegaDist is None ) and ( not ProtDist is None ):
            raise Exception( "keyword arguments OmegaDist and ProtDist cannot both be set" )

        # Setup OmegaDist if ProtDist was set
        if OmegaDist is None:
            OmegaDist = misc._Omega(ProtDist)

        # Make sure arrays are same length
        if not ( len(MstarDist) == len(OmegaDist) ):
            raise Exception( "MstarDist and OmegaDist (or ProtDist) must be same length" )

    # Work out which one to do
    if not percentile is None:

        # percentile was set, so get corresponding rotation rates
        result = _OmegaPercentile(Mstar,percentile,MstarDist,OmegaDist,params)

    else:

        # Rotation rate was set, so get corresponding percentile
        result = _PerPercentile(Mstar,Omega,MstarDist,OmegaDist,params)


    return result

_CheckInputMstar(Mstar)

Takes stellar mass, checks if value is a valid input mass.

Source code in src/mors/star.py
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
def _CheckInputMstar(Mstar):
    """Takes stellar mass, checks if value is a valid input mass."""

    # Make sure Mstar was set
    if Mstar is None:
        raise Exception("stellar mass not given")

    # Make sure it is a float
    #if not isinstance(Mstar,float):
        #raise Exception("stellar mass must be given as float")

    # Make sure it above minimum
    if ( Mstar < MstarMin ):
        raise Exception( "stellar mass cannot be less than lower limit of "+str(MstarMin)+" Msun" )

    # Make sure it above minimum
    if ( Mstar > MstarMax ):
        raise Exception( "stellar mass cannot be greater than upper limit of "+str(MstarMax)+" Msun" )

    return

_InputRotation(Mstar, Age, Omega, OmegaEnv, OmegaCore, Prot, percentile, params)

Takes input rotation values, checks sets up values correctly.

Source code in src/mors/star.py
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
def _InputRotation(Mstar,Age,Omega,OmegaEnv,OmegaCore,Prot,percentile,params):
    """Takes input rotation values, checks sets up values correctly."""

    # Make sure percentile and rotation are not both set
    if not percentile is None:
        if not Omega is None :
            raise Exception( "cannot set both percentile and Omega as arguments of Star" )
        if not OmegaEnv is None :
            raise Exception( "cannot set both percentile and OmegaEnv as arguments of Star" )
        if not OmegaCore is None :
            raise Exception( "cannot set both percentile and OmegaCore as arguments of Star" )
        if not Prot is None :
            raise Exception( "cannot set both percentile and Prot as arguments of Star" )

    # Make sure percentile and OmegaEnv are not both set
    if ( not percentile is None ) and ( not OmegaEnv is None ):
        raise Exception( "cannot set both percentile and OmegaEnv as arguments of Star" )

    # If percentile was set to a string, get float version
    if isinstance(percentile,str):
        if ( percentile == 'slow' ):
            percentile = 5.0
        elif ( percentile == 'medium' ):
            percentile = 50.0
        elif ( percentile == 'fast' ):
            percentile = 95.0
        else:
            raise Exception( "invalid percentile string (options are 'slow', 'medium', or 'fast')" )

    # If percentile was set, get Omega
    if not percentile is None:
        Omega = Percentile(Mstar=Mstar,percentile=percentile,params=params)

    # Make sure if Age is set that OmegaCore is not set
    if ( not Age is None ) and ( not OmegaCore is None ):
        raise Exception( "cannot set both Age and OmegaCore as arguments of Star" )

    # Make sure not both Omega and Prot were set
    if ( not Omega is None ) and ( not Prot is None ):
        raise Exception( "cannot set both Omega and Prot as arguments of Star" )

    # If Prot is set, get Omega
    if not Prot is None:
        Omega = phys._Omega(Prot)

    # Make sure at least one rotation rate is set
    if ( Omega is None ):

        # If Age is set then make sure OmegaEnv is set, otherwise make sure both OmegaEnv and OmegaCore are set
        if ( not Age is None ):
            if ( OmegaEnv is None ):
                raise Exception( "if Age is set then must set either Omega or OmegaEnv as argument of Star" )
        elif ( OmegaEnv is None ) or ( OmegaCore is None ):
            raise Exception( "must set either Omega or both OmegaEnv and OmegaCore as argument of Star" )

    else:

        # Since Omega is set, make sure both OmegaEnv and OmegaCore are not set
        if not ( ( OmegaEnv is None ) and ( OmegaCore is None ) ):
            raise Exception( "cannot set OmegaEnv and OmegaCore when Omega is set as arugment of Star" )

        # Set both OmegaEnv and OmegaCore to Omega
        OmegaEnv = Omega
        OmegaCore = Omega

    return Omega , OmegaEnv , OmegaCore

_OmegaPercentile(Mstar, percentile, MstarDist, OmegaDist, params)

Gets rotation rate that corresponds to a given percentile of the rotation distribution.

Source code in src/mors/star.py
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
def _OmegaPercentile(Mstar,percentile,MstarDist,OmegaDist,params):
    """Gets rotation rate that corresponds to a given percentile of the rotation distribution."""

    ## If percentile was a string, get actual value

    # Get part of distribution to include
    includeStars = np.where( np.abs(MstarDist-Mstar) <= params['dMstarPer'] )

    # Need at least two stars
    if ( len(includeStars[0]) < 2 ):
        raise Exception( "need at least two stars in mass bin to get percentile" )

    # Get percentile Omega
    Omega = np.percentile( OmegaDist[includeStars] , percentile )

    return Omega

_PerPercentile(Mstar, Omega, MstarDist, OmegaDist, params)

Gets percentile of the rotation distribution for a given mass and rotation rate.

Source code in src/mors/star.py
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
def _PerPercentile(Mstar,Omega,MstarDist,OmegaDist,params):
    """Gets percentile of the rotation distribution for a given mass and rotation rate."""

    # Get part of distribution to include
    includeStars = np.where( np.abs(MstarDist-Mstar) <= params['dMstarPer'] )

    # Need at least two stars
    if ( len(includeStars[0]) < 2 ):
        raise Exception( "need at least two stars in mass bin to get percentile" )

    # If Omega is less than minimum then make this as the 0th percentile
    if ( Omega <= np.min(OmegaDist[includeStars]) ):
        return 0.0

    # If Omega is above maximum then make this as the 100th percentile
    if ( Omega >= np.max(OmegaDist[includeStars]) ):
        return 100.0

    # Use bisector to invert numpy.percentile function
    # Initial limits
    perMin, perMax = 0.0, 100.0
    tol = 1.0e-5
    nIterMax = 10000

    # Start iterating
    for iIter in range(nIterMax):
    # Get mid percentile and corresponding Omega
        perMid = 0.5 * ( perMin + perMax )
        OmegaMid = np.percentile( OmegaDist[includeStars] , perMid )

        # See if OmegaMid is equal to or close enough to answer
        if ( abs(OmegaMid/Omega-1.0) < tol ):
            return perMid

        # See which limit to change
        if OmegaMid > Omega:
            perMax = perMid
        else:
            perMin = perMid

    # If we get here, not converged
    raise Exception(
        f"did not find percentile for Omega={Omega} (Mstar={Mstar}) after {nIterMax} iterations; last bracket=({perMin},{perMax}), last mid={perMid}")