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.
- examples/3 Software interfacing/Abaqus model export/bergsoysund-export.py +72 -0
- examples/3 Software interfacing/Abaqus model export/test.py +67 -0
- tests/test_IABSE_step11a.py +217 -0
- tests/test_IABSE_step11c.py +281 -0
- tests/test_IABSE_step2a.py +263 -0
- tests/test_wind.py +71 -0
- wawi/__init__.py +1 -1
- wawi/ext/__init__.py +0 -0
- wawi/ext/abq.py +259 -0
- wawi/ext/abq_legacy.py +462 -0
- wawi/ext/ansys.py +0 -0
- wawi/ext/orcaflex.py +0 -0
- wawi/ext/sofistik.py +0 -0
- wawi/model/__init__.py +11 -0
- wawi/model/_aero.py +363 -0
- wawi/model/_dry.py +141 -0
- wawi/model/_hydro.py +882 -0
- wawi/model/_model.py +1324 -0
- wawi/model/_screening.py +124 -0
- {wawi-0.0.8.dist-info → wawi-0.0.9.dist-info}/METADATA +1 -1
- wawi-0.0.9.dist-info/RECORD +39 -0
- wawi-0.0.9.dist-info/top_level.txt +3 -0
- wawi-0.0.8.dist-info/RECORD +0 -21
- wawi-0.0.8.dist-info/top_level.txt +0 -1
- {wawi-0.0.8.dist-info → wawi-0.0.9.dist-info}/LICENSE +0 -0
- {wawi-0.0.8.dist-info → wawi-0.0.9.dist-info}/WHEEL +0 -0
@@ -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
|