wawi 0.0.1__tar.gz → 0.0.5__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {wawi-0.0.1/wawi.egg-info → wawi-0.0.5}/PKG-INFO +7 -6
- {wawi-0.0.1 → wawi-0.0.5}/README.md +3 -3
- {wawi-0.0.1 → wawi-0.0.5}/pyproject.toml +4 -3
- wawi-0.0.5/tests/test_IABSE_step11a.py +219 -0
- wawi-0.0.5/tests/test_IABSE_step11c.py +284 -0
- wawi-0.0.5/tests/test_IABSE_step2a.py +265 -0
- wawi-0.0.5/tests/test_wind.py +71 -0
- wawi-0.0.5/wawi/__init__.py +10 -0
- wawi-0.0.5/wawi/fe.py +134 -0
- wawi-0.0.5/wawi/general.py +468 -0
- wawi-0.0.5/wawi/identification.py +66 -0
- wawi-0.0.5/wawi/io.py +719 -0
- wawi-0.0.5/wawi/modal.py +608 -0
- wawi-0.0.5/wawi/plot.py +569 -0
- wawi-0.0.5/wawi/prob.py +9 -0
- wawi-0.0.5/wawi/random.py +38 -0
- wawi-0.0.5/wawi/signal.py +45 -0
- wawi-0.0.5/wawi/structural.py +278 -0
- wawi-0.0.5/wawi/time_domain.py +126 -0
- wawi-0.0.5/wawi/tools.py +7 -0
- wawi-0.0.5/wawi/wave.py +491 -0
- wawi-0.0.5/wawi/wind.py +1109 -0
- wawi-0.0.5/wawi/wind_code.py +14 -0
- {wawi-0.0.1 → wawi-0.0.5/wawi.egg-info}/PKG-INFO +7 -6
- wawi-0.0.5/wawi.egg-info/SOURCES.txt +28 -0
- {wawi-0.0.1 → wawi-0.0.5}/wawi.egg-info/requires.txt +1 -0
- wawi-0.0.1/wawi/__init__.py +0 -6
- wawi-0.0.1/wawi.egg-info/SOURCES.txt +0 -9
- {wawi-0.0.1 → wawi-0.0.5}/LICENSE +0 -0
- {wawi-0.0.1 → wawi-0.0.5}/setup.cfg +0 -0
- {wawi-0.0.1 → wawi-0.0.5}/wawi.egg-info/dependency_links.txt +0 -0
- {wawi-0.0.1 → wawi-0.0.5}/wawi.egg-info/top_level.txt +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: wawi
|
3
|
-
Version: 0.0.
|
4
|
-
Summary:
|
5
|
-
Author-email: "Knut A.
|
3
|
+
Version: 0.0.5
|
4
|
+
Summary: WAve and WInd response prediction
|
5
|
+
Author-email: "Knut A. Kvåle" <knut.a.kvale@ntnu.no>, Ole Øiseth <ole.oiseth@ntnu.no>, Aksel Fenerci <aksel.fenerci@ntnu.no>, Øivind Wiig Petersen <oyvind.w.petersen@ntnu.no>
|
6
6
|
License: MIT License
|
7
7
|
|
8
8
|
Copyright (c) 2025 Knut Andreas Kvåle
|
@@ -42,13 +42,14 @@ Requires-Dist: scikit-learn
|
|
42
42
|
Requires-Dist: trame
|
43
43
|
Requires-Dist: ipywidgets
|
44
44
|
Requires-Dist: pyvistaqt
|
45
|
+
Requires-Dist: beefpy
|
45
46
|
|
46
47
|

