Ejemplo n.º 1
0
    def get_life(self, C, k, nr_load_classes=512, Su=False, range=False):
        """Calculate fatigue life with parameters C, k, as defined in [2].
 
        :param C: [int,float]
            Fatigue strength coefficient [MPa**k].
        :param k : [int,float]
            Fatigue strength exponent [/].
        :param nr_load_classes: int
            The number of intervals to divide the min-max range of the dataseries
            into. Defaults to 512.
        :param Su: [int,float]
            Ultimate tensile strength [MPa]. If specified, Goodman equivalent stress
            is used for fatigue life estimation. Defaults to False.
        :param range: bool
            If True, ranges instead of amplitudes are used for fatigue life estimation.
            Defaults to False.
        :return T: float
            Estimated fatigue life in seconds.
        """
        t = self.spectral_data.t
        ranges, means = fatpack.find_rainflow_ranges(self.spectral_data.data,
                                                     k=nr_load_classes,
                                                     return_means=True)

        if range == True: pass
        elif range == False: ranges *= 0.5
        else: raise Exception('Unrecognized Input Error')

        if Su: ranges = ranges / (1. - means / Su)

        d = np.sum(ranges**k / C)
        T = t / d

        return T
Ejemplo n.º 2
0
    def _compute_del(ts, slope, elapsed, **kwargs):
        """
        Computes damage equivalent load of input `ts`.

        Parameters
        ----------
        ts : np.array
            Time series to calculate DEL for.
        slope : int | float
            Slope of the fatigue curve.
        elapsed : int | float
            Elapsed time of the time series.
        rainflow_bins : int
            Number of bins used in rainflow analysis.
            Default: 100
        """

        bins = kwargs.get("rainflow_bins", 100)

        ranges = fatpack.find_rainflow_ranges(ts)
        Nrf, Srf = fatpack.find_range_count(ranges, 100)
        DELs = Srf**slope * Nrf / elapsed
        DEL = DELs.sum()**(1 / slope)

        return DEL
Ejemplo n.º 3
0
def fatigueLoad(foil_load_vertical, FN_interpolated, k=None, foilmodule=None, outputfolder=None, n_years=None, V=None):
    
    # Set global variables:
    global curve, category, S, fatigue_damage, path_main, y, df, li_img, li_fd

    # System paths
    # path_main = db_location +  "\Teknisk utvikling\\" + foilmodule[0:2] + ' ' + foilmodule[2:] + "\FEM ANALYSE\RAPPORT"
    # os.chdir(path_main)
    # print(path_main)

    # df = pd.read_excel(path_main + '\\Input til FATIGUE.xlsx', sheet_name=foilmodule)
    # print (df.iloc[:, [0,1]])
 
     # Generates overview of the loads acting on the foil:
    #%%
    reversals, reversals_ix = fatpack.find_reversals(foil_load_vertical)#, k)
    cycles, residue = fatpack.find_rainflow_cycles(reversals)
    processed_residue = fatpack.concatenate_reversals(residue, residue)
    cycles_residue, _ = fatpack.find_rainflow_cycles(processed_residue)
    cycles_total = np.concatenate((cycles, cycles_residue))
    
    # Find the rainflow ranges from the cycles
    force_range = fatpack.find_rainflow_ranges(foil_load_vertical)#, k)
    
    figsize = np.array([140., 140.]) / 10
    # fig = plt.figure(dpi=180, figsize=figsize)
    # fig.suptitle("Foil load data:", fontsize=16)
    
    # Plotting signal with reversals.
    # ax_signal = plt.subplot2grid((3, 2), (0, 0))
    # ax_signal.plot(foil_load_vertical/1000)
    # ax_signal.plot(reversals_ix, foil_load_vertical[reversals_ix]/1000, 'ro', fillstyle='none',
    #                 label='reversal')
    # # ax_signal.legend()
    # ax_signal.set(title="Signal", ylabel="Foil normal force [kN]", xlabel="Index", xlim=[0, 5000])
    
    # Plotting the cumulative distribution of the cycle count
    # ax_cumdist = plt.subplot2grid((3, 2), (1, 0))
    N, force_distribution = fatpack.find_range_count(force_range, 25)
    Ncum = N.sum() - np.cumsum(N)
    # ax_cumdist.semilogx(Ncum, force_distribution/1000)
    # ax_cumdist.set(title="Cumulative distribution, rainflow ranges",
    #                xlabel="Count, N", ylabel="Foil normal force range [kN]")
    
    # Interpolate for a fixed range of forces
    df_FN_interpolated = DataFrame (FN_interpolated,columns=['Foil normal force range [N]'])   
    f = interpolate.interp1d(force_distribution, Ncum, fill_value=0, bounds_error=False)
    Ncum_normalized = np.round(f(FN_interpolated))     
    df_Ncum = DataFrame (Ncum_normalized,columns=['Number of cycles simulated [-]'])   

    # Save distribution to excel file
    filename= outputfolder + '\FOIL FORCE STATISTICS ' + str(int(V/0.5144)) + ' knots.xlsx'
    append2excel.append_df_to_excel(filename, df_FN_interpolated, sheet_name='Sheet1', startcol=1, startrow = 5, truncate_sheet=None, index=False)

    append2excel.append_df_to_excel(filename, df_Ncum, sheet_name='Sheet1', startcol=0, startrow = 5,  truncate_sheet=None, index=False)
    append2excel.append_to_cell(filename, n_years, col=1, row = 0,  truncate_sheet=None, index=False)

    return force_range, force_distribution
