ucla-geotech-tools 1.0.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.
- ucla_geotech_tools-1.0.0/LICENSE.txt +7 -0
- ucla_geotech_tools-1.0.0/PKG-INFO +99 -0
- ucla_geotech_tools-1.0.0/README.md +80 -0
- ucla_geotech_tools-1.0.0/pyproject.toml +23 -0
- ucla_geotech_tools-1.0.0/src/ucla_geotech_tools/__init__.py +0 -0
- ucla_geotech_tools-1.0.0/src/ucla_geotech_tools/auto_fchp.py +193 -0
- ucla_geotech_tools-1.0.0/src/ucla_geotech_tools/response_spectrum.py +55 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2023 Scott J. Brandenberg
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ucla_geotech_tools
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A collection of software tools developed by the UCLA geotechnical group
|
|
5
|
+
License: LICENSE.txt
|
|
6
|
+
Author: Scott J. Brandenberg
|
|
7
|
+
Requires-Python: >=3.8,<4.0
|
|
8
|
+
Classifier: License :: Other/Proprietary License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Dist: numpy (>=1.22,<2.0)
|
|
16
|
+
Requires-Dist: scipy (>=1.8,<2.0)
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# auto_fchp
|
|
20
|
+
|
|
21
|
+
Return the high-pass corner frequency value that stabilizes the displacement of a ground motion record.
|
|
22
|
+
|
|
23
|
+
The high-pass corner frequency, fchp, is selected to satisfy two objectives:
|
|
24
|
+
1. A polynomial is fit to the filtered displacement record, and the amplitude of the polynomial fit
|
|
25
|
+
divided by the amplitude of the filtered displacement record is within a tolerance of a specified target.
|
|
26
|
+
2. The amplitude of the initial portion of the displacement record before the p-wave arrival divided by
|
|
27
|
+
the amplitude of the filtered displacement record is within a tolerance of a specified target.
|
|
28
|
+
|
|
29
|
+
The value of fchp is computed numerically using the Scipy package optimize.ridder(). Two initial values
|
|
30
|
+
of fchp are provided that bracket the root, and the Scipy package solves for the root that minimizes the
|
|
31
|
+
objective function. The tolerance is on the value of fchp (i.e., xtol in optimize.ridder)
|
|
32
|
+
|
|
33
|
+
A value of fchp is obtained first to satisfy objective 1. Objective 2 is optional, and if it is applied,
|
|
34
|
+
the value of fchp from objective 1 is checked against objective 2. If the residual from objective 2 is
|
|
35
|
+
less than or equal to zero, fchp is set equal to the value from objective 1. Otherwise, iterations are
|
|
36
|
+
performed to satisfy objective 2.
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
```python
|
|
40
|
+
pip install ucla_geotech_tools.auto_fchp
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Functions
|
|
44
|
+
```python
|
|
45
|
+
get_fchp_sp(dt, acc, **kwargs)
|
|
46
|
+
"""Args:
|
|
47
|
+
dt (float): time step in seconds
|
|
48
|
+
acc (numpy array, dtype=float): acceleration array in L/s/s, where L is length (e.g., meters)
|
|
49
|
+
|
|
50
|
+
Keyword Args:
|
|
51
|
+
target: desired ratio of polynomial fit amplitude to displacement amplitude (default=0.02)
|
|
52
|
+
tol: tolerance for fchp (default=0.001)
|
|
53
|
+
poly_order: order of polynomial to fit to filtered displacement record (default=6)
|
|
54
|
+
maxiter: maximum number of iterations (default=30)
|
|
55
|
+
fchp_min: minimum frequency in Hz to consider for fchp (default=0.001)
|
|
56
|
+
fchp_max: maximum frequency in Hz to consider for fchp (default=0.5)
|
|
57
|
+
tukey_alpha: alpha parameter for scipy.signal.windows.tukey (default=0.05)
|
|
58
|
+
apply_disp_ratio: flag to indicate whether to apply objective 2 (default=0)
|
|
59
|
+
disp_ratio_time: duration in seconds of the initial portion of the record before the p-wave arrival (default=30)
|
|
60
|
+
disp_ratio_target: target ratio of amplitude of initial portion of displacement record to amplitude of displacement record
|
|
61
|
+
"""
|
|
62
|
+
get_residual1(fchp, *args):
|
|
63
|
+
"""Return the residual defined as disp_fit/disp_filt - target.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
fchp = High-pass corner frequency
|
|
67
|
+
Fdisp = Numpy array containing Fourier coefficients of displacement record
|
|
68
|
+
time = Numpy array containing time values
|
|
69
|
+
poly_order = Order of polynomial to fit to filtered displacement
|
|
70
|
+
target = Desired ratio of polynomial fit amplitude to displacement signal amplitude
|
|
71
|
+
freq = Numpy array containing frequency values
|
|
72
|
+
|
|
73
|
+
Computed variables:
|
|
74
|
+
filt = Coefficients of 5th order Butterworth filter
|
|
75
|
+
Fdisp_filt = Numpy array containing Fourier coefficients of filtered displacement record
|
|
76
|
+
disp_filt = Numpy array containing filtered displacement record
|
|
77
|
+
coef = Coefficients of polynomial fit to displacement record
|
|
78
|
+
disp_fit = Numpy array containing polynomial fit ordinates at each time
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
get_residual2(fchp, *args):
|
|
83
|
+
"""Return the residual defined as disp_init/disp - target.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
fchp = High-pass corner frequency
|
|
87
|
+
Fdisp = Numpy array containing Fourier coefficients of displacement record
|
|
88
|
+
time = Numpy array containing time values
|
|
89
|
+
disp_time = Duration of initial portion of record before p-wave arrival
|
|
90
|
+
disp_target = Desired ratio of amplitude of initial portion of displacement record to amplitude of displacement record
|
|
91
|
+
freq = Numpy array containing frequency values
|
|
92
|
+
|
|
93
|
+
Computed variables:
|
|
94
|
+
filt = Coefficients of 5th order Butterworth filter
|
|
95
|
+
Fdisp_filt = Numpy array containing Fourier coefficients of filtered displacement record
|
|
96
|
+
disp_filt = Numpy array containing filtered displacement record
|
|
97
|
+
"""
|
|
98
|
+
```
|
|
99
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# auto_fchp
|
|
2
|
+
|
|
3
|
+
Return the high-pass corner frequency value that stabilizes the displacement of a ground motion record.
|
|
4
|
+
|
|
5
|
+
The high-pass corner frequency, fchp, is selected to satisfy two objectives:
|
|
6
|
+
1. A polynomial is fit to the filtered displacement record, and the amplitude of the polynomial fit
|
|
7
|
+
divided by the amplitude of the filtered displacement record is within a tolerance of a specified target.
|
|
8
|
+
2. The amplitude of the initial portion of the displacement record before the p-wave arrival divided by
|
|
9
|
+
the amplitude of the filtered displacement record is within a tolerance of a specified target.
|
|
10
|
+
|
|
11
|
+
The value of fchp is computed numerically using the Scipy package optimize.ridder(). Two initial values
|
|
12
|
+
of fchp are provided that bracket the root, and the Scipy package solves for the root that minimizes the
|
|
13
|
+
objective function. The tolerance is on the value of fchp (i.e., xtol in optimize.ridder)
|
|
14
|
+
|
|
15
|
+
A value of fchp is obtained first to satisfy objective 1. Objective 2 is optional, and if it is applied,
|
|
16
|
+
the value of fchp from objective 1 is checked against objective 2. If the residual from objective 2 is
|
|
17
|
+
less than or equal to zero, fchp is set equal to the value from objective 1. Otherwise, iterations are
|
|
18
|
+
performed to satisfy objective 2.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
```python
|
|
22
|
+
pip install ucla_geotech_tools.auto_fchp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Functions
|
|
26
|
+
```python
|
|
27
|
+
get_fchp_sp(dt, acc, **kwargs)
|
|
28
|
+
"""Args:
|
|
29
|
+
dt (float): time step in seconds
|
|
30
|
+
acc (numpy array, dtype=float): acceleration array in L/s/s, where L is length (e.g., meters)
|
|
31
|
+
|
|
32
|
+
Keyword Args:
|
|
33
|
+
target: desired ratio of polynomial fit amplitude to displacement amplitude (default=0.02)
|
|
34
|
+
tol: tolerance for fchp (default=0.001)
|
|
35
|
+
poly_order: order of polynomial to fit to filtered displacement record (default=6)
|
|
36
|
+
maxiter: maximum number of iterations (default=30)
|
|
37
|
+
fchp_min: minimum frequency in Hz to consider for fchp (default=0.001)
|
|
38
|
+
fchp_max: maximum frequency in Hz to consider for fchp (default=0.5)
|
|
39
|
+
tukey_alpha: alpha parameter for scipy.signal.windows.tukey (default=0.05)
|
|
40
|
+
apply_disp_ratio: flag to indicate whether to apply objective 2 (default=0)
|
|
41
|
+
disp_ratio_time: duration in seconds of the initial portion of the record before the p-wave arrival (default=30)
|
|
42
|
+
disp_ratio_target: target ratio of amplitude of initial portion of displacement record to amplitude of displacement record
|
|
43
|
+
"""
|
|
44
|
+
get_residual1(fchp, *args):
|
|
45
|
+
"""Return the residual defined as disp_fit/disp_filt - target.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
fchp = High-pass corner frequency
|
|
49
|
+
Fdisp = Numpy array containing Fourier coefficients of displacement record
|
|
50
|
+
time = Numpy array containing time values
|
|
51
|
+
poly_order = Order of polynomial to fit to filtered displacement
|
|
52
|
+
target = Desired ratio of polynomial fit amplitude to displacement signal amplitude
|
|
53
|
+
freq = Numpy array containing frequency values
|
|
54
|
+
|
|
55
|
+
Computed variables:
|
|
56
|
+
filt = Coefficients of 5th order Butterworth filter
|
|
57
|
+
Fdisp_filt = Numpy array containing Fourier coefficients of filtered displacement record
|
|
58
|
+
disp_filt = Numpy array containing filtered displacement record
|
|
59
|
+
coef = Coefficients of polynomial fit to displacement record
|
|
60
|
+
disp_fit = Numpy array containing polynomial fit ordinates at each time
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
get_residual2(fchp, *args):
|
|
65
|
+
"""Return the residual defined as disp_init/disp - target.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
fchp = High-pass corner frequency
|
|
69
|
+
Fdisp = Numpy array containing Fourier coefficients of displacement record
|
|
70
|
+
time = Numpy array containing time values
|
|
71
|
+
disp_time = Duration of initial portion of record before p-wave arrival
|
|
72
|
+
disp_target = Desired ratio of amplitude of initial portion of displacement record to amplitude of displacement record
|
|
73
|
+
freq = Numpy array containing frequency values
|
|
74
|
+
|
|
75
|
+
Computed variables:
|
|
76
|
+
filt = Coefficients of 5th order Butterworth filter
|
|
77
|
+
Fdisp_filt = Numpy array containing Fourier coefficients of filtered displacement record
|
|
78
|
+
disp_filt = Numpy array containing filtered displacement record
|
|
79
|
+
"""
|
|
80
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "ucla_geotech_tools"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "A collection of software tools developed by the UCLA geotechnical group"
|
|
5
|
+
authors = ["Scott J. Brandenberg"]
|
|
6
|
+
license = "LICENSE.txt"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
|
|
9
|
+
packages = [
|
|
10
|
+
{include = "ucla_geotech_tools", from = "src"},
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[tool.poetry.dependencies]
|
|
14
|
+
python = "^3.8"
|
|
15
|
+
numpy = "^1.22"
|
|
16
|
+
scipy = "^1.8"
|
|
17
|
+
|
|
18
|
+
[tool.poetry.group.test.dependencies]
|
|
19
|
+
obspy = "^1.4.0"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["poetry-core"]
|
|
23
|
+
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import scipy as sp
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_residual1(fchp, *args):
|
|
6
|
+
"""Return the residual defined as disp_fit/disp_filt - target.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
fchp = High-pass corner frequency
|
|
10
|
+
Fdisp = Numpy array containing Fourier coefficients of displacement record
|
|
11
|
+
time = Numpy array containing time values
|
|
12
|
+
poly_order = Order of polynomial to fit to filtered displacement
|
|
13
|
+
target = Desired ratio of polynomial fit amplitude to displacement signal amplitude
|
|
14
|
+
freq = Numpy array containing frequency values
|
|
15
|
+
|
|
16
|
+
Computed variables:
|
|
17
|
+
filt = Coefficients of 5th order Butterworth filter
|
|
18
|
+
Fdisp_filt = Numpy array containing Fourier coefficients of filtered displacement record
|
|
19
|
+
disp_filt = Numpy array containing filtered displacement record
|
|
20
|
+
coef = Coefficients of polynomial fit to displacement record
|
|
21
|
+
disp_fit = Numpy array containing polynomial fit ordinates at each time
|
|
22
|
+
"""
|
|
23
|
+
Fdisp = args[0]
|
|
24
|
+
time = args[1]
|
|
25
|
+
poly_order = args[2]
|
|
26
|
+
target = args[3]
|
|
27
|
+
freq = args[4]
|
|
28
|
+
filter_order = args[5]
|
|
29
|
+
filt = np.zeros(len(Fdisp))
|
|
30
|
+
filt[freq > 0] = 1 / np.sqrt(1 + (fchp / freq[freq > 0]) ** (2 * filter_order))
|
|
31
|
+
Fdisp_filt = Fdisp * filt
|
|
32
|
+
disp_filt = np.fft.irfft(Fdisp_filt)
|
|
33
|
+
coef = np.polyfit(time[0 : len(disp_filt)], disp_filt, poly_order)
|
|
34
|
+
disp_fit = np.polyval(coef, time)
|
|
35
|
+
return np.max(np.abs(disp_fit)) / np.max(np.abs(disp_filt)) - target
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_residual2(fchp, *args):
|
|
39
|
+
"""Return the residual defined as disp_init/disp - target.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
fchp = High-pass corner frequency
|
|
43
|
+
Fdisp = Numpy array containing Fourier coefficients of displacement record
|
|
44
|
+
time = Numpy array containing time values
|
|
45
|
+
disp_time = Duration of initial portion of record before p-wave arrival
|
|
46
|
+
disp_target = Desired ratio of amplitude of initial portion of displacement record to amplitude of displacement record
|
|
47
|
+
freq = Numpy array containing frequency values
|
|
48
|
+
|
|
49
|
+
Computed variables:
|
|
50
|
+
filt = Coefficients of 5th order Butterworth filter
|
|
51
|
+
Fdisp_filt = Numpy array containing Fourier coefficients of filtered displacement record
|
|
52
|
+
disp_filt = Numpy array containing filtered displacement record
|
|
53
|
+
"""
|
|
54
|
+
Fdisp = args[0]
|
|
55
|
+
time = args[1]
|
|
56
|
+
disp_time = args[2]
|
|
57
|
+
disp_target = args[3]
|
|
58
|
+
freq = args[4]
|
|
59
|
+
filter_order = args[5]
|
|
60
|
+
filt = np.zeros(len(Fdisp))
|
|
61
|
+
filt[freq > 0] = 1 / np.sqrt(1 + (fchp / freq[freq > 0]) ** (2 * filter_order))
|
|
62
|
+
Fdisp_filt = Fdisp * filt
|
|
63
|
+
disp_filt = np.fft.irfft(Fdisp_filt)
|
|
64
|
+
return (
|
|
65
|
+
np.max(np.abs(disp_filt[time <= disp_time])) / np.max(np.abs(disp_filt))
|
|
66
|
+
- disp_target
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_fchp(dt, acc, **kwargs):
|
|
71
|
+
"""Return the high-pass corner frequency value that stabilizes the displacement of a ground motion record.
|
|
72
|
+
|
|
73
|
+
The high-pass corner frequency, fchp, is selected to satisfy two objectives:
|
|
74
|
+
1. A polynomial is fit to the filtered displacement record, and the amplitude of the polynomial fit
|
|
75
|
+
divided by the amplitude of the filtered displacement record is within a tolerance of a specified target.
|
|
76
|
+
2. The amplitude of the initial portion of the displacement record before the p-wave arrival divided by
|
|
77
|
+
the amplitude of the filtered displacement record is within a tolerance of a specified target.
|
|
78
|
+
|
|
79
|
+
The value of fchp is computed numerically using the Scipy package optimize.ridder(). Two initial values
|
|
80
|
+
of fchp are provided that bracket the root, and the Scipy package solves for the root that minimizes the
|
|
81
|
+
objective function. The tolerance is on the value of fchp (i.e., xtol in optimize.ridder)
|
|
82
|
+
|
|
83
|
+
A value of fchp is obtained first to satisfy objective 1. Objective 2 is optional, and if it is applied,
|
|
84
|
+
the value of fchp from objective 1 is checked against objective 2. If the residual from objective 2 is
|
|
85
|
+
less than or equal to zero, fchp is set equal to the value from objective 1. Otherwise, iterations are
|
|
86
|
+
performed to satisfy objective 2.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
dt (float): time step in seconds
|
|
90
|
+
acc (numpy array, dtype=float): acceleration array in L/s/s
|
|
91
|
+
|
|
92
|
+
Keyword Args:
|
|
93
|
+
target: desired ratio of polynomial fit amplitude to displacement amplitude (default=0.02)
|
|
94
|
+
tol: tolerance for fchp (default=0.001)
|
|
95
|
+
poly_order: order of polynomial to fit to filtered displacement record (default=6)
|
|
96
|
+
maxiter: maximum number of iterations (default=30)
|
|
97
|
+
fchp_min: minimum frequency in Hz to consider for fchp (default=0.001)
|
|
98
|
+
fchp_max: maximum frequency in Hz to consider for fchp (default=0.5)
|
|
99
|
+
tukey_alpha: alpha parameter for scipy.signal.windows.tukey (default=0.05)
|
|
100
|
+
apply_disp_ratio: flag to indicate whether to apply objective 2 (default=0)
|
|
101
|
+
disp_ratio_time: duration in seconds of the initial portion of the record before the p-wave arrival (default=30)
|
|
102
|
+
disp_ratio_target: target ratio of amplitude of initial portion of displacement record to amplitude of displacement record
|
|
103
|
+
"""
|
|
104
|
+
options = [
|
|
105
|
+
"dt",
|
|
106
|
+
"acc",
|
|
107
|
+
"target",
|
|
108
|
+
"tol",
|
|
109
|
+
"poly_order",
|
|
110
|
+
"maxiter",
|
|
111
|
+
"fchp_min",
|
|
112
|
+
"fchp_max",
|
|
113
|
+
"filter_order",
|
|
114
|
+
"tukey_alpha",
|
|
115
|
+
"filter_type",
|
|
116
|
+
"disp_ratio",
|
|
117
|
+
"disp_ratio_time",
|
|
118
|
+
"disp_ratio_target",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
for key, value in kwargs.items():
|
|
122
|
+
if key not in options:
|
|
123
|
+
print(
|
|
124
|
+
key
|
|
125
|
+
+ " is not a valid argument. Please see documentation. Using default values for all parameters that are not specified."
|
|
126
|
+
)
|
|
127
|
+
exit(1)
|
|
128
|
+
|
|
129
|
+
target = kwargs.get("target", 0.02)
|
|
130
|
+
tol = kwargs.get("tol", 0.001)
|
|
131
|
+
poly_order = kwargs.get("poly_order", 6)
|
|
132
|
+
maxiter = kwargs.get("maxiter", 30)
|
|
133
|
+
fchp_min = kwargs.get("fchp_min", 0.001)
|
|
134
|
+
fchp_max = kwargs.get("fchp_max", 0.5)
|
|
135
|
+
filter_order = kwargs.get("filter_order", 5.0)
|
|
136
|
+
tukey_alpha = kwargs.get("tukey_alpha", 0.05)
|
|
137
|
+
apply_disp_ratio = kwargs.get("apply_disp_ratio", 0)
|
|
138
|
+
disp_ratio_time = kwargs.get("disp_ratio_time", 30)
|
|
139
|
+
disp_ratio_target = kwargs.get("disp_ratio_target", 0.02)
|
|
140
|
+
|
|
141
|
+
# Signal pre-processing. Subtract weighted mean, apply tukey window, define time, take FFT, compute Fdisp
|
|
142
|
+
if len(acc) % 2 != 0:
|
|
143
|
+
acc = acc[0:-1]
|
|
144
|
+
window = sp.signal.windows.tukey(len(acc), alpha=tukey_alpha)
|
|
145
|
+
meanacc = np.average(acc, weights=window)
|
|
146
|
+
acc = window * (acc - meanacc)
|
|
147
|
+
time = np.linspace(0, dt * len(acc), len(acc))
|
|
148
|
+
Facc = np.fft.rfft(acc)
|
|
149
|
+
freq = np.fft.rfftfreq(len(acc), dt)
|
|
150
|
+
Fdisp = np.zeros(len(Facc), dtype="complex")
|
|
151
|
+
Fdisp[freq > 0] = Facc[freq > 0] / -((2 * np.pi * freq[freq > 0]) ** 2)
|
|
152
|
+
|
|
153
|
+
# Solve fchp for objective 1
|
|
154
|
+
R0 = get_residual1(fchp_min, Fdisp, time, poly_order, target, freq, filter_order)
|
|
155
|
+
R2 = get_residual1(fchp_max, Fdisp, time, poly_order, target, freq, filter_order)
|
|
156
|
+
if np.sign(R0) != np.sign(R2):
|
|
157
|
+
fchp = sp.optimize.ridder(
|
|
158
|
+
get_residual1,
|
|
159
|
+
fchp_min,
|
|
160
|
+
fchp_max,
|
|
161
|
+
args=(Fdisp, time, poly_order, target, freq, filter_order),
|
|
162
|
+
xtol=tol,
|
|
163
|
+
maxiter=maxiter,
|
|
164
|
+
)
|
|
165
|
+
elif np.sign(R0) < 0:
|
|
166
|
+
fchp = minfc
|
|
167
|
+
elif np.sign(R2) > 0:
|
|
168
|
+
fchp = fchp_max
|
|
169
|
+
|
|
170
|
+
# Solve fchp for objective 2 if apply_disp_ratio is not zero
|
|
171
|
+
if apply_disp_ratio == 0:
|
|
172
|
+
return fchp
|
|
173
|
+
R0 = get_residual2(
|
|
174
|
+
fchp, Fdisp, time, disp_ratio_time, disp_ratio_target, freq, filter_order
|
|
175
|
+
)
|
|
176
|
+
R2 = get_residual2(
|
|
177
|
+
fchp_max, Fdisp, time, disp_ratio_time, disp_ratio_target, freq, filter_order
|
|
178
|
+
)
|
|
179
|
+
if np.sign(R0) != np.sign(R2):
|
|
180
|
+
fchp = sp.optimize.ridder(
|
|
181
|
+
get_residual2,
|
|
182
|
+
fchp,
|
|
183
|
+
fchp_max,
|
|
184
|
+
args=(Fdisp, time, disp_ratio_time, disp_ratio_target, freq, filter_order),
|
|
185
|
+
xtol=tol,
|
|
186
|
+
maxiter=maxiter,
|
|
187
|
+
)
|
|
188
|
+
elif np.sign(R0) < 0:
|
|
189
|
+
fchp = fchp
|
|
190
|
+
elif np.sign(R2) > 0:
|
|
191
|
+
fchp = fchp_max
|
|
192
|
+
|
|
193
|
+
return fchp
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def get_ngawest2_T():
|
|
4
|
+
return(np.asarray([0.01, 0.02, 0.022, 0.025, 0.029, 0.030, 0.032, 0.035, 0.036, 0.040, 0.042, 0.044, 0.045, 0.046, 0.048, 0.050, 0.055, 0.060, 0.065, 0.067, 0.070, 0.075, 0.080, 0.085, 0.090, 0.095, 0.100, 0.11, 0.12, 0.13, 0.133, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.22, 0.24, 0.25, 0.26, 0.28, 0.29, 0.30, 0.32, 0.34, 0.35, 0.36, 0.38, 0.40, 0.42, 0.44, 0.45, 0.46, 0.48, 0.50, 0.55, 0.60, 0.65, 0.667, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.5, 2.6, 2.8, 3.0, 3.2, 3.4, 3.5, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 20.0]))
|
|
5
|
+
|
|
6
|
+
def next_fast_len(target):
|
|
7
|
+
I2 = 0
|
|
8
|
+
while(2.0**I2<target):
|
|
9
|
+
I2+=1
|
|
10
|
+
I3 = 0
|
|
11
|
+
while(3.0**I3<target):
|
|
12
|
+
I3+=1
|
|
13
|
+
I5 = 0
|
|
14
|
+
while(5.0**I5<target):
|
|
15
|
+
I5+=1
|
|
16
|
+
return np.min([2.0**I2, 3.0**I3, 5.0**I5])
|
|
17
|
+
|
|
18
|
+
def get_ARS(T,D,motions,dt,zeropad):
|
|
19
|
+
if(zeropad==1):
|
|
20
|
+
NFFT = next_fast_len(len(motions))
|
|
21
|
+
else:
|
|
22
|
+
NFFT = len(motions[0])
|
|
23
|
+
freq = np.fft.rfftfreq(NFFT,dt)
|
|
24
|
+
Fmotions = np.fft.rfft(motions)
|
|
25
|
+
Sa = [None]*len(motions)
|
|
26
|
+
for i, Fmotion in enumerate(Fmotions):
|
|
27
|
+
Fconvolved_motion = Fmotion/(1.0+2.0j*D*freq*T[:,np.newaxis]-freq**2*T[:,np.newaxis]**2)
|
|
28
|
+
convolved_motion = np.fft.irfft(Fconvolved_motion)
|
|
29
|
+
Sa[i] = np.max(np.absolute(convolved_motion),axis=1)
|
|
30
|
+
return np.asarray(Sa)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_response_spectrum(**kwargs):
|
|
34
|
+
if('motions' not in kwargs):
|
|
35
|
+
print('you must specify motions')
|
|
36
|
+
exit(1)
|
|
37
|
+
if('dt' not in kwargs):
|
|
38
|
+
print('you must specify dt')
|
|
39
|
+
exit(1)
|
|
40
|
+
verbose = kwargs.get('verbose',1)
|
|
41
|
+
T = kwargs.get('T',get_ngawest2_T())
|
|
42
|
+
motions = kwargs['motions']
|
|
43
|
+
D = kwargs.get('D',0.05)
|
|
44
|
+
zeropad = kwargs.get('zeropad',1)
|
|
45
|
+
dt = kwargs['dt']
|
|
46
|
+
if('RotD' in kwargs):
|
|
47
|
+
if((motions.shape[0]!=2)):
|
|
48
|
+
print('You must specify two motions instead of '+ str(motions.shape[0]) + ' if you specify RotD')
|
|
49
|
+
exit(1)
|
|
50
|
+
RotD = np.asarray(kwargs['RotD'],dtype='float64')
|
|
51
|
+
if(len(RotD)==1):
|
|
52
|
+
RotD = RotD.reshape((RotD.size, 1))
|
|
53
|
+
return get_rotD_ARS(T,D,motions[0],motions[1],dt,zeropad,RotD,100)
|
|
54
|
+
else:
|
|
55
|
+
return get_ARS(T,D,motions,dt,zeropad)
|