|
47
48
|
=======================
|
48
49
|
|
49
50
|
What is wawi?
|
50
51
|
=======================
|
51
|
-
|
52
|
+
WAWI is a Python toolbox for prediction of response of structures exposed to wind and wave excitation. The package is still under development in its alpha stage, and documentation and testing will be completed along the way.
|
52
53
|
|
53
54
|
|
54
55
|
Installation
|
@@ -62,7 +63,7 @@ pip install wawi
|
|
62
63
|
or install directly from github:
|
63
64
|
|
64
65
|
```
|
65
|
-
pip install git+https://www.github.com/knutankv/wawi.git@
|
66
|
+
pip install git+https://www.github.com/knutankv/wawi.git@main
|
66
67
|
```
|
67
68
|
|
68
69
|
|
@@ -71,7 +72,7 @@ Quick start
|
|
71
72
|
|
72
73
|
Examples
|
73
74
|
=======================
|
74
|
-
Examples are provided as Jupyter Notebooks in the [examples folder](https://github.com/knutankv/wawi/tree/
|
75
|
+
Examples are provided as Jupyter Notebooks in the [examples folder](https://github.com/knutankv/wawi/tree/main/examples).
|
75
76
|
|
76
77
|
References
|
77
78
|
=======================
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
What is wawi?
|
5
5
|
=======================
|
6
|
-
|
6
|
+
WAWI is a Python toolbox for prediction of response of structures exposed to wind and wave excitation. The package is still under development in its alpha stage, and documentation and testing will be completed along the way.
|
7
7
|
|
8
8
|
|
9
9
|
Installation
|
@@ -17,7 +17,7 @@ pip install wawi
|
|
17
17
|
or install directly from github:
|
18
18
|
|
19
19
|
```
|
20
|
-
pip install git+https://www.github.com/knutankv/wawi.git@
|
20
|
+
pip install git+https://www.github.com/knutankv/wawi.git@main
|
21
21
|
```
|
22
22
|
|
23
23
|
|
@@ -26,7 +26,7 @@ Quick start
|
|
26
26
|
|
27
27
|
Examples
|
28
28
|
=======================
|
29
|
-
Examples are provided as Jupyter Notebooks in the [examples folder](https://github.com/knutankv/wawi/tree/
|
29
|
+
Examples are provided as Jupyter Notebooks in the [examples folder](https://github.com/knutankv/wawi/tree/main/examples).
|
30
30
|
|
31
31
|
References
|
32
32
|
=======================
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "wawi"
|
7
|
-
description = "
|
7
|
+
description = "WAve and WInd response prediction"
|
8
8
|
requires-python = ">= 3.6"
|
9
9
|
|
10
10
|
readme = "README.md"
|
11
11
|
license = {file = "LICENSE"}
|
12
12
|
authors = [
|
13
|
-
{name = "Knut A.
|
13
|
+
{name = "Knut A. Kvåle", email = "knut.a.kvale@ntnu.no"},
|
14
14
|
{name = "Ole Øiseth", email = "ole.oiseth@ntnu.no"},
|
15
15
|
{name = "Aksel Fenerci", email = "aksel.fenerci@ntnu.no"},
|
16
16
|
{name = "Øivind Wiig Petersen", email = "oyvind.w.petersen@ntnu.no"},
|
@@ -23,7 +23,8 @@ classifiers = [
|
|
23
23
|
]
|
24
24
|
dynamic = ["version"]
|
25
25
|
dependencies = ["plotly", "pandas", "numpy", "pyvista[jupyter]>=0.38.1",
|
26
|
-
"scikit-learn", "trame", "ipywidgets", "pyvistaqt"
|
26
|
+
"scikit-learn", "trame", "ipywidgets", "pyvistaqt",
|
27
|
+
"beefpy"]
|
27
28
|
|
28
29
|
|
29
30
|
[tool.setuptools]
|
@@ -0,0 +1,219 @@
|
|
1
|
+
|
2
|
+
import pytest
|
3
|
+
import numpy as np
|
4
|
+
from math import isclose
|
5
|
+
|
6
|
+
# use the local wawi (Github folder) instead of the installed version (remove this when using the installed version)
|
7
|
+
import sys
|
8
|
+
import os
|
9
|
+
sys.path.insert(0, os.path.abspath('C:\\Users\\aksef\\Documents\\GitHub\\wawi'))
|
10
|
+
|
11
|
+
# import functions
|
12
|
+
from wawi.io import import_folder
|
13
|
+
from wawi.model import Windstate
|
14
|
+
from wawi.wind import ADs, flatplate_ads
|
15
|
+
from wawi.wind import itflutter_cont_naive
|
16
|
+
from wawi.general import eval_3d_fun
|
17
|
+
|
18
|
+
model_folder = './tests/models/model_11a'
|
19
|
+
|
20
|
+
def iabse_11a_windstate(mean_v):
|
21
|
+
windstate = Windstate(mean_v,
|
22
|
+
90,
|
23
|
+
Iu=0.0,
|
24
|
+
Iw=0.05,
|
25
|
+
Au=6.8, Aw=9.4,
|
26
|
+
Cuy=0.0, Cwy=0.0,
|
27
|
+
Cuz=0.0, Cwz=0.0,
|
28
|
+
Lux=0.0, Lwx=20.0,
|
29
|
+
x_ref=[0,0,0], rho=1.22,
|
30
|
+
options = {
|
31
|
+
'spectra_type': 'vonKarman'
|
32
|
+
}
|
33
|
+
)
|
34
|
+
return windstate
|
35
|
+
|
36
|
+
davenport = lambda fred: 2*(7*fred-1+np.exp(-7*fred))/(7*fred)**2
|
37
|
+
|
38
|
+
|
39
|
+
# in-wind frequencies and damping ratio
|
40
|
+
|
41
|
+
@pytest.mark.parametrize(
|
42
|
+
'V, expected_f, expected_zeta, tol',
|
43
|
+
[(15, np.array([0.0987, 0.279]), np.array([0.0399, 0.0096]), 0.05),
|
44
|
+
(30, np.array([0.0999, 0.2691]), np.array([0.0921, 0.0189]), 0.1),
|
45
|
+
(45, np.array([0.1014, 0.2561]), np.array([0.1689, 0.0309]), 0.1),
|
46
|
+
(60, np.array([0.1027, 0.2340]), np.array([0.3034, 0.0418]), 0.15),
|
47
|
+
(75, np.array([0.0829, 0.1994]), np.array([0.5349, 0.0148]), 0.25)],
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
# aerodynamic stability
|
52
|
+
|
53
|
+
# input - wind velocities / output - frequencies, damping ratios ( test only the unstable mode)
|
54
|
+
|
55
|
+
def test_in_wind_frequencies_and_damping( V, expected_f, expected_zeta, tol):
|
56
|
+
|
57
|
+
# importing the relevant model
|
58
|
+
model = import_folder('./tests/models/model_11a')
|
59
|
+
model.modal_dry.xi0 = .3e-2
|
60
|
+
|
61
|
+
# assign flat plate ADs (Theodorsen)
|
62
|
+
model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
|
63
|
+
|
64
|
+
model.aero.windstate = iabse_11a_windstate(V)
|
65
|
+
model.run_eig(w_initial=[0.5, 1.25], freq_kind=True, itmax=50)
|
66
|
+
lambd = model.results.lambd
|
67
|
+
|
68
|
+
f2 = np.abs(lambd[1].imag) / (2*np.pi)
|
69
|
+
zeta2 = -lambd[1].real/ np.abs(lambd[1])
|
70
|
+
|
71
|
+
assert isclose(f2, expected_f[1], rel_tol = tol)
|
72
|
+
assert isclose(zeta2, expected_zeta[1], rel_tol = tol)
|
73
|
+
|
74
|
+
|
75
|
+
# flutter speed
|
76
|
+
def test_flutter():
|
77
|
+
|
78
|
+
expected_flutter_speed = 77.45
|
79
|
+
tol = 0.01
|
80
|
+
ad_dict = flatplate_ads()
|
81
|
+
|
82
|
+
# importing the relevant model
|
83
|
+
model = import_folder(model_folder)
|
84
|
+
model.modal_dry.xi0 = .3e-2
|
85
|
+
B = model.aero.sections['girder0'].B
|
86
|
+
|
87
|
+
Ms = model.dry_M
|
88
|
+
Cs = model.dry_C
|
89
|
+
Ks = model.dry_K
|
90
|
+
phi = model.get_dry_phi('full')
|
91
|
+
|
92
|
+
# calculate the flutter speed
|
93
|
+
res = itflutter_cont_naive(Ms, Cs, Ks, phi, np.array([0,1]), ad_dict, B, V=0.1, rho=1.22, dV=5,
|
94
|
+
overshoot_factor=0.5, itmax={}, tol={}, print_progress=True)
|
95
|
+
|
96
|
+
assert isclose(res['V'][-1], expected_flutter_speed, rel_tol = tol)
|
97
|
+
|
98
|
+
|
99
|
+
# buffeting at 45 m/s (compare with analytical)
|
100
|
+
def test_mean_speed_45():
|
101
|
+
|
102
|
+
# at 45 m/s & f = 0.278 Hz
|
103
|
+
V = 45
|
104
|
+
f = 0.278
|
105
|
+
|
106
|
+
# expected values (IABSE report)
|
107
|
+
sww = 5.26 # wind spectrum
|
108
|
+
# impedance matrix
|
109
|
+
h_exp = np.array( [[-0.0619+0.0056j, -(-0.1492-0.0811j)], [-(0.01+0.0419j), -1.1559+0.5382j]] )*1e6
|
110
|
+
# PSD response
|
111
|
+
S_exp = np.transpose(np.array( [ [0.0167, -0.007j], [0.007j, 0.00293] ] ))
|
112
|
+
|
113
|
+
|
114
|
+
model = import_folder(model_folder)
|
115
|
+
model.aero.windstate = iabse_11a_windstate(V)
|
116
|
+
|
117
|
+
wind_spectrum_func = model.aero.get_generic_kaimal(nodes = model.eldef.nodes)
|
118
|
+
wind_spectrum_ww = 2*np.pi*wind_spectrum_func(f*2*np.pi)[2,2]
|
119
|
+
|
120
|
+
model.modal_dry.xi0 = .3e-2
|
121
|
+
model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
|
122
|
+
model.aero.prepare_aero_matrices()
|
123
|
+
omega = np.array([0.001, f])*np.pi*2
|
124
|
+
HH = eval_3d_fun(model.get_frf_fun(opt = 1), omega)
|
125
|
+
|
126
|
+
model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
|
127
|
+
model.run_freqsim(omega,
|
128
|
+
include_selfexcited=['aero'],
|
129
|
+
include_action=['aero'],
|
130
|
+
print_progress=False, merge_aero_sections=True)
|
131
|
+
|
132
|
+
global_dof_ix = np.array([2,3])
|
133
|
+
S = model.get_result_psd(key='full',
|
134
|
+
index=global_dof_ix)
|
135
|
+
|
136
|
+
assert isclose(wind_spectrum_ww, sww, rel_tol = 0.01)
|
137
|
+
assert np.allclose(HH[:,:,1], h_exp, rtol=1e-2)
|
138
|
+
assert np.allclose((2*np.pi)*S[:,:,-1], S_exp, rtol=5e-2)
|
139
|
+
|
140
|
+
|
141
|
+
# buffeting responses (spectra)
|
142
|
+
|
143
|
+
@pytest.mark.parametrize(
|
144
|
+
'Vbuff, RS_vert_exp, RS_tors_exp',
|
145
|
+
[
|
146
|
+
(15, np.array([1.24E-01, 1.19E-01, 1.37E-01, 4.09E+00, 5.66E-04, 1.75E-04, 2.15E-04, 2.18E-05, 7.02E-06,]),
|
147
|
+
np.array([2.53E-03, 2.39E-03, 1.56E-03, 1.85E-04, 4.94E-04, 9.05E-04, 7.07E-02, 1.02E-03, 5.71E-05]) ),
|
148
|
+
|
149
|
+
(30, np.array([1.19E+00, 1.15E+00, 1.75E+00, 2.06E+01, 3.02E-02, 1.27E-02, 4.61E-03, 9.67E-04, 3.75E-04,]),
|
150
|
+
np.array([2.43E-02, 2.31E-02, 1.98E-02, 9.30E-04, 2.59E-02, 6.16E-02, 4.23E-01, 3.46E-02, 2.94E-03,]) ),
|
151
|
+
|
152
|
+
(45, np.array([5.60E+00 , 5.40E+00 , 7.45E+00 , 3.43E+01 , 3.41E-01 , 2.50E-01 , 1.67E-02 , 5.85E-03 , 2.78E-03]),
|
153
|
+
np.array([1.15E-01 , 1.08E-01 , 8.46E-02 , 1.55E-03 , 2.85E-01 , 1.11E+00 , 7.06E-01 , 1.52E-01 , 2.07E-02]) ),
|
154
|
+
|
155
|
+
(60, np.array([2.39E+01 , 2.25E+01 , 2.36E+01 , 4.45E+01 , 3.44E+00 , 6.03E+00 , 3.41E-02 , 1.62E-02 , 9.21E-03,]),
|
156
|
+
np.array([4.89E-01 , 4.51E-01 , 2.67E-01 , 2.00E-03 , 2.77E+00 , 2.38E+01 , 8.28E-01 , 3.06E-01 , 6.37E-02,]) ),
|
157
|
+
|
158
|
+
(75, np.array([1.47E+02 , 1.24E+02 , 6.17E+01 , 5.32E+01 , 9.48E+02 , 1.79E+00 , 5.34E-02 , 3.04E-02 , 1.98E-02,]),
|
159
|
+
np.array([3.02E+00 , 2.50E+00 , 6.99E-01 , 2.38E-03 , 7.29E+02 , 6.17E+00 , 8.39E-01 , 4.26E-01 , 1.26E-01,]) ),
|
160
|
+
|
161
|
+
]
|
162
|
+
)
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
# buffeting responses (spectra)
|
167
|
+
def test_response_spectra(Vbuff, RS_vert_exp, RS_tors_exp):
|
168
|
+
|
169
|
+
omega_axis = np.array( [0.001, 0.01, 0.052, 0.1, 0.2, 0.234, 0.278, 0.3, 0.35] )*np.pi*2
|
170
|
+
|
171
|
+
model = import_folder(model_folder)
|
172
|
+
model.aero.windstate = iabse_11a_windstate(Vbuff)
|
173
|
+
model.modal_dry.xi0 = .3e-2
|
174
|
+
model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
|
175
|
+
model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
|
176
|
+
|
177
|
+
model.run_freqsim(omega_axis,
|
178
|
+
include_selfexcited=['aero'],
|
179
|
+
include_action=['aero'],
|
180
|
+
print_progress=False, merge_aero_sections=True)
|
181
|
+
|
182
|
+
global_dof_ix = np.array([2,3])
|
183
|
+
S = model.get_result_psd(key='full',
|
184
|
+
index=global_dof_ix)
|
185
|
+
|
186
|
+
assert np.allclose((2*np.pi)*S[0,0,:], RS_vert_exp, rtol=5e-2)
|
187
|
+
assert np.allclose((31/2)**2*(2*np.pi)*S[1,1,:], RS_tors_exp, rtol=5e-2)
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
@pytest.mark.parametrize(
|
192
|
+
'Vbuff, RMS_vert_exp, RMS_tors_exp',
|
193
|
+
[(15, 0.2603, 0.0419 ),
|
194
|
+
(30, 0.778 , 0.2027 ),
|
195
|
+
(45, 1.3404, 0.4792 ),
|
196
|
+
(60, 2.1601, 0.9306 ),
|
197
|
+
(75, 4.4848, 2.8414 )],
|
198
|
+
)
|
199
|
+
|
200
|
+
# buffeting responses (RMS values) - for integration we use a finer frequency axis
|
201
|
+
def test_RMS_response(Vbuff, RMS_vert_exp, RMS_tors_exp):
|
202
|
+
|
203
|
+
omega_axis = np.linspace(0.001, 6, 1000)
|
204
|
+
|
205
|
+
model = import_folder(model_folder)
|
206
|
+
model.aero.windstate = iabse_11a_windstate(Vbuff)
|
207
|
+
model.modal_dry.xi0 = .3e-2
|
208
|
+
model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
|
209
|
+
model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
|
210
|
+
|
211
|
+
model.run_freqsim(omega_axis,
|
212
|
+
include_selfexcited=['aero'],
|
213
|
+
include_action=['aero'],
|
214
|
+
print_progress=False, merge_aero_sections=True)
|
215
|
+
|
216
|
+
stds = model.get_result_std(key = 'full')
|
217
|
+
|
218
|
+
assert isclose(stds[2], RMS_vert_exp, rel_tol = 10e-2) # vertical # 5-10% std reported in the paper
|
219
|
+
assert isclose(stds[3]*31/2, RMS_tors_exp, rel_tol = 20e-2) # torsional # 10-25% std reported in the paper
|
@@ -0,0 +1,284 @@
|
|
1
|
+
|
2
|
+
import pytest
|
3
|
+
import numpy as np
|
4
|
+
from math import isclose
|
5
|
+
import dill
|
6
|
+
|
7
|
+
# use the local wawi (Github folder) instead of the installed version (remove this when using the installed version)
|
8
|
+
import sys
|
9
|
+
import os
|
10
|
+
sys.path.insert(0, os.path.abspath('C:\\Users\\aksef\\Documents\\GitHub\\wawi'))
|
11
|
+
|
12
|
+
# import functions
|
13
|
+
from wawi.io import import_folder
|
14
|
+
from wawi.model import Windstate
|
15
|
+
from wawi.wind import ADs
|
16
|
+
from wawi.wind import itflutter_cont_naive
|
17
|
+
from wawi.general import eval_3d_fun
|
18
|
+
|
19
|
+
model_folder = './tests/models/model_11c'
|
20
|
+
|
21
|
+
|
22
|
+
def AD_dict(AD_funs):
|
23
|
+
AD_s = dict(
|
24
|
+
A1 = AD_funs['a_fun'][0],
|
25
|
+
A2 = AD_funs['a_fun'][1],
|
26
|
+
A3 = AD_funs['a_fun'][2],
|
27
|
+
A4 = AD_funs['a_fun'][3],
|
28
|
+
A5 = AD_funs['a_fun'][4],
|
29
|
+
A6 = AD_funs['a_fun'][5],
|
30
|
+
|
31
|
+
H1 = AD_funs['h_fun'][0],
|
32
|
+
H2 = AD_funs['h_fun'][1],
|
33
|
+
H3 = AD_funs['h_fun'][2],
|
34
|
+
H4 = AD_funs['h_fun'][3],
|
35
|
+
H5 = AD_funs['h_fun'][4],
|
36
|
+
H6 = AD_funs['h_fun'][5],
|
37
|
+
|
38
|
+
P1 = AD_funs['p_fun'][0],
|
39
|
+
P2 = AD_funs['p_fun'][1],
|
40
|
+
P3 = AD_funs['p_fun'][2],
|
41
|
+
P4 = AD_funs['p_fun'][3],
|
42
|
+
P5 = AD_funs['p_fun'][4],
|
43
|
+
P6 = AD_funs['p_fun'][5],
|
44
|
+
)
|
45
|
+
return AD_s
|
46
|
+
|
47
|
+
|
48
|
+
def iabse_11c_windstate(mean_v):
|
49
|
+
windstate = Windstate(mean_v,
|
50
|
+
90,
|
51
|
+
Iu=0.1,
|
52
|
+
Iw=0.05,
|
53
|
+
Au=6.8, Aw=9.4, # not used in von Karman
|
54
|
+
Cuy=0.0, Cwy=0.0,# full coherence assumed
|
55
|
+
Cuz=0.0, Cwz=0.0,
|
56
|
+
Lux=200.0, Lwx=20.0,
|
57
|
+
x_ref=[0,0,0], rho=1.22,
|
58
|
+
options = {
|
59
|
+
'spectra_type': 'vonKarman'
|
60
|
+
}
|
61
|
+
)
|
62
|
+
return windstate
|
63
|
+
|
64
|
+
davenport = lambda fred: 2*(7*fred-1+np.exp(-7*fred))/(7*fred)**2
|
65
|
+
|
66
|
+
@pytest.mark.parametrize(
|
67
|
+
'V, expected_f, expected_zeta, tol',
|
68
|
+
[(45, np.array([0.0520, 0.1029, 0.2488,]), np.array([0.0142, 0.1660, 0.0429,]), 0.1),
|
69
|
+
(60, np.array([0.0520, 0.1025, 0.2275,]), np.array([0.0169, 0.2879, 0.0366,]), 0.15),
|
70
|
+
(72, np.array([0.0520, 0.0933, 0.2049]), np.array([0.0209, 0.5099, 0.0015,]), 0.25)],
|
71
|
+
)
|
72
|
+
|
73
|
+
def test_in_wind_frequencies_and_damping( V, expected_f, expected_zeta, tol):
|
74
|
+
|
75
|
+
# importing the relevant model
|
76
|
+
model = import_folder(model_folder)
|
77
|
+
model.modal_dry.xi0 = .3e-2
|
78
|
+
|
79
|
+
# assign ADs (Storebelt ADs)
|
80
|
+
with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
|
81
|
+
AD_funs = dill.load(file)
|
82
|
+
AD_s = AD_dict(AD_funs)
|
83
|
+
model.aero.sections['girder0'].ADs = ADs(**AD_s)
|
84
|
+
|
85
|
+
model.aero.windstate = iabse_11c_windstate(V)
|
86
|
+
model.run_eig(w_initial=[0.3, 0.6, 1.5], freq_kind=True, itmax=50)
|
87
|
+
lambd = model.results.lambd
|
88
|
+
|
89
|
+
f3 = np.abs(lambd[2].imag) / (2*np.pi)
|
90
|
+
zeta3 = -lambd[2].real/ np.abs(lambd[2])
|
91
|
+
|
92
|
+
assert isclose(f3, expected_f[2], rel_tol = tol)
|
93
|
+
assert isclose(zeta3, expected_zeta[2], rel_tol = tol)
|
94
|
+
|
95
|
+
|
96
|
+
# flutter speed
|
97
|
+
def test_flutter():
|
98
|
+
|
99
|
+
expected_flutter_speed = 72.3
|
100
|
+
tol = 0.01
|
101
|
+
with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
|
102
|
+
AD_funs = dill.load(file)
|
103
|
+
ad_dict = AD_dict(AD_funs)
|
104
|
+
|
105
|
+
# importing the relevant model
|
106
|
+
model = import_folder(model_folder)
|
107
|
+
model.modal_dry.xi0 = .3e-2
|
108
|
+
B = model.aero.sections['girder0'].B
|
109
|
+
|
110
|
+
Ms = model.dry_M
|
111
|
+
Cs = model.dry_C
|
112
|
+
Ks = model.dry_K
|
113
|
+
phi = model.get_dry_phi('full')
|
114
|
+
|
115
|
+
# calculate the flutter speed
|
116
|
+
res = itflutter_cont_naive(Ms, Cs, Ks, phi, np.array([0,1]), ad_dict, B, V=0.1, rho=1.22, dV=5,
|
117
|
+
overshoot_factor=0.5, itmax={}, tol={}, print_progress=True)
|
118
|
+
|
119
|
+
assert isclose(res['V'][-1], expected_flutter_speed, rel_tol = tol)
|
120
|
+
|
121
|
+
# buffeting at 45 m/s (compare with reference values)
|
122
|
+
def test_mean_speed_45():
|
123
|
+
|
124
|
+
# at 45 m/s & f = 0.278 Hz
|
125
|
+
V = 45
|
126
|
+
f = 0.278
|
127
|
+
|
128
|
+
# expected values (IABSE report)
|
129
|
+
swind_exp = np.array( [ [7.2139, 0], [0, 5.2599] ] )
|
130
|
+
# admittance matrix (BqBq matrix in wawi)
|
131
|
+
Bq_exp = np.array( [
|
132
|
+
[ 0.0088 , 0.0116 ],
|
133
|
+
[ 0.0076 , 0.2536 ],
|
134
|
+
[ 0.099 , 2.0687 ],
|
135
|
+
] )*1e4
|
136
|
+
# impedance matrix
|
137
|
+
h_exp = np.array( [
|
138
|
+
[ -6.74+0.11j , -0.01+0.01j , -0.29-0.26j ],
|
139
|
+
[ 0.07+0.04j , -6.25+0.80j , -19.40-0.37j ],
|
140
|
+
[ 0.06+0.23j , 0.50+5.39j , -158.52+79.71j ],
|
141
|
+
] )*1e4
|
142
|
+
# sign convention
|
143
|
+
h_exp[2, :] *= -1
|
144
|
+
h_exp[:, 2] *= -1
|
145
|
+
|
146
|
+
# PSD response
|
147
|
+
S_exp = np.array( [ [0.26-0j, 1+1j, 1-1j ],
|
148
|
+
[1-1j, 20-0j, 0-13j ],
|
149
|
+
[1+1j, 0+13j, 8-0j ],
|
150
|
+
])*1e-4
|
151
|
+
# sign convention
|
152
|
+
S_exp[2, :] *= -1
|
153
|
+
S_exp[:, 2] *= -1
|
154
|
+
|
155
|
+
model = import_folder(model_folder)
|
156
|
+
model.aero.windstate = iabse_11c_windstate(V)
|
157
|
+
|
158
|
+
wind_spectrum_func = model.aero.get_generic_kaimal(nodes = model.eldef.nodes)
|
159
|
+
wind_spectrum_uu = 2*np.pi*wind_spectrum_func(f*2*np.pi)[0,0]
|
160
|
+
wind_spectrum_ww = 2*np.pi*wind_spectrum_func(f*2*np.pi)[2,2]
|
161
|
+
|
162
|
+
model.modal_dry.xi0 = .3e-2
|
163
|
+
# assign ADs (Storebelt ADs)
|
164
|
+
with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
|
165
|
+
AD_funs = dill.load(file)
|
166
|
+
AD_s = AD_dict(AD_funs)
|
167
|
+
model.aero.sections['girder0'].ADs = ADs(**AD_s)
|
168
|
+
model.aero.prepare_aero_matrices()
|
169
|
+
omega = np.array([0.001, f])*np.pi*2
|
170
|
+
HH = eval_3d_fun(model.get_frf_fun(opt = 1), omega)
|
171
|
+
|
172
|
+
model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
|
173
|
+
model.run_freqsim(omega,
|
174
|
+
include_selfexcited=['aero'],
|
175
|
+
include_action=['aero'],
|
176
|
+
print_progress=False, merge_aero_sections=True)
|
177
|
+
|
178
|
+
global_dof_ix = np.array([1,2,3])
|
179
|
+
S = model.get_result_psd(key='full',
|
180
|
+
index=global_dof_ix)
|
181
|
+
|
182
|
+
assert isclose(wind_spectrum_ww, swind_exp[1,1], rel_tol = 0.01)
|
183
|
+
assert isclose(wind_spectrum_uu, swind_exp[0,0], rel_tol = 0.01)
|
184
|
+
assert np.allclose(np.diag(HH[:,:,1]), np.diag(h_exp), rtol=1e-2)
|
185
|
+
assert np.allclose((2*np.pi)*np.diag(S[:,:,-1]), np.diag(S_exp), rtol=5e-2)
|
186
|
+
|
187
|
+
|
188
|
+
@pytest.mark.parametrize(
|
189
|
+
'Vbuff, RS_horz_exp, RS_vert_exp, RS_tors_exp',
|
190
|
+
[
|
191
|
+
(15,
|
192
|
+
np.array([4.00E-02, 2.12E-02, 4.12E+00, 7.00E-05, 6.03E-07, 2.03E-07, 9.11E-08, 3.36E-08, 1.09E-08,]),
|
193
|
+
np.array([6.36E-02, 6.05E-02, 6.64E-02, 9.94E-01, 2.93E-04, 9.17E-05, 1.73E-05, 9.17E-06, 3.41E-06,]),
|
194
|
+
np.array([1.52E-03, 1.38E-03, 8.59E-04, 6.95E-05, 2.86E-04, 5.35E-04, 1.16E-02, 5.03E-04, 3.11E-05,]),
|
195
|
+
),
|
196
|
+
|
197
|
+
(30,
|
198
|
+
np.array([3.30E-01, 2.76E-01, 8.88E+01, 2.70E-03, 2.84E-05, 1.15E-05, 3.72E-06, 1.85E-06, 6.55E-07,]),
|
199
|
+
np.array([5.89E-01, 5.75E-01, 8.34E-01, 8.70E+00, 1.65E-02, 7.54E-03, 4.73E-04, 2.98E-04, 1.64E-04,]),
|
200
|
+
np.array([1.40E-02, 1.33E-02, 1.07E-02, 8.31E-05, 1.60E-02, 4.26E-02, 9.19E-02, 1.40E-02, 1.46E-03,]),
|
201
|
+
),
|
202
|
+
|
203
|
+
(45,
|
204
|
+
np.array([1.16E+00, 1.10E+00, 4.02E+02, 1.74E-02, 3.07E-04, 2.02E-04, 2.56E-05, 1.55E-05, 5.92E-06,]),
|
205
|
+
np.array([2.58E+00, 2.55E+00, 3.56E+00, 1.74E+01, 2.18E-01, 2.11E-01, 2.03E-03, 1.45E-03, 1.08E-03,]),
|
206
|
+
np.array([6.12E-02, 5.91E-02, 4.50E-02, 2.94E-04, 1.95E-01, 1.11E+00, 1.89E-01, 5.54E-02, 9.36E-03,]),
|
207
|
+
),
|
208
|
+
|
209
|
+
(60,
|
210
|
+
np.array([3.00E+00, 2.99E+00, 1.01E+03, 6.09E-02, 3.95E-03, 2.53E-03 ,8.00E-05, 5.47E-05, 2.47E-05]),
|
211
|
+
np.array([9.43, 9.35, 10.9, 24.7, 2.46, 1.92, 0.00423, 0.00351, 0.00328,]),
|
212
|
+
np.array([0.221, 0.215, 0.143, 0.000666, 2, 9.1 , 0.279, 0.115, 0.0272,]),
|
213
|
+
),
|
214
|
+
]
|
215
|
+
)
|
216
|
+
|
217
|
+
# buffeting responses (spectra)
|
218
|
+
def test_response_spectra(Vbuff, RS_horz_exp, RS_vert_exp, RS_tors_exp):
|
219
|
+
|
220
|
+
omega_axis = np.array( [0.001, 0.01, 0.052, 0.1, 0.2, 0.234, 0.278, 0.3, 0.35] )*np.pi*2
|
221
|
+
|
222
|
+
# import the model and assign properties
|
223
|
+
model = import_folder(model_folder)
|
224
|
+
model.aero.windstate = iabse_11c_windstate(Vbuff)
|
225
|
+
model.modal_dry.xi0 = .3e-2
|
226
|
+
# assign ADs (Storebelt ADs)
|
227
|
+
with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
|
228
|
+
AD_funs = dill.load(file)
|
229
|
+
AD_s = AD_dict(AD_funs)
|
230
|
+
model.aero.sections['girder0'].ADs = ADs(**AD_s)
|
231
|
+
model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
|
232
|
+
|
233
|
+
# run analysis
|
234
|
+
model.run_freqsim(omega_axis,
|
235
|
+
include_selfexcited=['aero'],
|
236
|
+
include_action=['aero'],
|
237
|
+
print_progress=False, merge_aero_sections=True)
|
238
|
+
|
239
|
+
# extract PSD
|
240
|
+
global_dof_ix = np.array([1,2,3])
|
241
|
+
S = model.get_result_psd(key='full',
|
242
|
+
index=global_dof_ix)
|
243
|
+
|
244
|
+
assert np.allclose((2*np.pi)*S[0,0,:], RS_horz_exp, rtol=5e-2)
|
245
|
+
assert np.allclose((2*np.pi)*S[1,1,:], RS_vert_exp, rtol=5e-2)
|
246
|
+
assert np.allclose((31/2)**2*(2*np.pi)*S[2,2,:], RS_tors_exp, rtol=5e-2)
|
247
|
+
|
248
|
+
|
249
|
+
# buffeting responses (RMS values) - for integration we use a finer frequency axis
|
250
|
+
|
251
|
+
@pytest.mark.parametrize(
|
252
|
+
'Vbuff, RMS_horz_exp, RMS_vert_exp, RMS_tors_exp',
|
253
|
+
[(15, 0.0952, 0.173, 0.0211, ),
|
254
|
+
(30, 0.4319, 0.5596, 0.1134,),
|
255
|
+
(45, 0.99, 0.9888, 0.2984, ), # note that the lateral response is given wrong in the table in the paper. It is taken from the spectral plot.
|
256
|
+
(60, 1.7349, 1.5557, 0.7181 ),
|
257
|
+
])
|
258
|
+
|
259
|
+
def test_RMS_response(Vbuff, RMS_horz_exp, RMS_vert_exp, RMS_tors_exp):
|
260
|
+
|
261
|
+
omega_axis = np.linspace(0.001, 6, 1000)
|
262
|
+
|
263
|
+
model = import_folder(model_folder)
|
264
|
+
model.aero.windstate = iabse_11c_windstate(Vbuff)
|
265
|
+
model.modal_dry.xi0 = .3e-2
|
266
|
+
# assign ADs (Storebelt ADs)
|
267
|
+
with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
|
268
|
+
AD_funs = dill.load(file)
|
269
|
+
AD_s = AD_dict(AD_funs)
|
270
|
+
model.aero.sections['girder0'].ADs = ADs(**AD_s)
|
271
|
+
model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
|
272
|
+
|
273
|
+
# run analysis
|
274
|
+
model.run_freqsim(omega_axis,
|
275
|
+
include_selfexcited=['aero'],
|
276
|
+
include_action=['aero'],
|
277
|
+
print_progress=False, merge_aero_sections=True)
|
278
|
+
|
279
|
+
# RMS responses
|
280
|
+
stds = model.get_result_std(key = 'full')
|
281
|
+
|
282
|
+
assert isclose(stds[1], RMS_horz_exp, rel_tol = 15e-2) # horizontal # 5-15% std reported in the paper
|
283
|
+
assert isclose(stds[2], RMS_vert_exp, rel_tol = 15e-2) # vertical # 5-15% std reported in the paper
|
284
|
+
assert isclose(stds[3]*31/2, RMS_tors_exp, rel_tol = 15e-2) # torsional # 10-20% std reported in the paper
|