pyuff 1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyuff-1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) acknowledgement: from 2014-2017 this package was part of the OpenModal project
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pyuff-1.0/PKG-INFO ADDED
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyuff
3
+ Version: 1.0
4
+ Summary: Frequency response function as used in structural dynamics.
5
+ Home-page: https://github.com/ladisk/pyFRF
6
+ Author: Janko Slavič, Luka Novak, Martin Česnik, et al.
7
+ Author-email: janko.slavic@fs.uni-lj.si
8
+ Keywords: FRF,MIMO,SIMO,ODS
9
+ License-File: LICENSE
10
+
11
+ pyFRF
12
+ =====
13
+
14
+ Frequency response function as used in structural dynamics.
15
+ -----------------------------------------------------------
16
+ For more information check out the showcase examples and see documentation_.
17
+
18
+ Basic ``pyFRF`` usage:
19
+ ----------------------
20
+
21
+ Make an instance of ``FRF`` class:
22
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23
+
24
+ .. code:: python
25
+
26
+ a = pyFRF.FRF(
27
+ sampling_freq,
28
+ exc=None,
29
+ resp=None,
30
+ exc_type='f', resp_type='a',
31
+ window='none',
32
+ weighting='linear',
33
+ fft_len=None,
34
+ nperseg=None,
35
+ noverlap=None,
36
+ archive_time_data=False,
37
+ frf_type='H1',
38
+ copy=True
39
+ )
40
+
41
+ Adding data:
42
+ ~~~~~~~~~~~~
43
+ We can add the excitation and response data at the beginning through ``exc`` and ``resp`` arguments, otherwise, the excitation and response
44
+ data can be added later via ``add_data()`` method:
45
+
46
+ .. code:: python
47
+
48
+ a.add_data(exc, resp)
49
+
50
+ Computing FRF:
51
+ ~~~~~~~~~~~~~~
52
+ Preferable way to get the frequency response functions is via ``get_FRF()`` method:
53
+
54
+ .. code:: python
55
+
56
+ frf = a.get_FRF(type="default", form="receptance")
57
+
58
+ We can also directly get the requested FRF via other methods: ``get_H1()``, ``get_H2()``, ``get_Hv()`` and, ``get_ods_frf()``:
59
+
60
+ .. code:: python
61
+
62
+ H1 = a.get_H1()
63
+ H2 = a.get_H2()
64
+ Hv = a.get_Hv()
65
+ ods_frf = a.get_ods_frf()
66
+
67
+ .. _documentation: https://pyfrf.readthedocs.io/en/latest/
68
+
69
+ |pytest|
70
+
71
+ |binder| to test the *Showcase.ipynb*.
72
+
73
+ .. |binder| image:: https://mybinder.org/badge_logo.svg
74
+ :target: https://mybinder.org/v2/gh/ladisk/pyFRF/main
75
+ .. |pytest| image:: https://github.com/ladisk/pyFRF/actions/workflows/python-package.yml/badge.svg
76
+ :target: https://github.com/ladisk/pyFRF/actions
pyuff-1.0/fft_tools.py ADDED
@@ -0,0 +1,235 @@
1
+ """
2
+ Tools to work wit fft data (this file is part of the pyFRF package);
3
+ especially, frequency response functions
4
+
5
+ """
6
+
7
+ import numpy as np
8
+
9
+ _FRF_TYPES = {'a': 2, 'v': 1, 'd': 0} # accelerance, mobility, receptance
10
+
11
+ def multiply(ffts, m):
12
+ """Multiplies ffts*m. ffts can be a single fft or an array of ffts.
13
+
14
+ :param ffts: array of fft data
15
+ :param m: multiplication vector
16
+ :return: multiplied array of fft data
17
+ """
18
+ out = np.zeros_like(ffts, dtype='complex')
19
+ if len(np.shape(ffts)) == 2: # list of
20
+ n = np.shape(ffts)[0] # number of frfs
21
+ for j in range(n):
22
+ out[j, :] = ffts[j, :] * m
23
+ else:
24
+ out[:] = ffts[:] * m
25
+
26
+ return out
27
+
28
+
29
+ def frequency_integration(ffts, omega, order=1):
30
+ """Integrates ffts (one or many) in the frequency domain.
31
+
32
+ :param ffts: [rad/s] : angular frequency vector
33
+ :param omega: angular frequency
34
+ :param order: order of integration
35
+ :return: integrated array of fft data
36
+ """
37
+ return multiply(ffts, np.power(-1.j / omega, order))
38
+
39
+
40
+ def frequency_derivation(ffts, omega, order=1):
41
+ """Derivates ffts (one or many) in the frequency domain.
42
+
43
+ :param ffts: array of fft data
44
+ :param omega: [rad/s] angular frequency vector
45
+ :param order: order of derivation
46
+ :return: derivated array of fft data
47
+ """
48
+ with np.errstate(invalid='ignore'):
49
+ return multiply(ffts, np.power(1.j * omega, order))
50
+
51
+ def convert_frf(input_frfs, omega, input_frf_type, output_frf_type):
52
+ """ Converting the frf accelerance/mobility/receptance
53
+
54
+ The most general case is when `input_frfs` is of shape:
55
+ `nr_inputs` * `nr_outputs` * `frf_len`
56
+
57
+ :param input_frfs: frequency response function vector (of dim 1, 2 or 3)
58
+ :param omega: [rad/s] angular frequency vector
59
+ :param input_frf_type: 'd' receptance, 'v' mobility, 'a' accelerance (of dim 0, 1, 2)
60
+ :param output_frf_type: 'd' receptance, 'v' mobility, 'a' accelerance (of dim 0, 1, 2)
61
+ :return: frequency response function vector (of dim 1, 2 or 3)
62
+ """
63
+ # put all data to 3D frf type (nr_inputs * nr_outputs * frf_len)
64
+ ini_shape = input_frfs.shape
65
+ if 1 <=len(ini_shape) > 3 :
66
+ raise Exception('Input frf should be if dimension 3 or smaller')
67
+ elif len(ini_shape) == 2:
68
+ input_frfs = np.expand_dims(input_frfs, axis=0)
69
+
70
+ if type(input_frf_type) == str:
71
+ input_frf_type = [ini_shape[0]*[input_frf_type]]
72
+ else:
73
+ input_frf_type = [input_frf_type]
74
+
75
+ if type(output_frf_type) == str:
76
+ output_frf_type = [ini_shape[0]*[output_frf_type]]
77
+ else:
78
+ output_frf_type = [output_frf_type]
79
+ elif len(ini_shape) == 1:
80
+ input_frfs = np.expand_dims(np.expand_dims(input_frfs, axis=0), axis=0)
81
+ input_frf_type = [[input_frf_type]]
82
+ output_frf_type = [[output_frf_type]]
83
+
84
+ # reshaping of frfs
85
+ (nr_inputs, nr_outputs, frf_len) = input_frfs.shape
86
+ nr_frfs = nr_inputs * nr_outputs
87
+ input_frfs = input_frfs.reshape(nr_frfs,-1)
88
+
89
+ # reshaping of input and output frf types
90
+ input_frf_type = np.asarray(input_frf_type)
91
+ output_frf_type = np.asarray(output_frf_type)
92
+ if len(input_frf_type.shape) != 2 or len(output_frf_type.shape) !=2:
93
+ raise Exception('Input and output frf type should be of dimension 2.')
94
+ input_frf_type = input_frf_type.flatten()
95
+ output_frf_type = output_frf_type.flatten()
96
+ if len(input_frf_type) != nr_frfs or len(output_frf_type) != nr_frfs:
97
+ raise Exception('Input and output frf type length should correspond to the number frfs.')
98
+
99
+ try:
100
+ input_frf_type = [_FRF_TYPES[_] for _ in input_frf_type]
101
+ output_frf_type = [_FRF_TYPES[_] for _ in output_frf_type]
102
+ except:
103
+ raise('Only frf types: d, v and a are supported.')
104
+
105
+ # do the conversion
106
+ output_frfs = np.zeros_like(input_frfs)
107
+ for i in range(nr_frfs):
108
+ order = output_frf_type[i] - input_frf_type[i]
109
+ if (order > 2) or (order <-2):
110
+ raise Exception('FRF conversion not supported.')
111
+ output_frfs[i, :] = frequency_derivation(input_frfs[i, :], omega, order=order)
112
+
113
+ #reshape back to original shape
114
+ if len(ini_shape) == 3:
115
+ return output_frfs.reshape((nr_inputs, nr_outputs, -1))
116
+ elif len(ini_shape) == 2:
117
+ return output_frfs.reshape((nr_outputs, -1))
118
+ elif len(ini_shape) == 1:
119
+ return output_frfs[0]
120
+
121
+
122
+ def correct_time_delay(fft, w, time_delay):
123
+ """
124
+ Corrects the ``fft`` with regards to the ``time_delay``.
125
+
126
+ :param fft: fft array
127
+ :param w: angular frequency [rad/s]
128
+ :param time_delay: time dalay in seconds
129
+ :return: corrected fft array
130
+ """
131
+ return fft / (np.exp(1j * w * time_delay))
132
+
133
+
134
+ def PSD(x, dt=1):
135
+ """ Power spectral density
136
+
137
+ :param x: time domain data
138
+ :param dt: delta time
139
+ :return: PSD, freq
140
+ """
141
+ X = np.fft.rfft(x)
142
+ freq = np.fft.rfftfreq(len(x), d=dt)
143
+ X = 2 * dt * np.abs(X.conj() * X)/ len(x)
144
+
145
+ return X, freq
146
+
147
+
148
+ def fft_adjusted_lower_limit(x, lim, nr):
149
+ """
150
+ Compute the fft of complex matrix x with adjusted summation limits:
151
+
152
+ y(j) = sum[k=-n-1, -n-2, ... , -low_lim-1, low_lim, low_lim+1, ... n-2,
153
+ n-1] x[k] * exp(-sqrt(-1)*j*k* 2*pi/n),
154
+ j = -n-1, -n-2, ..., -low_limit-1, low_limit, low_limit+1, ... n-2, n-1
155
+
156
+ :param x: Single-sided complex array to Fourier transform.
157
+ :param lim: lower limit index of the array x.
158
+ :param nr: number of points of interest
159
+ :return: Fourier transformed two-sided array x with adjusted lower limit.
160
+ Retruns [0, -1, -2, ..., -nr+1] and [0, 1, 2, ... , nr-1] values.
161
+
162
+ """
163
+ nf = 2 * (len(x) - lim) - 1
164
+
165
+ n = np.arange(-nr + 1, nr)
166
+
167
+ a = np.fft.fft(x, n=nf).real[n]
168
+ b = np.fft.fft(x[:lim], n=nf).real[n]
169
+ c = x[lim].conj() * np.exp(1j * 2 * np.pi * n * lim / nf)
170
+
171
+ res = 2 * (a - b) - c
172
+
173
+ return res[:nr][::-1], res[nr - 1:]
174
+
175
+
176
+ def check_fft_for_speed(data_length, exception_if_prime_above=20):
177
+ """To avoid slow FFT, raises an exception if largest prime above `exception_if_prime_above`.
178
+
179
+ See: http://stackoverflow.com/questions/23287/largest-prime-factor-of-a-number/
180
+
181
+ :param data_length: length of data for frf
182
+ :param exception_if_prime_above: raise exception if the largest prime number is above
183
+ :return: none
184
+ """
185
+
186
+ def prime_factors(n):
187
+ """Returns all prime factors of a positive integer
188
+
189
+ See: http://stackoverflow.com/questions/23287/largest-prime-factor-of-a-number/412942#412942
190
+
191
+ :param n: lenght
192
+ :return: array of prime numbers
193
+ """
194
+ factors = []
195
+ d = 2
196
+ while n > 1:
197
+ while n % d == 0:
198
+ factors.append(d)
199
+ n /= d
200
+ d += 1
201
+ if d * d > n:
202
+ if n > 1:
203
+ factors.append(n)
204
+ break
205
+ return factors
206
+
207
+ if np.max(prime_factors(data_length)) > exception_if_prime_above:
208
+ raise Exception('Change the number of time/frequency points or the FFT will run slow.')
209
+
210
+
211
+ def irfft_adjusted_lower_limit(x, low_lim, indices):
212
+ """
213
+ Compute the ifft of real matrix x with adjusted summation limits:
214
+
215
+ y(j) = sum[k=-n-2, ... , -low_lim-1, low_lim, low_lim+1, ... n-2,
216
+ n-1] x[k] * exp(sqrt(-1)*j*k* 2*pi/n),
217
+ j =-n-2, ..., -low_limit-1, low_limit, low_limit+1, ... n-2, n-1
218
+
219
+ :param x: Single-sided real array to Fourier transform.
220
+ :param low_lim: lower limit index of the array x.
221
+ :param indices: list of indices of interest
222
+ :return: Fourier transformed two-sided array x with adjusted lower limit.
223
+ Retruns values.
224
+ """
225
+
226
+ nf = 2 * (x.shape[1] - 1)
227
+ a = (np.fft.irfft(x, n=nf)[:, indices]) * nf
228
+ b = (np.fft.irfft(x[:, :low_lim], n=nf)[:, indices]) * nf
229
+ return a - b
230
+
231
+
232
+ if __name__ == '__main__':
233
+ plot_figure = False
234
+ # check_fft_for_speed(4) #fast
235
+ # check_fft_for_speed(59612) #slow