Code examples#

Note

More examples will be provided on request.

Plot time series and power spectral density#

Import the time series database, load data from file and plot all time series.

 1"""
 2Example of using the time series database class
 3"""
 4import os
 5
 6from qats import TsDB
 7
 8db = TsDB()
 9
10# locate time series file
11file_name = os.path.join("..", "..", "..", "data", "mooring.ts")
12
13# load time series from file
14db.load([file_name])
15
16# plot everything on the file

You can also select a subset of the timeseries (suports wildcards) as shown below.

15
16# plot only specific time series identified by name

You can also plot the power spectral density of the selected time series. Because the ‘surge’ time series was sampled at a varying time step the the time series are resampled to a constant time step of 0.1 seconds before the FFT.

15
16# plot the power spectral density for the same time series

Count cycles using the Rainflow algorithm#

Note

Some of the syntax shown below is not applicable for version <= 4.6.1, in particular the unpacking of cycles. The changes implemented since 4.6.1 are backwards compatible, however; the syntax suggested below is recommended due to better performance. One may also experience minor changes in rebinned cycles. Finally, the mesh established by qats.fatigue.rainflow.mesh() (and used by TimeSeries.plot_cycle_rangemean3d()) was not correct for version <= 4.6.1. For more details, please refer to the CHANGELOG.

Fetch a single time series from the time series database, extract and visualize the cycle distribution using the Rainflow algorithm.

 1"""
 2Example on working with cycle range and range-mean distributions.
 3"""
 4import os
 5
 6from qats import TsDB
 7
 8# locate time series file
 9file_name = os.path.join("..", "..", "..", "data", "simo_p_out.ts")
10
11# load time series directly on db initiation
12db = TsDB.fromfile(file_name)
13
14# fetch one of the time series from the db
15ts = db.get(name='tension_2_qs')
16
17# plot its cycle ranges as bar diagram
18ts.plot_cycle_range(n=100)
19
20# plot its cycle-range-mean distribution as scatter
21ts.plot_cycle_rangemean(n=100)
22
23# ... or as a 3D surface.
24ts.plot_cycle_rangemean3d(nr=25, nm=25)
25
26# you can also collect the cycle range-mean and count numbers (see TimeSeries.rfc() and TimeSeries.get() for options)
27cycles = ts.rfc()
28
29# unpack cycles to separate 1D arrays if you prefer
_images/ts_cycle_range.png

Single cycle range distribution.#

_images/ts_cycle_range_mean.png

Single cycle range-mean distribution.#

_images/ts_cycle_range_mean_3d.png

Single 3D cycle range-mean distribution#

Compare the cycle range and range-mean distribution from several time series using the methods on the TsDB class.

30ranges, means, counts = cycles.T
31
32# The TsDB class also has similar methods to ease comparison
33# compare cycle range distribution (range versus count) grouped in 100 bins
34db.plot_cycle_range(names='tension*', n=100)
_images/tsdb_cycle_range.png

Comparison of several cycle range distributions.#

_images/tsdb_cycle_range_mean.png

Comparison of several cycle range-mean distributions.#

Calculate fatigue damage in mooring lines#

Using a traditional S-N curve with constant slope and intercept.

 1"""
 2Calculate mooring line fatigue.
 3"""
 4import os
 5from math import pi
 6
 7import numpy as np
 8
 9from qats import TsDB
10from qats.fatigue.sn import SNCurve, minersum
11
12# load time series
13db = TsDB.fromfile(os.path.join("..", "..", "..", "data", "simo_p_out.ts"))
14
15# initiate SN-curve: DNVGL-OS-E301 curve for studless chain
16sncurve = SNCurve(name="Studless chain OS-E301", m1=3.0, a1=6e10)
17
18# Calculate fatigue damage for all mooring line tension time series (kN)
19for ts in db.getl(names='tension_*_qs'):
20    # count tension (discarding the 100s transient)
21    cycles = ts.rfc(twin=(100., 1e12))
22
23    # unpack cycle range and count as separate lists (discard cycle means)
24    ranges, _, counts = cycles.T
25
26    # calculate cross section stress cycles (shown here: 118mm studless chain, with unit [kN] for tension cycles)
27    area = 2. * pi * (118. / 2.) ** 2.          # mm^2
28    ranges = [r * 1e3 / area for r in ranges]   # MPa
29
30    # calculate fatigue damage from Palmgren-Miner rule (SCF=1, no thickness correction)
31    damage = minersum(ranges, counts, sncurve)
32
33    # print summary
34    print(f"{ts.name}:")
35    print(f"\t- Max stress range = {max(ranges):12.5g} MPa")
36    print(f"\t- Total number of stress ranges = {sum(counts)}")
37    print(f"\t- Fatigue damage = {damage:12.5g}\n")
38

Apply low-pass and high-pass filters to time series#

Initiate time series database and load time series file in one go. Plot the low-passed and high-passed time series and the sum of those.

 1"""
 2Example showing how to directly initiate the database with time series from file and then filter the time series.
 3"""
 4import os
 5
 6import matplotlib.pyplot as plt
 7
 8from qats import TsDB
 9
10# locate time series file
11file_name = os.path.join("..", "..", "..", "data", "mooring.ts")
12db = TsDB.fromfile(file_name)
13ts = db.get(name="Surge")
14
15# Low-pass and high-pass filter the time series at 0.03Hz
16# Note that the transient 1000 first seconds are skipped
17t, xlo = ts.get(twin=(1000, 1e12), filterargs=("lp", 0.03))
18_, xhi = ts.get(twin=(1000, 1e12), filterargs=("hp", 0.03))
19
20# plot for visual inspection
21plt.figure()
22plt.plot(ts.t, ts.x, "r", label="Original")
23plt.plot(t, xlo, label="LP")
24plt.plot(t, xhi, label="HP")
25plt.plot(t, xlo + xhi, "k--", label="LP + HP")
26plt.legend()
27plt.grid(True)
28plt.show()

Tail fitting with Weibull#

Fit a parametric Weibull distribution to the largest 13% of the peak sample. Often called tail fitting.

 1"""
 2Example showing tail fitting with Weibull
 3"""
 4import os
 5
 6from qats import TsDB
 7from qats.stats.weibull import lse, plot_fit
 8
 9# locate time series file
10file_name = os.path.join("..", "..", "..", "data", "mooring.ts")
11db = TsDB.fromfile(file_name)
12ts = db.get(name="mooring line 4")
13
14# fetch peak sample
15x = ts.maxima()
16
17# fit distribution to the values exceeding the 0.87-quantile of the empirical cumulative distribution function
18a, b, c = lse(x, threshold=0.87)
19
20# plot sample and fitted distribution on Weibull scales
21plot_fit(x, (a, b, c))
_images/weibull_tail_fit.png

Comparison of empirical sample distribution and fitted parametric distribution on linearized scales.#

Merge files and export to different format#

Todo

Coming soon