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.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: wawi
3
- Version: 0.0.1
4
- Summary: WAwe and WInd response prediction
5
- Author-email: "Knut A. Kvaale" <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>
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
  ![WAWI logo](https://raw.githubusercontent.com/knutankv/wawi/main/wawi-logo-animated.svg)
47
48
  =======================
48
49
 
49
50
  What is wawi?
50
51
  =======================
51
- [NOT PUBLISHED YET]
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@master
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/master/examples).
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
- [NOT PUBLISHED YET]
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@master
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/master/examples).
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 = "WAwe and WInd response prediction"
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. Kvaale", email = "knut.a.kvale@ntnu.no"},
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