wawi 0.0.8__py3-none-any.whl → 0.0.9__py3-none-any.whl

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.

Potentially problematic release.


This version of wawi might be problematic. Click here for more details.

@@ -0,0 +1,72 @@
1
+ from sys import path
2
+ from collections import OrderedDict
3
+
4
+ path.append('C:/Users/knutankv/git-repos/wawi/') # this is an easy quick fix to enable importing wawi package in Abaqus environment
5
+ savefolder = 'C:/Temp/'
6
+
7
+ # NOTE: Only linear beam elements are supported in relevant sets!!
8
+
9
+ import wawi.ext.abq
10
+ import json
11
+ import numpy as np
12
+
13
+ #%% Get (O) database object
14
+ db = wawi.ext.abq.get_db('odb')
15
+
16
+ #%% Definitions
17
+ frequency_step = 'Step-1'
18
+ part = db.rootAssembly.instances['TRUSSPART']
19
+ step_obj = db.steps[frequency_step]
20
+
21
+ if 'ALL' not in part.elementSets: #CREATE SET OF ALL ELEMENTS IN PART
22
+ part.ElementSet('ALL', part.elements)
23
+
24
+ #%% Grab regions
25
+ region_full = part.elementSets['ALL']
26
+ region_hydro = db.rootAssembly.nodeSets['PALL']
27
+
28
+ #%% Get modal parameters
29
+ fn, m = wawi.ext.abq.get_modal_parameters(frequency_step)
30
+
31
+ #%% Get wind elements and mode shapes
32
+ node_matrix, element_matrix = wawi.ext.abq.get_element_matrices(region_full, obj=part)
33
+ node_labels = node_matrix[:,0]
34
+ nodes = wawi.ext.abq.create_list_of_nodes(part, node_labels)
35
+
36
+ # Export element definitions as json
37
+ el_data = dict(node_matrix=node_matrix.tolist(), element_matrix=element_matrix.tolist())
38
+
39
+ with open('element.json', 'w') as f:
40
+ json.dump(el_data, f)
41
+
42
+ phi_full_disp = wawi.ext.abq.get_nodal_phi(step_obj, nodes, flatten_components=True, field_outputs=['UT', 'UR'])
43
+
44
+ #%% Get pontoon data
45
+ node_matrix_pontoons = wawi.ext.abq.get_element_matrices(region_hydro, obj=part)
46
+ node_labels_pontoons = node_matrix_pontoons[:,0]
47
+ nodes_pontoons = wawi.ext.abq.create_list_of_nodes(part, node_labels_pontoons)
48
+ phi_h = wawi.ext.abq.get_nodal_phi(step_obj, nodes_pontoons, flatten_components=True, field_outputs=['UT', 'UR'])
49
+
50
+ # Export pontoon.json
51
+ pontoon_types = ['ptype_1']*7
52
+ rotations = -np.array([13.944407004001892, 9.2962713360012632, 4.6481356680006325, 0.0,
53
+ -4.6481356680006334, -9.2962713360012632, -13.944407004001883])
54
+
55
+ pontoon_data = OrderedDict()
56
+
57
+ for ix, node in enumerate(node_labels_pontoons):
58
+ key = 'P'+str (ix+1)
59
+ pontoon_data[key] = dict(coordinates=node_matrix_pontoons[ix, 1:].tolist(),
60
+ node=node,
61
+ rotation=rotations[ix],
62
+ pontoon_type=pontoon_types[ix])
63
+
64
+ with open('pontoon.json', 'w') as f:
65
+ json.dump(pontoon_data, f)
66
+
67
+ ## ------------------- EXPORT MODES ----------------
68
+ modal_data = dict(omega_n=(fn*2*np.pi).tolist(), m=m.tolist(), phi=dict(full=phi_full_disp.tolist(),
69
+ hydro=phi_h.tolist()))
70
+
71
+ with open('modal.json', 'w') as f:
72
+ json.dump(modal_data, f)
@@ -0,0 +1,67 @@
1
+ from sys import path
2
+ from collections import OrderedDict
3
+
4
+ path.append('C:/Users/knutankv/git-repos/wawi/') # this is an easy quick fix to enable importing wawi package in Abaqus environment
5
+ savefolder = 'C:/Temp/'
6
+
7
+ import wawi.ext.abq
8
+ import json
9
+ import numpy as np
10
+
11
+ #%% Get (O) database object
12
+ db = wawi.ext.abq.get_db('odb')
13
+
14
+ #%% Definitions
15
+ frequency_step = 'Step-6'
16
+ part = db.rootAssembly.instances['BRIDGE-1']
17
+ step_obj = db.steps[frequency_step]
18
+
19
+ if 'ALL' not in part.elementSets: #CREATE SET OF ALL ELEMENTS IN PART
20
+ part.ElementSet('ALL', part.elements)
21
+
22
+ #%% Grab regions
23
+ region_full = part.elementSets['ALL']
24
+ region_hydro = part.nodeSets['SPRING']
25
+
26
+ #%% Get modal parameters
27
+ fn, m = wawi.ext.abq.get_modal_parameters(frequency_step)
28
+
29
+ #%% Get wind elements and mode shapes
30
+ node_matrix, element_matrix = wawi.ext.abq.get_element_matrices(region_full, obj=part)
31
+ node_labels = node_matrix[:,0]
32
+
33
+ # Export element definitions as json
34
+ el_data = dict(node_matrix=node_matrix.tolist(), element_matrix=element_matrix.tolist())
35
+
36
+ with open('element.json', 'w') as f:
37
+ json.dump(el_data, f)
38
+
39
+ phi_full_disp = wawi.ext.abq.get_nodal_phi(step_obj, node_labels, flatten_components=True)
40
+
41
+ #%% Get pontoon data
42
+ node_matrix_pontoons = wawi.ext.abq.get_element_matrices(region_hydro, obj=part, sort_nodes_fun=None)
43
+ node_labels = node_matrix_pontoons[:,0]
44
+ phi_h = wawi.ext.abq.get_nodal_phi(step_obj, node_labels, flatten_components=True)
45
+
46
+ # Export pontoon.json
47
+ pontoon_types = ['ptype_1']*48
48
+ rotations = np.zeros(48)
49
+
50
+ pontoon_data = OrderedDict()
51
+
52
+ for ix, node in enumerate(node_labels):
53
+ key = 'P'+str (ix+1)
54
+ pontoon_data[key] = dict(coordinates=node_matrix_pontoons[ix, 1:].tolist(),
55
+ node=node,
56
+ rotation=rotations[ix],
57
+ pontoon_type=pontoon_types[ix])
58
+
59
+ with open('pontoon.json', 'w') as f:
60
+ json.dump(pontoon_data, f)
61
+
62
+ ## ------------------- EXPORT MODES ----------------
63
+ modal_data = dict(omega_n=(fn*2*np.pi).tolist(), m=m.tolist(), phi=dict(full=phi_full_disp.tolist(),
64
+ hydro=phi_h.tolist()))
65
+
66
+ with open('modal.json', 'w') as f:
67
+ json.dump(modal_data, f)
@@ -0,0 +1,217 @@
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
+ spectrum_type='vonKarman'
31
+ )
32
+ return windstate
33
+
34
+ davenport = lambda fred: 2*(7*fred-1+np.exp(-7*fred))/(7*fred)**2
35
+
36
+
37
+ # in-wind frequencies and damping ratio
38
+
39
+ @pytest.mark.parametrize(
40
+ 'V, expected_f, expected_zeta, tol',
41
+ [(15, np.array([0.0987, 0.279]), np.array([0.0399, 0.0096]), 0.05),
42
+ (30, np.array([0.0999, 0.2691]), np.array([0.0921, 0.0189]), 0.1),
43
+ (45, np.array([0.1014, 0.2561]), np.array([0.1689, 0.0309]), 0.1),
44
+ (60, np.array([0.1027, 0.2340]), np.array([0.3034, 0.0418]), 0.15),
45
+ (75, np.array([0.0829, 0.1994]), np.array([0.5349, 0.0148]), 0.25)],
46
+ )
47
+
48
+
49
+ # aerodynamic stability
50
+
51
+ # input - wind velocities / output - frequencies, damping ratios ( test only the unstable mode)
52
+
53
+ def test_in_wind_frequencies_and_damping( V, expected_f, expected_zeta, tol):
54
+
55
+ # importing the relevant model
56
+ model = import_folder('./tests/models/model_11a')
57
+ model.modal_dry.xi0 = .3e-2
58
+
59
+ # assign flat plate ADs (Theodorsen)
60
+ model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
61
+
62
+ model.aero.windstate = iabse_11a_windstate(V)
63
+ model.run_eig(w_initial=[0.5, 1.25], freq_kind=True, itmax=50)
64
+ lambd = model.results.lambd
65
+
66
+ f2 = np.abs(lambd[1].imag) / (2*np.pi)
67
+ zeta2 = -lambd[1].real/ np.abs(lambd[1])
68
+
69
+ assert isclose(f2, expected_f[1], rel_tol = tol)
70
+ assert isclose(zeta2, expected_zeta[1], rel_tol = tol)
71
+
72
+
73
+ # flutter speed
74
+ def test_flutter():
75
+
76
+ expected_flutter_speed = 77.45
77
+ tol = 0.01
78
+ ad_dict = flatplate_ads()
79
+
80
+ # importing the relevant model
81
+ model = import_folder(model_folder)
82
+ model.modal_dry.xi0 = .3e-2
83
+ B = model.aero.sections['girder0'].B
84
+
85
+ Ms = model.dry_M
86
+ Cs = model.dry_C
87
+ Ks = model.dry_K
88
+ phi = model.get_dry_phi('full')
89
+
90
+ # calculate the flutter speed
91
+ res = itflutter_cont_naive(Ms, Cs, Ks, phi, np.array([0,1]), ad_dict, B, V=0.1, rho=1.22, dV=5,
92
+ overshoot_factor=0.5, itmax={}, tol={}, print_progress=True)
93
+
94
+ assert isclose(res['V'][-1], expected_flutter_speed, rel_tol = tol)
95
+
96
+
97
+ # buffeting at 45 m/s (compare with analytical)
98
+ def test_mean_speed_45():
99
+
100
+ # at 45 m/s & f = 0.278 Hz
101
+ V = 45
102
+ f = 0.278
103
+
104
+ # expected values (IABSE report)
105
+ sww = 5.26 # wind spectrum
106
+ # impedance matrix
107
+ h_exp = np.array( [[-0.0619+0.0056j, -(-0.1492-0.0811j)], [-(0.01+0.0419j), -1.1559+0.5382j]] )*1e6
108
+ # PSD response
109
+ S_exp = np.transpose(np.array( [ [0.0167, -0.007j], [0.007j, 0.00293] ] ))
110
+
111
+
112
+ model = import_folder(model_folder)
113
+ model.aero.windstate = iabse_11a_windstate(V)
114
+
115
+ wind_spectrum_func = model.aero.get_generic_kaimal(nodes = model.eldef.nodes)
116
+ wind_spectrum_ww = 2*np.pi*wind_spectrum_func(f*2*np.pi)[2,2]
117
+
118
+ model.modal_dry.xi0 = .3e-2
119
+ model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
120
+ model.aero.prepare_aero_matrices()
121
+ omega = np.array([0.001, f])*np.pi*2
122
+ HH = eval_3d_fun(model.get_frf_fun(opt = 1), omega)
123
+
124
+ model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
125
+ model.run_freqsim(omega,
126
+ include_selfexcited=['aero'],
127
+ include_action=['aero'],
128
+ print_progress=False, merge_aero_sections=True)
129
+
130
+ global_dof_ix = np.array([2,3])
131
+ S = model.get_result_psd(key='full',
132
+ index=global_dof_ix)
133
+
134
+ assert isclose(wind_spectrum_ww, sww, rel_tol = 0.01)
135
+ assert np.allclose(HH[:,:,1], h_exp, rtol=1e-2)
136
+ assert np.allclose((2*np.pi)*S[:,:,-1], S_exp, rtol=5e-2)
137
+
138
+
139
+ # buffeting responses (spectra)
140
+
141
+ @pytest.mark.parametrize(
142
+ 'Vbuff, RS_vert_exp, RS_tors_exp',
143
+ [
144
+ (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,]),
145
+ 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]) ),
146
+
147
+ (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,]),
148
+ 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,]) ),
149
+
150
+ (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]),
151
+ 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]) ),
152
+
153
+ (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,]),
154
+ 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,]) ),
155
+
156
+ (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,]),
157
+ 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,]) ),
158
+
159
+ ]
160
+ )
161
+
162
+
163
+
164
+ # buffeting responses (spectra)
165
+ def test_response_spectra(Vbuff, RS_vert_exp, RS_tors_exp):
166
+
167
+ 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
168
+
169
+ model = import_folder(model_folder)
170
+ model.aero.windstate = iabse_11a_windstate(Vbuff)
171
+ model.modal_dry.xi0 = .3e-2
172
+ model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
173
+ model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
174
+
175
+ model.run_freqsim(omega_axis,
176
+ include_selfexcited=['aero'],
177
+ include_action=['aero'],
178
+ print_progress=False, merge_aero_sections=True)
179
+
180
+ global_dof_ix = np.array([2,3])
181
+ S = model.get_result_psd(key='full',
182
+ index=global_dof_ix)
183
+
184
+ assert np.allclose((2*np.pi)*S[0,0,:], RS_vert_exp, rtol=5e-2)
185
+ assert np.allclose((31/2)**2*(2*np.pi)*S[1,1,:], RS_tors_exp, rtol=5e-2)
186
+
187
+
188
+
189
+ @pytest.mark.parametrize(
190
+ 'Vbuff, RMS_vert_exp, RMS_tors_exp',
191
+ [(15, 0.2603, 0.0419 ),
192
+ (30, 0.778 , 0.2027 ),
193
+ (45, 1.3404, 0.4792 ),
194
+ (60, 2.1601, 0.9306 ),
195
+ (75, 4.4848, 2.8414 )],
196
+ )
197
+
198
+ # buffeting responses (RMS values) - for integration we use a finer frequency axis
199
+ def test_RMS_response(Vbuff, RMS_vert_exp, RMS_tors_exp):
200
+
201
+ omega_axis = np.linspace(0.001, 6, 1000)
202
+
203
+ model = import_folder(model_folder)
204
+ model.aero.windstate = iabse_11a_windstate(Vbuff)
205
+ model.modal_dry.xi0 = .3e-2
206
+ model.aero.sections['girder0'].ADs = ADs(**flatplate_ads())
207
+ model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
208
+
209
+ model.run_freqsim(omega_axis,
210
+ include_selfexcited=['aero'],
211
+ include_action=['aero'],
212
+ print_progress=False, merge_aero_sections=True)
213
+
214
+ stds = model.get_result_std(key = 'full')
215
+
216
+ assert isclose(stds[2], RMS_vert_exp, rel_tol = 10e-2) # vertical # 5-10% std reported in the paper
217
+ assert isclose(stds[3]*31/2, RMS_tors_exp, rel_tol = 20e-2) # torsional # 10-25% std reported in the paper
@@ -0,0 +1,281 @@
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
+ spectrum_type='vonKarman')
59
+ return windstate
60
+
61
+ davenport = lambda fred: 2*(7*fred-1+np.exp(-7*fred))/(7*fred)**2
62
+
63
+ @pytest.mark.parametrize(
64
+ 'V, expected_f, expected_zeta, tol',
65
+ [(45, np.array([0.0520, 0.1029, 0.2488,]), np.array([0.0142, 0.1660, 0.0429,]), 0.1),
66
+ (60, np.array([0.0520, 0.1025, 0.2275,]), np.array([0.0169, 0.2879, 0.0366,]), 0.15),
67
+ (72, np.array([0.0520, 0.0933, 0.2049]), np.array([0.0209, 0.5099, 0.0015,]), 0.25)],
68
+ )
69
+
70
+ def test_in_wind_frequencies_and_damping( V, expected_f, expected_zeta, tol):
71
+
72
+ # importing the relevant model
73
+ model = import_folder(model_folder)
74
+ model.modal_dry.xi0 = .3e-2
75
+
76
+ # assign ADs (Storebelt ADs)
77
+ with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
78
+ AD_funs = dill.load(file)
79
+ AD_s = AD_dict(AD_funs)
80
+ model.aero.sections['girder0'].ADs = ADs(**AD_s)
81
+
82
+ model.aero.windstate = iabse_11c_windstate(V)
83
+ model.run_eig(w_initial=[0.3, 0.6, 1.5], freq_kind=True, itmax=50)
84
+ lambd = model.results.lambd
85
+
86
+ f3 = np.abs(lambd[2].imag) / (2*np.pi)
87
+ zeta3 = -lambd[2].real/ np.abs(lambd[2])
88
+
89
+ assert isclose(f3, expected_f[2], rel_tol = tol)
90
+ assert isclose(zeta3, expected_zeta[2], rel_tol = tol)
91
+
92
+
93
+ # flutter speed
94
+ def test_flutter():
95
+
96
+ expected_flutter_speed = 72.3
97
+ tol = 0.01
98
+ with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
99
+ AD_funs = dill.load(file)
100
+ ad_dict = AD_dict(AD_funs)
101
+
102
+ # importing the relevant model
103
+ model = import_folder(model_folder)
104
+ model.modal_dry.xi0 = .3e-2
105
+ B = model.aero.sections['girder0'].B
106
+
107
+ Ms = model.dry_M
108
+ Cs = model.dry_C
109
+ Ks = model.dry_K
110
+ phi = model.get_dry_phi('full')
111
+
112
+ # calculate the flutter speed
113
+ res = itflutter_cont_naive(Ms, Cs, Ks, phi, np.array([0,1]), ad_dict, B, V=0.1, rho=1.22, dV=5,
114
+ overshoot_factor=0.5, itmax={}, tol={}, print_progress=True)
115
+
116
+ assert isclose(res['V'][-1], expected_flutter_speed, rel_tol = tol)
117
+
118
+ # buffeting at 45 m/s (compare with reference values)
119
+ def test_mean_speed_45():
120
+
121
+ # at 45 m/s & f = 0.278 Hz
122
+ V = 45
123
+ f = 0.278
124
+
125
+ # expected values (IABSE report)
126
+ swind_exp = np.array( [ [7.2139, 0], [0, 5.2599] ] )
127
+ # admittance matrix (BqBq matrix in wawi)
128
+ Bq_exp = np.array( [
129
+ [ 0.0088 , 0.0116 ],
130
+ [ 0.0076 , 0.2536 ],
131
+ [ 0.099 , 2.0687 ],
132
+ ] )*1e4
133
+ # impedance matrix
134
+ h_exp = np.array( [
135
+ [ -6.74+0.11j , -0.01+0.01j , -0.29-0.26j ],
136
+ [ 0.07+0.04j , -6.25+0.80j , -19.40-0.37j ],
137
+ [ 0.06+0.23j , 0.50+5.39j , -158.52+79.71j ],
138
+ ] )*1e4
139
+ # sign convention
140
+ h_exp[2, :] *= -1
141
+ h_exp[:, 2] *= -1
142
+
143
+ # PSD response
144
+ S_exp = np.array( [ [0.26-0j, 1+1j, 1-1j ],
145
+ [1-1j, 20-0j, 0-13j ],
146
+ [1+1j, 0+13j, 8-0j ],
147
+ ])*1e-4
148
+ # sign convention
149
+ S_exp[2, :] *= -1
150
+ S_exp[:, 2] *= -1
151
+
152
+ model = import_folder(model_folder)
153
+ model.aero.windstate = iabse_11c_windstate(V)
154
+
155
+ wind_spectrum_func = model.aero.get_generic_kaimal(nodes = model.eldef.nodes)
156
+ wind_spectrum_uu = 2*np.pi*wind_spectrum_func(f*2*np.pi)[0,0]
157
+ wind_spectrum_ww = 2*np.pi*wind_spectrum_func(f*2*np.pi)[2,2]
158
+
159
+ model.modal_dry.xi0 = .3e-2
160
+ # assign ADs (Storebelt ADs)
161
+ with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
162
+ AD_funs = dill.load(file)
163
+ AD_s = AD_dict(AD_funs)
164
+ model.aero.sections['girder0'].ADs = ADs(**AD_s)
165
+ model.aero.prepare_aero_matrices()
166
+ omega = np.array([0.001, f])*np.pi*2
167
+ HH = eval_3d_fun(model.get_frf_fun(opt = 1), omega)
168
+
169
+ model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
170
+ model.run_freqsim(omega,
171
+ include_selfexcited=['aero'],
172
+ include_action=['aero'],
173
+ print_progress=False, merge_aero_sections=True)
174
+
175
+ global_dof_ix = np.array([1,2,3])
176
+ S = model.get_result_psd(key='full',
177
+ index=global_dof_ix)
178
+
179
+ assert isclose(wind_spectrum_ww, swind_exp[1,1], rel_tol = 0.01)
180
+ assert isclose(wind_spectrum_uu, swind_exp[0,0], rel_tol = 0.01)
181
+ assert np.allclose(np.diag(HH[:,:,1]), np.diag(h_exp), rtol=1e-2)
182
+ assert np.allclose((2*np.pi)*np.diag(S[:,:,-1]), np.diag(S_exp), rtol=5e-2)
183
+
184
+
185
+ @pytest.mark.parametrize(
186
+ 'Vbuff, RS_horz_exp, RS_vert_exp, RS_tors_exp',
187
+ [
188
+ (15,
189
+ 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,]),
190
+ 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,]),
191
+ 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,]),
192
+ ),
193
+
194
+ (30,
195
+ 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,]),
196
+ 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,]),
197
+ 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,]),
198
+ ),
199
+
200
+ (45,
201
+ 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,]),
202
+ 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,]),
203
+ 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,]),
204
+ ),
205
+
206
+ (60,
207
+ 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]),
208
+ np.array([9.43, 9.35, 10.9, 24.7, 2.46, 1.92, 0.00423, 0.00351, 0.00328,]),
209
+ np.array([0.221, 0.215, 0.143, 0.000666, 2, 9.1 , 0.279, 0.115, 0.0272,]),
210
+ ),
211
+ ]
212
+ )
213
+
214
+ # buffeting responses (spectra)
215
+ def test_response_spectra(Vbuff, RS_horz_exp, RS_vert_exp, RS_tors_exp):
216
+
217
+ 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
218
+
219
+ # import the model and assign properties
220
+ model = import_folder(model_folder)
221
+ model.aero.windstate = iabse_11c_windstate(Vbuff)
222
+ model.modal_dry.xi0 = .3e-2
223
+ # assign ADs (Storebelt ADs)
224
+ with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
225
+ AD_funs = dill.load(file)
226
+ AD_s = AD_dict(AD_funs)
227
+ model.aero.sections['girder0'].ADs = ADs(**AD_s)
228
+ model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
229
+
230
+ # run analysis
231
+ model.run_freqsim(omega_axis,
232
+ include_selfexcited=['aero'],
233
+ include_action=['aero'],
234
+ print_progress=False, merge_aero_sections=True)
235
+
236
+ # extract PSD
237
+ global_dof_ix = np.array([1,2,3])
238
+ S = model.get_result_psd(key='full',
239
+ index=global_dof_ix)
240
+
241
+ assert np.allclose((2*np.pi)*S[0,0,:], RS_horz_exp, rtol=5e-2)
242
+ assert np.allclose((2*np.pi)*S[1,1,:], RS_vert_exp, rtol=5e-2)
243
+ assert np.allclose((31/2)**2*(2*np.pi)*S[2,2,:], RS_tors_exp, rtol=5e-2)
244
+
245
+
246
+ # buffeting responses (RMS values) - for integration we use a finer frequency axis
247
+
248
+ @pytest.mark.parametrize(
249
+ 'Vbuff, RMS_horz_exp, RMS_vert_exp, RMS_tors_exp',
250
+ [(15, 0.0952, 0.173, 0.0211, ),
251
+ (30, 0.4319, 0.5596, 0.1134,),
252
+ (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.
253
+ (60, 1.7349, 1.5557, 0.7181 ),
254
+ ])
255
+
256
+ def test_RMS_response(Vbuff, RMS_horz_exp, RMS_vert_exp, RMS_tors_exp):
257
+
258
+ omega_axis = np.linspace(0.001, 6, 1000)
259
+
260
+ model = import_folder(model_folder)
261
+ model.aero.windstate = iabse_11c_windstate(Vbuff)
262
+ model.modal_dry.xi0 = .3e-2
263
+ # assign ADs (Storebelt ADs)
264
+ with open( model_folder + '/AD_funs_SB_scanlan.pkl', 'rb') as file:
265
+ AD_funs = dill.load(file)
266
+ AD_s = AD_dict(AD_funs)
267
+ model.aero.sections['girder0'].ADs = ADs(**AD_s)
268
+ model.aero.sections['girder0'].Admittance = lambda fred: np.full((4, 3), davenport(fred))
269
+
270
+ # run analysis
271
+ model.run_freqsim(omega_axis,
272
+ include_selfexcited=['aero'],
273
+ include_action=['aero'],
274
+ print_progress=False, merge_aero_sections=True)
275
+
276
+ # RMS responses
277
+ stds = model.get_result_std(key = 'full')
278
+
279
+ assert isclose(stds[1], RMS_horz_exp, rel_tol = 15e-2) # horizontal # 5-15% std reported in the paper
280
+ assert isclose(stds[2], RMS_vert_exp, rel_tol = 15e-2) # vertical # 5-15% std reported in the paper
281
+ assert isclose(stds[3]*31/2, RMS_tors_exp, rel_tol = 15e-2) # torsional # 10-20% std reported in the paper