wawi 0.0.1__tar.gz → 0.0.3__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.3
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,104 @@
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, flatplate_ads
16
+ from wawi.wind import itflutter_cont_naive
17
+ from wawi.general import eval_3d_fun
18
+
19
+ model_folder = './tests/models/model_2a'
20
+
21
+ def AD_dict(AD_funs):
22
+ AD_s = dict(
23
+ A1 = lambda v: -AD_funs['a_fun'][0](v), # sign convention
24
+ A2 = AD_funs['a_fun'][1],
25
+ A3 = AD_funs['a_fun'][2],
26
+ A4 = lambda v: -AD_funs['a_fun'][3](v),
27
+ A5 = lambda v: -AD_funs['a_fun'][4](v),
28
+ A6 = lambda v: -AD_funs['a_fun'][5](v),
29
+
30
+ H1 = AD_funs['h_fun'][0],
31
+ H2 = lambda v: -AD_funs['h_fun'][1](v),
32
+ H3 = lambda v: -AD_funs['h_fun'][2](v),
33
+ H4 = AD_funs['h_fun'][3],
34
+ H5 = AD_funs['h_fun'][4],
35
+ H6 = AD_funs['h_fun'][5],
36
+
37
+ P1 = AD_funs['p_fun'][0],
38
+ P2 = lambda v: -AD_funs['p_fun'][1](v),
39
+ P3 = lambda v: -AD_funs['p_fun'][2](v),
40
+ P4 = AD_funs['p_fun'][3],
41
+ P5 = AD_funs['p_fun'][4],
42
+ P6 = AD_funs['p_fun'][5],
43
+ )
44
+ return AD_s
45
+
46
+ def iabse_2a_windstate(mean_v):
47
+ windstate = Windstate(mean_v,
48
+ 90,
49
+ Iu=0.1,
50
+ Iw=0.05,
51
+ Au=6.8, Aw=9.4, # not used in von Karman
52
+ Cuy=10.0, Cwy=6.5,
53
+ Cuz=10.0, Cwz=3.0,
54
+ Lux=200.0, Lwx=20.0,
55
+ x_ref=[0,0,0], rho=1.22,
56
+ options = {
57
+ 'spectra_type': 'vonKarman'
58
+ }
59
+ )
60
+
61
+ return windstate
62
+
63
+ davenport = lambda fred: 2*(7*fred-1+np.exp(-7*fred))/(7*fred)**2
64
+
65
+ omega = np.linspace(0.001, 6, 1000)
66
+
67
+ # import the model and assign properties
68
+ model = import_folder(model_folder)
69
+ model.modal_dry.xi0 = .3e-2
70
+
71
+ # assign ADs (BB3 ADs)
72
+ with open( model_folder + '/AD_funs_BB3_scanlan.pkl', 'rb') as file:
73
+ AD_funs = dill.load(file)
74
+ AD_s = AD_dict(AD_funs)
75
+ for key in model.aero.sections:
76
+ model.aero.sections[key].ADs = ADs(**AD_s)
77
+
78
+ V = 30
79
+ # assign windstate
80
+ model.aero.windstate = iabse_2a_windstate(30)
81
+ # admittance
82
+ for key in model.aero.sections:
83
+ model.aero.sections[key].Admittance = lambda fred: np.full((4, 3), davenport(fred))
84
+
85
+ model.run_eig(w_initial=model.modal_dry.omega_n.tolist(), freq_kind=True, itmax=100)
86
+
87
+ # run analysis
88
+ model.run_freqsim(omega,
89
+ include_selfexcited=['aero'],
90
+ include_action=['aero'],
91
+ print_progress=False, merge_aero_sections=True)
92
+ # RMS responses
93
+ stds = model.get_result_std(key = 'full')
94
+ # global dofs
95
+ global_dof_ix1 = model.eldef.node_dof_lookup(36)[1:4]
96
+ global_dof_ix2 = model.eldef.node_dof_lookup(26)[1:4]
97
+
98
+ assert isclose(stds[global_dof_ix1][0], RMS_horz_exp1, rel_tol = 10e-2) # midspan horizontal
99
+ assert isclose(stds[global_dof_ix1][1], RMS_vert_exp1, rel_tol = 10e-2) # midspan vertical
100
+ assert isclose(stds[global_dof_ix1][2]*31/2, RMS_tors_exp1, rel_tol = 10e-2) # midspan torsional
101
+
102
+ assert isclose(stds[global_dof_ix2][0], RMS_horz_exp2, rel_tol = 15e-2) # q-span horizontal
103
+ assert isclose(stds[global_dof_ix2][1], RMS_vert_exp2, rel_tol = 15e-2) # q-span vertical
104
+ assert isclose(stds[global_dof_ix2][2]*31/2, RMS_tors_exp2, rel_tol = 20e-2) # q-span torsional
@@ -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