Exemplo n.º 1
0
def _CheckBadData(Tracks):
    """Takes current set of tracks, checks last entries for bad data."""

    # Originally assume no error and sould continue
    error = False

    # Make sure rotation rates are positive
    if (Tracks['OmegaEnv'][-1] <= 0.0):
        misc._PrintError(
            "negative value of OmegaEnv in rotational evolution calculation")
        error = True
    if (Tracks['OmegaCore'][-1] <= 0.0):
        misc._PrintError(
            "negative value of OmegaCore in rotational evolution calculation")
        error = True

    # Loop over elements in Tracks and check each one for NaN or Inf
    for track in Tracks:
        if math.isnan(Tracks[track][-1]):
            misc._PrintError("NaN found for " + track +
                             " in rotational evolution calculation")
            error = True
        if math.isinf(Tracks[track][-1]):
            misc._PrintError("inf found for " + track +
                             " in rotational evolution calculation")
            error = True

    # If there was an error, kill the code
    if error:
        misc._PrintErrorKill("ending simulation due to bad data found")

    return
Exemplo n.º 2
0
    def Values(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 for all stars.
    
    
    Parameters
    ----------
    Age : float
        Age of cluster.
    Quantity : str
        String holding name of parameter to get.
    
    Returns
    ----------
    values : numpy.ndarray
        Set of extended quantities.
    
    """

        # Make sure input parameters are set
        if Age is None:
            misc._PrintErrorKill(
                "keyword parameter Age not set in call to function")
        if Quantity is None:
            misc._PrintErrorKill(
                "keyword parameter Quantity not set in call to function")

        # Make array to hold values
        values = np.zeros(self.nStars)

        # Loop over stars and get values for each
        for iStar in range(0, self.nStars):
            values[iStar] = self.stars[iStar].Value(Age=Age, Quantity=Quantity)

        return values
Exemplo n.º 3
0
def _Interpolate1D(Xarray, Yarray, X):
    """Takes 1D arrays for corresponding X and Y values, returns interpolated Y value corresponding to input X."""

    # Note that it is assumed here that Xarray is in ascending order and this won't work if it is not

    # Round the input values to decimal places determined by nDecValue at top of file
    X = round(X, nDecValue)

    # Make sure X is in limits
    if not (Xarray[0] <= X <= Xarray[-1]):
        misc._PrintErrorKill("input value " + str(X) +
                             " is not within limits of " + str(Xarray[0]) +
                             " to " + str(Xarray[-1]))

    # Get index of X closest to but smaller than value
    iMin = misc._getIndexLTordered(Xarray, X)
    iMax = iMin + 1

    # Check if X is an element in Xarray
    if (Xarray[iMin] == X):
        return Yarray[iMin]
    elif (Xarray[iMax] == X):
        return Yarray[iMax]
    else:

        # Do linear interpolation
        mInterp = (Yarray[iMax] - Yarray[iMin]) / (Xarray[iMax] - Xarray[iMin])
        cInterp = Yarray[iMin] - mInterp * Xarray[iMin]
        Y = mInterp * X + cInterp

    return Y
Exemplo n.º 4
0
def _GaussianElimination(Ain, B):
    """Solves the equation A*X = B using Gaussian elimination."""

    # This is meant to be used when getting the k coefficients in the Rosenbrock
    # method. This subroutine is able to be relatively simple because the NxN
    # matrix A is given by I-dt*gamma*J, where I is the identity matrix. It is
    # highly unlikely that any of the diagonal terms in A are zero, since that
    # would only really be the case if a diagonal term in J is exactly 1.0/(dt*gamma),
    # which would be quite a coincidence. If any diagonal terms in A are zero,
    # this subroutine will give a nonsense result. I have added an if statement
    # below to catch these errors, so the user will at least know what is going on.

    #---------------------------------------
    # GET N AND ALLOCATE X AND A

    N = len(B)
    X = np.zeros(N)
    A = np.zeros((N, N))

    # ---------------------------------------
    # COPY A and B

    A[:, :] = Ain[:, :]
    X[:] = B[:]

    #---------------------------------------
    # GET IN ROW ECHELON FORMAT

    # Loop over rows
    for i in range(0, N):

        # Make sure diagonal term not zero
        if (A[i, i] == 0.0):
            misc._PrintErrorKill("diagonal term in zero")

        # Divide row by value in diagonal element
        X[i] = X[i] / A[i, i]
        A[i, i:N] = A[i, i:N] / A[i, i]

        # Get rid of zeros in columns below this diagonal
        # unless doing final row
        if (i < N - 1):
            for k in range(i + 1, N):
                if (A[k, i] != 0.0):
                    X[k] += -X[i] * A[k, i]
                    A[k, :] += -A[i, :] * A[k, i]

    #---------------------------------------
    # GET IN REDUCED ROW ECHELON FORMAT

    for i in range(N - 2, -1, -1):
        for j in range(N - 1, i, -1):
            if (A[i, j] != 0.0):
                X[i] += -A[i, j] * X[j]
                A[i, :] += -A[i, j] * A[j, :]

    #---------------------------------------

    return X
Exemplo n.º 5
0
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):
        misc._PrintErrorKill(
            "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 = 0.0
    perMax = 100.0

    # Omegas for limits
    OmegaMin = np.percentile(OmegaDist[includeStars], perMin)
    OmegaMax = np.percentile(OmegaDist[includeStars], perMax)

    # Tolerance and maximum number of iterations
    tol = 1.0e-5
    nIterMax = 10000

    # Start iterating
    found = False
    for iIter in range(nIterMax):

        # Get mid value and percentle
        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
            OmegaMax = OmegaMid
        else:
            perMin = perMid
            OmegaMin = OmegaMid

    # Make sure it was found
    if not found:
        misc._PrintErrorKill("did not find percentile " + str(per) +
                             " for Omega of " + str(Omega))

    return per
Exemplo n.º 6
0
def _ValueSingle(Mstar, Age, ParamString, ModelData=ModelDataDefault):
    """Takes stellar mass and age and parameter string, returns value of that parameter at that mass and age."""

    ## Make sure Mstar and age are floats
    #if not isinstance(Mstar,float):
    #misc._PrintErrorKill("argument Mstar must be float in call to Value")
    #if not isinstance(Age,float):
    #misc._PrintErrorKill("argument Age must be float in call to Value")

    # Make sure ParamString is indeed a string
    if not isinstance(ParamString, str):
        misc._PrintErrorKill(
            "argument ParamString must be string in call to Value")

    # Make sure ParamString corresponds to a valid parameter
    if not (ParamString in ModelData['ParamsAll']):
        misc._PrintErrorKill("parameter '" + ParamString + "' is not valid")

    # Check if an evolutionary track for this exact stellar mass is present
    if (Mstar in ModelData):

        # In this case, an evo track for this exact stellar mass is already loaded and all is needed is an interpolation
        # but first check the age is within the correct age limit
        _CheckAgeLimit(ModelData[Mstar]['Age'], Age)
        value = _Interpolate1D(ModelData[Mstar]['Age'],
                               ModelData[Mstar][ParamString], Age)

    else:

        # In this case, no evo track for this specific mass is loaded and so a 2D interpolation between values from two
        # separate mass bins is needed
        # Note, it is not assumed that the mass bins are loaded into the dictionary in ascending order

        # Make sure within mass limit
        _CheckMassLimit(ModelData['MstarAll'], Mstar)

        # Get nearest mass bin below
        iMin = misc._getIndexLT(ModelData['MstarAll'], Mstar)
        MstarMin = ModelData['MstarAll'][iMin]

        # Get nearest mass bin above
        iMax = misc._getIndexGT(ModelData['MstarAll'], Mstar)
        MstarMax = ModelData['MstarAll'][iMax]

        # Check age ranges for both (max first since the upper limit is likely lower)
        _CheckAgeLimit(ModelData[MstarMax]['Age'], Age)
        _CheckAgeLimit(ModelData[MstarMin]['Age'], Age)

        # Do interpolation
        value = _Interpolate2D(MstarMin, MstarMax, Mstar,
                               ModelData[MstarMin]['Age'],
                               ModelData[MstarMax]['Age'], Age,
                               ModelData[MstarMin][ParamString],
                               ModelData[MstarMax][ParamString])

    return value
Exemplo n.º 7
0
def _CheckMassLimit(MstarArray, Mstar):
    """Takes array of masses and an age, outputs error and stops code if mass is not within limits."""

    if not (np.min(MstarArray) <= Mstar <= np.max(MstarArray)):
        misc._PrintErrorKill("input stellar mass " + str(Mstar) +
                             " is not within limits of " +
                             str(np.min(MstarArray)) + " to " +
                             str(np.max(MstarArray)))

    return
Exemplo n.º 8
0
def _CheckAgeLimit(AgeArray, Age):
    """Takes age track and an age, outputs error and stops code if age is not within limits."""

    # Round the age to decimal places determined by nDecValue at top of file
    Age = round(Age, nDecValue)

    # Do check
    if not (AgeArray[0] <= Age <= AgeArray[-1]):
        misc._PrintErrorKill("input age " + str(Age) +
                             " is not within limits of " + str(AgeArray[0]) +
                             " to " + str(AgeArray[-1]))

    return
Exemplo n.º 9
0
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):
        misc._PrintErrorKill(
            "need at least two stars in mass bin to get percentile")

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

    return Omega
Exemplo n.º 10
0
    def ActivityLifetime(self, Quantity=None, Threshold=None, AgeMax=None):
        """
    Takes threshold value, returns ages at which each star last drops below this threshold.
    
    This function can be used to determine when each 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.
    
    """

        # Make sure Quantity is set
        if Quantity is None:
            misc._PrintErrorKill("Quantity not set in call to function")

        # Make sure Quantity is string
        if not isinstance(Quantity, str):
            misc._PrintErrorKill("Quantity must be string")

        # Make array
        AgeActive = np.zeros(self.nStars)

        # Loop over stars and get each
        for iStar in range(self.nStars):
            AgeActive[iStar] = self.stars[iStar].ActivityLifetime(
                Quantity=Quantity, Threshold=Threshold, AgeMax=AgeMax)

        return AgeActive
Exemplo n.º 11
0
def _CheckInputRotation(Age, Omega, OmegaEnv, OmegaCore):
    """Takes input rotation, checks if values are setup correctly."""

    # Make sure if Age is set that OmegaCore is not set
    if (not Age is None) and (not OmegaCore is None):
        misc._PrintErrorKill(
            "cannot set both Age and OmegaCore as arguments of Cluster")

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

        # Since Omega is not set, make sure both OmegaEnv and OmegaCore are set
        if (OmegaEnv is None) or (OmegaCore is None):
            misc._PrintErrorKill(
                "must set either Omega or both OmegaEnv and OmegaCore as arugment 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)):
            misc._PrintErrorKill(
                "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
Exemplo n.º 12
0
def _shouldAppend(Age, AgesOut):
    """Takes information about age in evolutionary calculation, returns if age should be output."""

    # If AgesOut is not set, then should always output
    if AgesOut is None:
        return AgesOut, True

    # If AgesOut is set, work out if this is one of the output ages
    if not AgesOut is None:

        # If AgesOut is just a number (float or int) then it is easy
        if isinstance(AgesOut, (float, int)):

            # Check if close enough to AgesOut to output
            if (abs(Age / float(AgesOut)) - 1.0 < 1.0e-6):
                return AgesOut, True
            else:
                return AgesOut, False

        else:

            # Get smallest distance between age in AgesOut and current Age
            indexMin = np.argmin(np.abs(AgesOut - Age))
            deltaAgeMin = np.min(np.abs(AgesOut - Age))

            # Since we are removing this output age each time, indexMin should always be zero
            if not indexMin == 0:
                misc._PrintErrorKill(
                    "indexMin is not zero, probably because the input AgesOut is not in ascending order"
                )

            # If age is close enough to element in AgesOut then remove this element and return True
            if deltaAgeMin < 1.0e-3:
                AgesOut = np.delete(AgesOut, indexMin)
                return AgesOut, True

            else:
                return AgesOut, False

    return AgesOut, False
Exemplo n.º 13
0
    def Track(self, Quantity=None):
        """Takes a quantity string, return evolutionary track for that quantity."""

        # Make sure Quantity is set
        if Quantity is None:
            misc._PrintErrorKill("Quantity not set in call to function")

        # Make sure Quantity is string
        if not isinstance(Quantity, str):
            misc._PrintErrorKill("Quantity must be string")

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

        return self.Tracks[Quantity]
Exemplo n.º 14
0
def _CheckInputMstar(Mstar):
    """Takes stellar mass, checks if value is a valid input mass."""

    # Make sure Mstar was set
    if Mstar is None:
        misc._PrintErrorKill("stellar mass not given")

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

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

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

    return
Exemplo n.º 15
0
def _ReadEvolutionTrack(starEvoDir, evoModels, Mstar, MstarFilenameMiddle):

    # This function loads the original stellar evolution models from Spada et al. (2013) for an individual
    # mass bin and puts them into a dictionary.

    # Set strings for starting and ending of filenames
    filename_prefix = starEvoDir + evoModels + "/M"
    filename_postfix1 = "_" + evoModels + ".track1"
    filename_postfix2 = "_" + evoModels + ".track2"

    # Get names of data files holding evo models
    filename1 = filename_prefix + MstarFilenameMiddle + filename_postfix1
    filename2 = filename_prefix + MstarFilenameMiddle + filename_postfix2

    # Read contents of files
    with open(filename1, 'r') as f:
        content1 = f.readlines()
    with open(filename2, 'r') as f:
        content2 = f.readlines()

    # Number of header lines
    nHeader = 1

    # Remove any duplicate ages
    content1 = _RemoveDuplicateAges(nHeader, content1)
    content2 = _RemoveDuplicateAges(nHeader, content2)

    # Make sure they are the same lengths
    if not (len(content1) == len(content2)):
        misc._PrintErrorKill("two evo files not same length")

    # Get number of age bins
    nAge = len(content1) - nHeader

    # Make arrays to hold desired quantities
    Age = np.zeros(nAge)
    Rstar = np.zeros(nAge)
    Lbol = np.zeros(nAge)
    Teff = np.zeros(nAge)
    Itotal = np.zeros(nAge)
    Icore = np.zeros(nAge)
    Ienv = np.zeros(nAge)
    Mcore = np.zeros(nAge)
    Menv = np.zeros(nAge)
    Rcore = np.zeros(nAge)
    tauConv = np.zeros(nAge)
    dItotaldt = np.zeros(nAge)
    dIcoredt = np.zeros(nAge)
    dIenvdt = np.zeros(nAge)
    dMcoredt = np.zeros(nAge)
    dRcoredt = np.zeros(nAge)

    # Read quantities from first file into the arrays (has age, Lbol, and Rstar)
    iAge = 0
    for line in content1[nHeader:len(content1)]:

        # Split data into list
        data = line.split()

        # Read data
        Age[iAge] = data[0]
        Lbol[iAge] = data[4]
        Teff[iAge] = data[7]
        Rstar[iAge] = data[5]

        # Update index
        iAge += 1

    # Read quantities from second file into the arrays (has Itotal, Ienv, Menv, Rcore, and tauConv)
    iAge = 0
    for line in content2[nHeader:len(content2)]:

        # Split data into list
        data = line.split()

        # Read data
        Itotal[iAge] = data[7]
        Ienv[iAge] = data[6]
        Menv[iAge] = data[2]
        Rcore[iAge] = data[3]
        tauConv[iAge] = data[4]

        # Update i
        iAge += 1

    # Change some units and convert from log to lin
    Age[:] = Age[:] * 1000.0  # convert from Gyr into Myr
    Rstar[:] = 10.0**Rstar[:]  # convert into linear since Spada has in log
    Lbol[:] = 10.0**Lbol[:]  # also this one
    Teff[:] = 10.0**Teff[:]  # also this one

    # Get some core quantities
    Icore[:] = Itotal[:] - Ienv[:]
    Mcore[:] = (1.0 - Menv[:]) * Mstar
    Rcore[:] = Rcore[:] * Rstar[:]

    # Get gradients
    dItotaldt[:] = _CalculateGradient(Age, Itotal)
    dIcoredt[:] = _CalculateGradient(Age, Icore)
    dIenvdt[:] = _CalculateGradient(Age, Ienv)
    dMcoredt[:] = _CalculateGradient(Age, Mcore)
    dRcoredt[:] = _CalculateGradient(Age, Rcore)

    # Round mass and ages to nDecValue decimal places as specified at top of file
    Mstar = round(Mstar, nDecValue)
    Age = np.round(Age, decimals=nDecValue)

    # Add all quantities to the dictionary
    Data = {}
    Data['Mstar'] = Mstar
    Data['Age'] = Age
    Data['Rstar'] = Rstar
    Data['Lbol'] = Lbol
    Data['Teff'] = Teff
    Data['Itotal'] = Itotal
    Data['Icore'] = Icore
    Data['Ienv'] = Ienv
    Data['Mcore'] = Mcore
    Data['Menv'] = Menv
    Data['Rcore'] = Rcore
    Data['tauConv'] = tauConv
    Data['dItotaldt'] = dItotaldt
    Data['dIcoredt'] = dIcoredt
    Data['dIenvdt'] = dIenvdt
    Data['dMcoredt'] = dMcoredt
    Data['dRcoredt'] = dRcoredt
    #Data[''] =

    return Data
Exemplo n.º 16
0
    def __init__(self,
                 Mstar=None,
                 Age=None,
                 Omega=None,
                 OmegaEnv=None,
                 OmegaCore=None,
                 AgesOut=None,
                 starEvoDir=None,
                 evoModels=None,
                 params=params.paramsDefault,
                 verbose=False):
        """
    Initialises instance of Cluster class.
    
    This is the main function that is run when creating an instance of the Cluster class and it sets up all
    the things needed including calculating evolutionary tracks for each of the stars star. The function requires 
    that an array of stellar masses (in Msun) and initial rotation rates (in OmegaSun=2.67e-6 rad s^-1) are input.
    using the Mstar and Omega keyword arguments. 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 tracks for each star 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. Age can also be set as an array giving different ages for 
    each star.
    
    """

        # Make sure Mstar was set
        if Mstar is None:
            misc._PrintErrorKill("Mstar keyword argument not set")

        # 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 = _CheckInputRotation(
            Age, Omega, OmegaEnv, OmegaCore)

        # Make sure arrays all same length
        if not (len(Mstar) == len(Omega)):
            misc._PrintErrorKill("Mstar and Omega have different lengths")
        if not (len(Mstar) == len(OmegaEnv)):
            misc._PrintErrorKill("Mstar and OmegaEnv have different lengths")
        if not (len(Mstar) == len(OmegaCore)):
            misc._PrintErrorKill("Mstar and OmegaCore have different lengths")

        # Set number of stars
        self.nStars = len(Mstar)

        # Set parameters
        self.params = params

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

        # Set Mstar arrays
        self.Mstar = Mstar

        # Set AgesOut
        self.AgesOut = AgesOut

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

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

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

        return
Exemplo n.º 17
0
    def _LoadEvoTracks(self, Age, OmegaEnv0, OmegaCore0, AgesOut=None):
        """Loads rotation and activity tracks."""

        # Set starting age to 1 Myr
        self.AgeMin = 1.0

        # Get ending age as end of stellar evolution track
        self.AgeMax = self.StarEvo.ModelData[self.Mstar]['Age'][-1]

        # If needing to get initial rotation rate then get it
        if not Age is None:

            # Get initial rotation
            Omega0 = RE.FitRotation(Mstar=self.Mstar,
                                    Age=Age,
                                    Omega=OmegaEnv0,
                                    AgeMin=self.AgeMin,
                                    params=self.params,
                                    StarEvo=self.StarEvo)

            # Make sure a correct initial rotation rate was found
            if (Omega0 == -1):
                misc._PrintErrorKill(
                    "input rotation rate too low for given mass and age")
            if (Omega0 == -2):
                misc._PrintErrorKill(
                    "input rotation rate too high for given mass and age")
            if (Omega0 == -3):
                misc._PrintErrorKill(
                    "input rotation rate in valid range for mass and age but solver was unable to fit track"
                )

            # Set initial rotation rates for input into RE.EvolveRotation()
            OmegaEnv0 = Omega0
            OmegaCore0 = Omega0

        # Run evolution model using OmegaEnv0 and OmegaCore0 as initial rotation rate
        self.Tracks = RE.EvolveRotation(Mstar=self.Mstar,
                                        OmegaEnv0=OmegaEnv0,
                                        OmegaCore0=OmegaCore0,
                                        AgeMin=self.AgeMin,
                                        AgeMax=self.AgeMax,
                                        AgesOut=AgesOut,
                                        params=self.params,
                                        StarEvo=self.StarEvo)

        # Set the percentile of this track in the starting distribution
        self.percentile = Percentile(Mstar=self.Mstar, Omega=OmegaEnv0)

        # Make evolutionary tracks attributes of this class
        # (this is so the user can get a track using e.g. star.Age instead of star.Tracks['Age'])
        for track in self.Tracks:

            # Note: the following two options are basically equivalent
            #self.__dict__[track+'Track'] = self.Tracks[track]
            setattr(self, track + 'Track', self.Tracks[track])

        # Load dictionary holding units for each quantity
        self.Units = phys.QuantitiesUnits()

        # Make functions for each quantity that return this quantity at a given age as attributes of class
        self._setupQuantityFunctions()

        return
Exemplo n.º 18
0
    def Percentile(self,
                   Mstar=None,
                   Age=None,
                   Omega=None,
                   Prot=None,
                   percentile=None):
        """
    Gets rotation rate of percentile or percentile of rotation rate in the rotation distribution at given age.
    
    This function can be used for two purposes
      1. to determine the percentile in a rotation distribution of a star given its mass, rotation rate, and age
      2. to determine the rotation rate of a star in a rotation distribution given its mass, percentile, and age
    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 and the age in Myr using the Age keyword argument. These should only be given as floats. 
    
    Parameters
    ----------
    Mstar : float
        Stellar mass in Msun.
    Age : float
        Age of star in Myr.
    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).
    
    Returns
    ----------
    result : float
        Either rotation rate for percentile or percentile for rotation rate.
    
    """

        # Make sure the age is specified
        if Age is None:
            misc._PrintErrorKill(
                "keyword parameter Age not set in call to function")

        # 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:
                misc._PrintErrorKill(
                    "invalid percentile string (options are 'slow', 'medium', or 'fast')"
                )

        # Get the rotation distribution at this age
        OmegaDist = self.Values(Age=Age, Quantity='OmegaEnv')

        # Get the result
        result = star.Percentile(Mstar=Mstar,
                                 Omega=Omega,
                                 Prot=Prot,
                                 percentile=percentile,
                                 MstarDist=self.Mstar,
                                 OmegaDist=OmegaDist,
                                 params=self.params)

        return result
Exemplo n.º 19
0
    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:
            misc._PrintErrorKill("Quantity not set in call to function")

        # Make sure Quantity is string
        if not isinstance(Quantity, str):
            misc._PrintErrorKill("Quantity must be string")

        # Check input Quantity is valid
        if not Quantity in QuantitiesAllowed:
            QuantitiesString = ''
            for quantity in QuantitiesAllowed:
                QuantitiesString += quantity + " , "
            misc._PrintErrorKill("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
Exemplo n.º 20
0
def EvolveRotationStep(Mstar=None,
                       Age=None,
                       OmegaEnv=None,
                       OmegaCore=None,
                       dAgeMax=None,
                       dAge=None,
                       params=params.paramsDefault,
                       StarEvo=None):
    """
  Evolves rotation by a single step.
  
  This is the main function to be used to do timestep updates of the core and envelope rotation rates
  for a star given its basic parameters. The five arguments Mstar, Age, OmegaEnv, OmegaCore, 
  and either dAgeMax or dAge must be set in the call to this function. For certain solvers, the timestep 
  is determined automatically and this value is returned along with the updated envelope and
  core rotation rates
  
  Parameters
  ----------
  Mstar : float
      Mass of star in Msun.
  Age : float , optional
      Age of star.
  OmegaEnv : float 
      Envelope rotation rate in OmegaSun (=2.67e-6 rad/s).
  OmegaCore : float 
      Core rotation rate in OmegaSun (=2.67e-6 rad/s).
  dAgeMax : float , optional
      Maximum timestep to take in Myr.
  dAgeMax : float , optional
      Timestep to take in Myr.
  
  Returns
  ----------
  dAge : numpy.ndarray
      Age step in Myr.
  OmegaEnv : numpy.ndarray
      Envelope rotation rates in OmegaSun (=2.67e-6 rad/s).
  OmegaCore : numpy.ndarray
      Core rotation rates in OmegaSun (=2.67e-6 rad/s).
  
  """

    # Make sure parameters are set
    if Mstar is None:
        misc._PrintErrorKill("argument Mstar must be set in call to function")
    if Age is None:
        misc._PrintErrorKill("argument Age must be set in call to function")
    if OmegaEnv is None:
        misc._PrintErrorKill(
            "argument OmegaEnv must be set in call to function")
    if OmegaCore is None:
        misc._PrintErrorKill(
            "argument OmegaCore must be set in call to function")
    if ((dAgeMax is None) and (dAge is None)):
        misc._PrintErrorKill(
            "either dAge or dAgeMax argument must be set in call to function")

    # If an instance of the StarEvo class is not input, load it with defaults
    if StarEvo is None:
        StarEvo = SE.StarEvo()

    # Do step based on method used
    if (params['TimeIntegrationMethod'] == 'ForwardEuler'):

        dAge, dAgeNew, OmegaEnv, OmegaCore = _EvolveRotationStepFE(
            Mstar=Mstar,
            Age=Age,
            OmegaEnv=OmegaEnv,
            OmegaCore=OmegaCore,
            dAge=dAge,
            params=params,
            StarEvo=StarEvo)

    elif (params['TimeIntegrationMethod'] == 'RungeKutta4'):

        dAge, dAgeNew, OmegaEnv, OmegaCore = _EvolveRotationStepRK4(
            Mstar=Mstar,
            Age=Age,
            OmegaEnv=OmegaEnv,
            OmegaCore=OmegaCore,
            dAge=dAge,
            params=params,
            StarEvo=StarEvo)

    elif (params['TimeIntegrationMethod'] == 'RungeKuttaFehlberg'):

        dAge, dAgeNew, OmegaEnv, OmegaCore = _EvolveRotationStepRKF(
            Mstar=Mstar,
            Age=Age,
            OmegaEnv=OmegaEnv,
            OmegaCore=OmegaCore,
            dAge=dAge,
            dAgeMax=dAgeMax,
            params=params,
            StarEvo=StarEvo)

    elif (params['TimeIntegrationMethod'] == 'Rosenbrock'):

        dAge, dAgeNew, OmegaEnv, OmegaCore = _EvolveRotationStepRB(
            Mstar=Mstar,
            Age=Age,
            OmegaEnv=OmegaEnv,
            OmegaCore=OmegaCore,
            dAge=dAge,
            dAgeMax=dAgeMax,
            params=params,
            StarEvo=StarEvo)

    else:
        misc._PrintErrorKill(
            "invalid value of TimeIntegrationMethod in parameters")

    return (dAge, dAgeNew, OmegaEnv, OmegaCore)
Exemplo n.º 21
0
def FitRotation(Mstar=None,
                Age=None,
                Omega=None,
                AgeMin=None,
                params=params.paramsDefault,
                StarEvo=None):
    """
  Takes stellar mass, age, and surface rotation rate, returns initial rotation rate of star. 
  
  This function can be used if the age and surface rotation rate of the star is known and the user wants to get
  the corresponding initial (usually 1 Myr) rotation rate for the star. The star's mass, age, and rotation rates 
  must be input. The starting age to get the initial rotation rate for can be set, and by default this is set to
  AgeMinFit in the parameters and it is not recommended to change this. In addition, model parameters and an instance
  of the StarEvo class can be input. The return value of this function should be the initial rotation rate but if 
  the input Omega is below the minimum that can be fit, it will be -1, if it is above the maximum that can be 
  fit, it will be -2, and if it is in the range but no solution could be found it will be -3.
  
  Parameters
  ----------
  Mstar : float
      Mass of star in Msun.
  Age : float
      Age of the star in Myr. 
  Omega : float 
      Rotation rate in OmegaSun (=2.67e-6 rad/s).
  AgeMin : float , optional
      Starting age (default = 1 Myr).
  params : dict , optional
      Parameters to determine behavior of code (default given in parameters.py).
  StarEvo : Mors.stellarevo.StarEvo
      Instance of StarEvo class holding stellar evolution models to use.
  
  Returns
  ----------
  Omega0 : dict
      Starting rotation rate in OmegaSun (=2.67e-6 rad/s) or negative if cannot get initial rotation.
  
  """

    #---------------------------------------------------------------------

    # Make sure stellar mass is set
    if Mstar is None:
        misc._PrintErrorKill("argument Mstar must be set in call to function")

    # Make sure initial rotation is set correctly
    if Omega is None:
        misc._PrintErrorKill("argument Omega must be set in call to function")

    # Make sure AgeMin is set
    if AgeMin is None:
        AgeMin = params['AgeMinFit']

    # If an instance of the StarEvo class is not input, load it with defaults
    if StarEvo is None:
        StarEvo = SE.StarEvo()

    # Make sure this exact stellar mass is loaded (will do nothing if it already is)
    StarEvo.LoadTrack(Mstar)

    #---------------------------------------------------------------------

    # First thing to be checked is if the model can actually fit this rotation rate at this age since
    # we have defined minimum and maximum initial rotation rates in the parameters, given by

    # Set starting Omega0Min and Omega0Max for fitting
    Omega0Min = params['Omega0FitMin']
    Omega0Max = params['Omega0FitMax']

    # Get minimum rotation rate at Age corresponding to Omega0Min
    Tracks = EvolveRotation(Mstar=Mstar,
                            Omega0=Omega0Min,
                            AgeMin=params['AgeMinFit'],
                            AgeMax=Age,
                            params=params,
                            StarEvo=StarEvo)
    OmegaMin = Tracks['OmegaEnv'][-1]

    # Get maximum rotation rate at Age corresponding to Omega0Max
    Tracks = EvolveRotation(Mstar=Mstar,
                            Omega0=Omega0Max,
                            AgeMin=params['AgeMinFit'],
                            AgeMax=Age,
                            params=params,
                            StarEvo=StarEvo)
    OmegaMax = Tracks['OmegaEnv'][-1]

    # Check that Omega is between OmegaMin and OmegaMax
    if (Omega < OmegaMin):
        return -1
    if (Omega > OmegaMax):
        return -2

    #---------------------------------------------------------------------

    # Initially assume not found solution
    found = False

    # Start looping until solution has been found
    for iStep in range(0, params['nStepMaxFit']):

        # Get middle Omega0
        Omega0Mid = 0.5 * (Omega0Min + Omega0Max)

        # Get rotation at mid spot
        Tracks = EvolveRotation(Mstar=Mstar,
                                Omega0=Omega0Mid,
                                AgeMin=params['AgeMinFit'],
                                AgeMax=Age,
                                params=params,
                                StarEvo=StarEvo)
        OmegaMid = Tracks['OmegaEnv'][-1]

        #print(Omega0Min,Omega0Mid,Omega0Max,OmegaMin,Omega,OmegaMax,OmegaMid/Omega,Omega0Min/Omega0Max)
        # Check if OmegaMid is close enough to the desired answer
        if (abs(OmegaMid / Omega - 1.0) < params['toleranceFit']):
            found = True
            Omega0 = Omega0Mid
            break

        # Check if Omega0Min and Omega0Max are is close enough together (closest they will get)
        if (abs(Omega0Min / Omega0Max - 1.0) < params['toleranceFit']):
            found = True
            Omega0 = Omega0Mid
            break

        # Change either min or max Omega0
        # Note that we already know that the solution is between the min and max
        if (Omega < OmegaMid):
            Omega0Max = Omega0Mid
            OmegaMax = OmegaMid
        else:
            Omega0Min = Omega0Mid
            OmegaMin = OmegaMid

    # Return -3 if not found result
    if not found:
        return -3

    #---------------------------------------------------------------------

    return Omega0
Exemplo n.º 22
0
def EvolveRotation(Mstar=None,
                   Omega0=None,
                   OmegaEnv0=None,
                   OmegaCore0=None,
                   AgeMin=None,
                   AgeMax=None,
                   AgesOut=None,
                   params=params.paramsDefault,
                   StarEvo=None):
    """
  Takes stellar mass and rotation rate, evolves rotation. 
  
  This is the main function for rotational evolution calculations. It will take some basic stellar parameters and
  evolve rotation between two ages.
  
  Parameters
  ----------
  Mstar : float
      Mass of star in Msun.
  Omega0 : float , optional
      Starting rotation rate in OmegaSun (=2.67e-6 rad/s).
  OmegaEnv0 : float , optional
      Starting envelope rotation rate in OmegaSun (=2.67e-6 rad/s).
  OmegaCore0 : float , optional
      Starting envelope rotation rate in OmegaSun (=2.67e-6 rad/s).
  AgeMin : float , optional
      Starting age (default = 1 Myr).
  AgeMax : float , optional
      Final age (default = 5 Gyr).
  AgesOut : float or numpy.ndarray , optional
      Ages to output data for in Myr.
  params : dict , optional
      Parameters to determine behavior of code (default given in parameters.py).
  StarEvo : Mors.stellarevo.StarEvo
      Instance of StarEvo class holding stellar evolution models to use.
  
  Returns
  ----------
  Tracks : dict
      Dictionary with evolutionary tracks and related information. 
  
  """

    # Make sure stellar mass is set
    if Mstar is None:
        misc._PrintErrorKill("argument Mstar must be set in call to function")

    # Make sure initial rotation is set correctly
    if Omega0 is None:
        if ((OmegaEnv0 is None) or (OmegaCore0 is None)):
            misc._PrintErrorKill(
                "if Omega0 is not set then both OmegaEnv0 and OmegaCore0 must be set in call to function"
            )
    else:
        if not ((OmegaEnv0 is None) and (OmegaCore0 is None)):
            misc._PrintErrorKill(
                "if Omega0 is set then neither OmegaEnv0 nor OmegaCore0 can be set in call to function"
            )

    # Make sure AgeMin is set
    if AgeMin is None:
        AgeMin = params['AgeMinDefault']

    # Make sure AgeMax is set
    if AgeMax is None:
        AgeMax = params['AgeMaxDefault']

    # Make editable and np array form of AgesOut and use this from now on
    if not AgesOut is None:
        if isinstance(AgesOut, (float, int)):
            AgesOut2 = np.array([AgesOut])
        else:
            AgesOut2 = copy.deepcopy(AgesOut)
    else:
        AgesOut2 = None

    # If AgesOut has been set, use the final age from that as ending age
    if not AgesOut2 is None:
        AgeMax = AgesOut2[-1]

    # If an instance of the StarEvo class is not input, load it with defaults
    if StarEvo is None:
        StarEvo = SE.StarEvo()

    # Make sure this exact stellar mass is loaded (will do nothing if it already is)
    StarEvo.LoadTrack(Mstar)

    # Starting values for time
    iAge = 0
    Age = AgeMin

    # Starting timestep in Myr
    dAge = 0.5

    # If AgesOut was set, make sure no ages are below AgeMin
    if not AgesOut2 is None:
        AgesOut2 = np.delete(AgesOut2, np.where(AgesOut2 < AgeMin))

    # Starting rotation rates
    if Omega0 is None:
        OmegaEnv = OmegaEnv0
        OmegaCore = OmegaCore0
    else:
        OmegaEnv = Omega0
        OmegaCore = Omega0

    # Make dictionary for holding tracks and add initial values
    Tracks = _CreateTracks(Mstar, Age, dAge, OmegaEnv, OmegaCore, params,
                           StarEvo)

    # Start looping, end only when end time is reached (additional 0.999 factor to stop code getting stuck)
    while (Age < 0.999 * AgeMax):

        # Make sure timestep is not too long
        dAge, dAgeMax = _dAgeCalc(dAge, Age, AgeMax, AgesOut2, params)

        # Do timestep
        dAge, dAgeNew, OmegaEnv, OmegaCore = EvolveRotationStep(
            Mstar=Mstar,
            Age=Age,
            OmegaEnv=OmegaEnv,
            OmegaCore=OmegaCore,
            dAgeMax=dAgeMax,
            dAge=dAge,
            params=params,
            StarEvo=StarEvo)

        # Update time
        iAge += 1
        Age += dAge

        # Work out if should add this age to output
        AgesOut2, shouldAppend = _shouldAppend(Age, AgesOut2)

        # Add current state to tracks if should output
        if shouldAppend:
            Tracks = _AppendTracks(Tracks, Mstar, Age, dAge, OmegaEnv,
                                   OmegaCore, params, StarEvo)

        # Check for bad data
        _CheckBadData(Tracks)

        # Get new age step
        dAge = dAgeNew

        # Determine if too many steps taken
        if (iAge >= params['nStepMax']):
            misc._PrintErrorKill("too many timesteps taken")

    # In Tracks, replace Mstar array with simple Mstar float
    Tracks['Mstar'] = Mstar
    Tracks['nAge'] = len(Tracks['Age'])

    return Tracks
Exemplo n.º 23
0
    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:
            misc._PrintErrorKill("Band not set in call to function")

        # Make sure Band is string
        if not isinstance(Band, str):
            misc._PrintErrorKill("Band must be string")

        # Check input Band is valid
        if not Band in BandsAllowed:
            BandsString = ''
            for band in BandsAllowed:
                BandsString += band + " , "
            misc._PrintErrorKill("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:
            misc._PrintErrorKill("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 + " , "
                misc._PrintErrorKill("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
Exemplo n.º 24
0
def Value(MstarIn, AgeIn, ParamString, ModelData=ModelDataDefault):
    """
  Takes stellar mass, age, and a parameter string, returns values corresponding to named parameter.
  
  The set of models should have already been loaded. With this function, the user can ask for a value
  of one of the parameters for a specific stellar mass and age. All three of these can be input as multiple
  values and an array of values will be returned if this is the case. For example, if MstarIn is input as 
  a 1D array, the function will return a 1D array giving the value for each of these masses. ParamString
  can be input as a list of strings and values for each parameter in that list will be returned in a 1D 
  array. If two are given as arrays or lists, then a 2D array will be returned. If all three then a 3D 
  array with dimensions len(MstarIn)xlen(AgeIn)xlen(ParamString) will be returned.
  
  Parameters
  ----------
  MstarIn : float or int or numpy.ndarray
      Mass of star in Msun.
  AgeIn : float or int or numpy.ndarray
      Age in Myr.
  ParamString : str
      String holding name of parameter to get value for.
  ModelData : dict , optional
      Dictionary of dictionaries holding set of stellar evolution models.
  
  Returns
  ----------
  value : float or numpy.ndarray
      Value of parameter at this mass and age. 
  
  """

    # Check if ModelData is not None, otherwise need to load defaults
    if ModelData is None:
        ModelData = _LoadDefaultModelData()

    # There are now eight scenarios here
    #   1. Mstar and Age are both floats, so return float.
    #   2. Mstar is float and Age is numpy.ndarray, so return numpy.ndarray of length len(Age).
    #   3. Mstar is numpy.ndarray and Age is float, so return numpy.ndarray of length len(Age)
    #   4. Mstar and Age are both numpy.ndarray, so return numpy.ndarray of shape ( len(Mstar) , len(Age) ).
    # Note: could use type(X).__module__ == 'numpy' to test for numpy but it doesn't work if

    # Get Mstar and Age in correct types
    Mstar = misc._convertFloatArray(MstarIn)
    Age = misc._convertFloatArray(AgeIn)

    # Make sure Mstar and Age are correct types
    if Mstar is None:
        misc._PrintErrorKill("argument Mstar has invalid type")
    if Age is None:
        misc._PrintErrorKill("argument Age has invalid type")

    # Find if scenario 1 (most likely)
    if (isinstance(Mstar, float) and isinstance(Age, float)
            and isinstance(ParamString, str)):

        # Scenario 1 so just get value
        value = _ValueSingle(Mstar, Age, ParamString, ModelData=ModelData)

    else:

        # In this case, the output will be an array with up to three dimensions, so first make 3D array
        # and then remove dimensions with only one element

        # Get number of elements in all three directions
        try:
            nMstar = len(Mstar)
        except:
            nMstar = 1

        try:
            nAge = len(Age)
        except:
            nAge = 1

        if isinstance(ParamString, list):
            nParam = len(ParamString)
        else:
            nParam = 1

        # Make array
        value = np.zeros((nMstar, nAge, nParam))

        # Loop over values
        for iMstar in range(0, nMstar):
            for iAge in range(0, nAge):
                for iParam in range(0, nParam):

                    # Get values of Mstar, Age, and ParamString
                    if isinstance(Mstar, float):
                        MstarValue = Mstar
                    else:
                        MstarValue = Mstar[iMstar]

                    if isinstance(Age, float):
                        AgeValue = Age
                    else:
                        AgeValue = Age[iAge]

                    if isinstance(ParamString, str):
                        ParamStringValue = ParamString
                    else:
                        ParamStringValue = ParamString[iParam]

                    # Now get the value
                    value[iMstar, iAge,
                          iParam] = _ValueSingle(MstarValue,
                                                 AgeValue,
                                                 ParamStringValue,
                                                 ModelData=ModelData)

        # Now get rid of unwanted dimensions
        value = np.squeeze(value)

    return value
Exemplo n.º 25
0
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:
        misc._PrintErrorKill("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:
                misc._PrintErrorKill(
                    "keyword arguments Omega and Prot cannot both be set")
        # At least one
        if (Omega is None) and (Prot is None):
            misc._PrintErrorKill(
                "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):
            misc._PrintErrorKill(
                "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):
            misc._PrintErrorKill(
                "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)):
            misc._PrintErrorKill(
                "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
Exemplo n.º 26
0
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:
            misc._PrintErrorKill(
                "cannot set both percentile and Omega as arguments of Star")
        if not OmegaEnv is None:
            misc._PrintErrorKill(
                "cannot set both percentile and OmegaEnv as arguments of Star")
        if not OmegaCore is None:
            misc._PrintErrorKill(
                "cannot set both percentile and OmegaCore as arguments of Star"
            )
        if not Prot is None:
            misc._PrintErrorKill(
                "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):
        misc._PrintErrorKill(
            "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:
            misc._PrintErrorKill(
                "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):
        misc._PrintErrorKill(
            "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):
        misc._PrintErrorKill(
            "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):
                misc._PrintErrorKill(
                    "if Age is set then must set either Omega or OmegaEnv as argument of Star"
                )
        elif (OmegaEnv is None) or (OmegaCore is None):
            misc._PrintErrorKill(
                "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)):
            misc._PrintErrorKill(
                "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