Ejemplo n.º 4
0
def damage_equivalent_load(data_signal, m, bin_num=100, data_length=600):
    """
    Calculates the damage equivalent load of a single data signal (or channel) 
    based on IEC TS 62600-3:2020 ED1. 4-point rainflow counting algorithm from 
    fatpack module is based on the following resources:
        
    - `C. Amzallag et. al. Standardization of the rainflow counting method for
      fatigue analysis. International Journal of Fatigue, 16 (1994) 287-293`
      `ISO 12110-2, Metallic materials - Fatigue testing - Variable amplitude
      fatigue testing.`
    - `G. Marsh et. al. Review and application of Rainflow residue processing
      techniques for accurate fatigue damage estimation. International Journal
      of Fatigue, 82 (2016) 757-765`
    
    Parameters
    -----------
    data_signal : array
        Data signal being analyzed
    
    m : float/int
        Fatigue slope factor of material
    
    bin_num : int
        Number of bins for rainflow counting method (minimum=100)
    
    data_length : float/int
        Length of measured data (seconds)
    
    Returns
    -----------
    DEL : float
        Damage equivalent load of single data signal
    """
    # check data types
    try:
        data_signal = np.array(data_signal)
    except:
        pass
    assert isinstance(data_signal, np.ndarray), 'data_signal must be of type np.ndarray'
    assert isinstance(m, (float,int)), 'm must be of type float or int'
    assert isinstance(bin_num, (float,int)), 'bin_num must be of type float or int'
    assert isinstance(data_length, (float,int)), 'data_length must be of type float or int'

    # find rainflow ranges
    ranges = fatpack.find_rainflow_ranges(data_signal)

    # find range count and bin
    Nrf, Srf = fatpack.find_range_count(ranges, bin_num)

    # get DEL
    DELs = Srf**m * Nrf / data_length
    DEL = DELs.sum() ** (1/m)

    return DEL
Ejemplo n.º 5
0
    def rainflow_histogram(self):
        r, sm = fatpack.find_rainflow_ranges(self.y, return_means=True, k=self.nbins)

        # Next create the bins to divide the stress ranges and means into

        bins_r = np.linspace(min(r), max(r), self.nbins)
        bins_sm = np.linspace(min(sm), max(sm), self.nbins)

        # and establish the data array. Note that other datavectors vectors, e.g. 
        # Smin and Smax, may also be used to create other data arrays and
        # resulting rainflow matrices.

        data_array = np.array([sm, r]).T

        # Finally, establish the rainflow matrix from the data array and the
        # specified row and column bins.

        n = fatpack.find_rainflow_matrix(data_array, bins_sm, bins_r)
        n = n.sum(axis=0)
        r = bins_r[0:-1]
        S = (r/2)/(1-np.mean(self.y)/self.sigmaf)
        # r = []
        # n = []

        # for i in range(len(cc)):
        #     r.append(cc[i][0]/2)
        #     n.append(cc[i][1])
        #     # print(r[i], n[i])

        # O código abaixo acho que está certo, porém preciso dar um jeito de fazê-lo ser mais rápido para obter os ranges e means de rainflow

        rangemax = max(S) - min(S)

        nclass = self.nbins
        tns = sum(n)

        bw = (min(S) + (rangemax/nclass)*(nclass+1)) - (min(S) + (rangemax/nclass)*(nclass))

        p = []
        appendp = p.append

        summ = 0
        for i in range(len(n)):
            appendp((n[i]/tns)/bw)
            summ += p[i]*bw
        
        #Finish here!!!!

        return S, n, p
Ejemplo n.º 6
0
    def get_DEL(self, fast_data, chan_dict, binNum=100, t=600):
        """ Calculates the short-term damage equivalent load of multiple variables
        
        Parameters: 
        -----------
        fast_data: list
            List of dictionaries containing openfast output data (returned from ROSCO_toolbox.FAST_IO.load_output)
        
        chan_dict : list, tuple
            tuple/list containing channel names to be analyzed and corresponding fatigue slope factor "m"
            ie. ('TwrBsFxt',4)
        
        binNum : int
            number of bins for rainflow counting method (minimum=100)
        
        t : float/int
            Used to control DEL frequency. Default for 1Hz is 600 seconds for 10min data
        
        Outputs:
        -----------
        dfDEL : pd.DataFrame
            Damage equivalent load of each specified variable for one fast output file  
        """
        # check data types
        assert isinstance(fast_data, (list)), 'fast_data must be of type list'
        assert isinstance(
            chan_dict,
            (list, tuple)), 'chan_dict must be of type list or tuple'
        assert isinstance(binNum,
                          (float, int)), 'binNum must be of type float or int'
        assert isinstance(t, (float, int)), 't must be of type float or int'

        # create dictionary from chan_dict
        dic = dict(chan_dict)

        # pre-allocate list
        dflist = []

        for fd in fast_data:
            if self.verbose:
                print('Processing data for {}'.format(fd['meta']['name']))
            dlist = []  # initiate blank list every loop
            # loop through channels and apply corresponding fatigue slope
            for var in dic.keys():
                # find rainflow ranges
                ranges = fatpack.find_rainflow_ranges(fd[var])

                # find range count and bin
                Nrf, Srf = fatpack.find_range_count(ranges, binNum)

                # get DEL
                DELs = Srf**dic[var] * Nrf / t
                DEL = DELs.sum()**(1 / dic[var])
                dlist.append(DEL)
            # append DEL values for each channel to master list
            dflist.append(dlist)

            # create dataframe to return
            dfDEL = pd.DataFrame(np.transpose(dflist))
            dfDEL = dfDEL.T
            dfDEL.columns = dic.keys()

        return dfDEL