wawi 0.0.8__py3-none-any.whl → 0.0.11__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 +5 -3
- wawi/ext/__init__.py +0 -0
- wawi/ext/abq.py +259 -0
- wawi/ext/ansys.py +0 -0
- wawi/ext/orcaflex.py +0 -0
- wawi/ext/sofistik.py +0 -0
- wawi/io.py +1 -1
- 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 +1338 -0
- wawi/model/_screening.py +124 -0
- wawi/time_domain.py +45 -0
- wawi/wind.py +1 -1
- {wawi-0.0.8.dist-info → wawi-0.0.11.dist-info}/METADATA +10 -2
- wawi-0.0.11.dist-info/RECORD +38 -0
- {wawi-0.0.8.dist-info → wawi-0.0.11.dist-info}/WHEEL +1 -1
- wawi-0.0.11.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.11.dist-info/licenses}/LICENSE +0 -0
wawi/model/_aero.py
ADDED
@@ -0,0 +1,363 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import json
|
3
|
+
from wawi.wind import quasisteady_ads, ADs, compute_aero_matrices, generic_kaimal_matrix
|
4
|
+
from wawi.general import fun_scale, fun_sum
|
5
|
+
from beef.rotation import rodrot
|
6
|
+
|
7
|
+
'''
|
8
|
+
AERO SUBMODULE
|
9
|
+
'''
|
10
|
+
class Aero:
|
11
|
+
def __init__(self, sections=None, phi_key='full', element_assignments=None, windstate=None):
|
12
|
+
self.sections = sections
|
13
|
+
self.elements = ensure_list_of_ints(element_assignments)
|
14
|
+
self.phi_key = phi_key
|
15
|
+
self.phi = None
|
16
|
+
self.phi_ixs = dict()
|
17
|
+
self.eldef = dict()
|
18
|
+
|
19
|
+
self.windstate = windstate
|
20
|
+
|
21
|
+
self.Kfun = None
|
22
|
+
self.Cfun = None
|
23
|
+
|
24
|
+
def get_phi(self, group):
|
25
|
+
return self.phi[self.phi_ixs[group], :]
|
26
|
+
|
27
|
+
@property
|
28
|
+
def windstate(self):
|
29
|
+
return self._windstate
|
30
|
+
|
31
|
+
@windstate.setter
|
32
|
+
def windstate(self, val):
|
33
|
+
self._windstate = val
|
34
|
+
self.Cfun = None
|
35
|
+
self.Kfun = None
|
36
|
+
self.Sqq_aero = None
|
37
|
+
|
38
|
+
@property
|
39
|
+
def K(self):
|
40
|
+
if self.Kfun is None:
|
41
|
+
return 0.0
|
42
|
+
else:
|
43
|
+
return self.Kfun
|
44
|
+
|
45
|
+
@property
|
46
|
+
def C(self):
|
47
|
+
if self.Cfun is None:
|
48
|
+
return 0.0
|
49
|
+
else:
|
50
|
+
return self.Cfun
|
51
|
+
|
52
|
+
def get_generic_kaimal(self, nodes=None, group=None):
|
53
|
+
if (nodes is None) and (group is None):
|
54
|
+
raise ValueError('Input either nodes or group!')
|
55
|
+
elif group is not None:
|
56
|
+
nodes = self.eldef[group].nodes
|
57
|
+
|
58
|
+
return lambda om: generic_kaimal_matrix(om, nodes, self.windstate.T, self.windstate.A,
|
59
|
+
self.windstate.sigma, self.windstate.C, self.windstate.Lx, self.windstate.U, spectrum_type=self.windstate.spectrum_type)
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
def get_aero_matrices(self, omega_reduced=None, aero_sections=None, print_progress=False):
|
64
|
+
if aero_sections is None:
|
65
|
+
aero_sections = self.elements.keys()
|
66
|
+
|
67
|
+
Cae_m = [None]*len(aero_sections)
|
68
|
+
Kae_m = [None]*len(aero_sections)
|
69
|
+
|
70
|
+
for ix, sec in enumerate(aero_sections):
|
71
|
+
phi = self.get_phi(sec)
|
72
|
+
U = self.windstate.U
|
73
|
+
AD = self.sections[sec].ADs
|
74
|
+
B = self.sections[sec].B
|
75
|
+
els = self.elements[sec]
|
76
|
+
T_wind = self.windstate.T
|
77
|
+
|
78
|
+
Kae_m[ix], Cae_m[ix] = compute_aero_matrices(U, AD, B, els, T_wind, phi,
|
79
|
+
omega_reduced=omega_reduced, print_progress=print_progress, rho=self.windstate.rho)
|
80
|
+
|
81
|
+
return fun_sum(*Kae_m), fun_sum(*Cae_m)
|
82
|
+
|
83
|
+
|
84
|
+
def prepare_aero_matrices(self, omega=None, print_progress=False, aero_sections=None):
|
85
|
+
self.Kfun, self.Cfun = self.get_aero_matrices(omega_reduced=omega,
|
86
|
+
print_progress=print_progress,
|
87
|
+
aero_sections=aero_sections)
|
88
|
+
|
89
|
+
|
90
|
+
'''
|
91
|
+
AERO SECTION CLASS
|
92
|
+
'''
|
93
|
+
def ensure_list_of_ints(d):
|
94
|
+
for key in d:
|
95
|
+
d[key] = [int(di) for di in d[key]]
|
96
|
+
|
97
|
+
return d
|
98
|
+
|
99
|
+
|
100
|
+
class AeroSection:
|
101
|
+
def __init__(self, D=None, B=None, ADs=None, Cd=0.0, dCd=0.0, Cm=0.0, dCm=0.0, Cl=0.0, dCl=0.0, admittance=None):
|
102
|
+
self.D = D
|
103
|
+
self.B = B
|
104
|
+
self.Cd = Cd
|
105
|
+
self.dCd = dCd
|
106
|
+
self.Cm = Cm
|
107
|
+
self.dCm = dCm
|
108
|
+
self.Cl = Cl
|
109
|
+
self.dCl = dCl
|
110
|
+
self.admittance = admittance
|
111
|
+
|
112
|
+
if ADs is None:
|
113
|
+
self.add_quasisteady_ads()
|
114
|
+
self.quasisteady = True
|
115
|
+
else:
|
116
|
+
self.ADs = ADs
|
117
|
+
self.quasisteady = False
|
118
|
+
|
119
|
+
def assign(self, **kwargs):
|
120
|
+
for key, val in kwargs.items():
|
121
|
+
setattr(self, key, val)
|
122
|
+
|
123
|
+
@property
|
124
|
+
def all_lc(self):
|
125
|
+
keys = ['Cd', 'Cm', 'Cl', 'dCd', 'dCm', 'dCl']
|
126
|
+
return {key: getattr(self, key) for key in keys}
|
127
|
+
|
128
|
+
def add_quasisteady_ads(self):
|
129
|
+
self.ADs = ADs(**quasisteady_ads(self.D, self.B, self.all_lc), ad_type='quasisteady')
|
130
|
+
|
131
|
+
def __str__(self):
|
132
|
+
return f"<AeroSection> (D = {self.D}, B = {self.B}, Cd = {self.Cd}, Cd' = {self.dCd}, Cl = {self.Cl}, Cl' = {self.dCl}, Cm = {self.Cm}, Cm' = {self.dCm}, Admittance = {self.admittance})"
|
133
|
+
|
134
|
+
|
135
|
+
'''
|
136
|
+
WIND STATE CLASS
|
137
|
+
'''
|
138
|
+
|
139
|
+
class Windstate:
|
140
|
+
def __init__(self, U0, direction, Au=0.0, Av=0.0, Aw=0.0,
|
141
|
+
Iu=0.0, Iv=0.0, Iw=0.0, Cuy=0.0, Cuz=0.0, Cvy=0.0, Cvz=0.0, Cwy=0.0, Cwz=0.0, Lux=0.0, Lvx=0.0, Lwx=0.0,
|
142
|
+
x_ref=np.array([0,0,0]), scaling=None, name=None, spectrum_type='kaimal', rho=1.225, options=None):
|
143
|
+
|
144
|
+
self.U0 = U0
|
145
|
+
self.direction = direction # interpreted as positive in clock-wise direction and defines origin and not heading!
|
146
|
+
|
147
|
+
self.A = np.array([Au, Av, Aw])
|
148
|
+
self.I = np.array([Iu, Iv, Iw])
|
149
|
+
|
150
|
+
self.C = np.array([[0, 0, 0],
|
151
|
+
[Cuy, Cvy, Cwy],
|
152
|
+
[Cuz, Cvz, Cwz]])
|
153
|
+
|
154
|
+
self.Lx = np.array([Lux, Lvx, Lwx])
|
155
|
+
|
156
|
+
self.spectrum_type = spectrum_type
|
157
|
+
self.options = options
|
158
|
+
|
159
|
+
if scaling is None:
|
160
|
+
self.scaling = lambda x: 1.0 #{x} = [x,y,z]
|
161
|
+
else:
|
162
|
+
self.scaling = scaling
|
163
|
+
|
164
|
+
self.x_ref = x_ref
|
165
|
+
self.name = name
|
166
|
+
self.rho = rho
|
167
|
+
|
168
|
+
def __str__(self):
|
169
|
+
string = f'''\
|
170
|
+
WAWI WindState
|
171
|
+
--------------
|
172
|
+
U={self.U0:.1f}m/s, direction={self.direction:.1f}deg
|
173
|
+
A=[{self.Au:.2f}, {self.Av:.2f}, {self.Aw:.2f}]
|
174
|
+
I=[{self.Iu:.2f}, {self.Iv:.2f}, {self.Iw:.2f}]
|
175
|
+
Cux, Cvx, Cwx = [{self.C[0,0]}, {self.C[0,1]}, {self.C[0,2]}]
|
176
|
+
Cuy, Cvy, Cwy = [{self.C[1,0]}, {self.C[1,1]}, {self.C[1,2]}]
|
177
|
+
Cuz, Cvz, Cwz = [{self.C[2,0]}, {self.C[2,1]}, {self.C[2,2]}]
|
178
|
+
Lx = [{self.Lx[0]:.2f}, {self.Lx[1]:.2f}, {self.Lx[2]:.2f}]
|
179
|
+
'''
|
180
|
+
|
181
|
+
return string
|
182
|
+
|
183
|
+
|
184
|
+
@property
|
185
|
+
def Iu(self):
|
186
|
+
return self.I[0]
|
187
|
+
@Iu.setter
|
188
|
+
def Iu(self, val):
|
189
|
+
self.I[0] = val
|
190
|
+
|
191
|
+
@property
|
192
|
+
def Iv(self):
|
193
|
+
return self.I[1]
|
194
|
+
@Iv.setter
|
195
|
+
def Iv(self, val):
|
196
|
+
self.I[1] = val
|
197
|
+
|
198
|
+
@property
|
199
|
+
def Iw(self):
|
200
|
+
return self.I[2]
|
201
|
+
@Iw.setter
|
202
|
+
def Iw(self, val):
|
203
|
+
self.I[2] = val
|
204
|
+
|
205
|
+
@property
|
206
|
+
def sigma(self):
|
207
|
+
return self.I*self.U0
|
208
|
+
|
209
|
+
@property
|
210
|
+
def T(self):
|
211
|
+
return rodrot((-self.direction+180)*np.pi/180)
|
212
|
+
|
213
|
+
@property
|
214
|
+
def U(self):
|
215
|
+
return fun_scale(self.scaling, self.U0)
|
216
|
+
|
217
|
+
@property
|
218
|
+
def V(self):
|
219
|
+
return self.U
|
220
|
+
|
221
|
+
@property
|
222
|
+
def V0(self):
|
223
|
+
return self.U0
|
224
|
+
|
225
|
+
@property
|
226
|
+
def Au(self):
|
227
|
+
return self.A[0]
|
228
|
+
@Au.setter
|
229
|
+
def Au(self, val):
|
230
|
+
self.A[0] = val
|
231
|
+
|
232
|
+
@property
|
233
|
+
def Av(self):
|
234
|
+
return self.A[1]
|
235
|
+
@Av.setter
|
236
|
+
def Av(self, val):
|
237
|
+
self.A[1] = val
|
238
|
+
|
239
|
+
@property
|
240
|
+
def Aw(self):
|
241
|
+
return self.A[2]
|
242
|
+
@Aw.setter
|
243
|
+
def Aw(self, val):
|
244
|
+
self.A[2] = val
|
245
|
+
|
246
|
+
@property
|
247
|
+
def sigma_u(self):
|
248
|
+
return self.sigma[0]
|
249
|
+
|
250
|
+
@property
|
251
|
+
def sigma_v(self):
|
252
|
+
return self.sigma[1]
|
253
|
+
|
254
|
+
@property
|
255
|
+
def sigma_w(self):
|
256
|
+
return self.sigma[2]
|
257
|
+
|
258
|
+
@property
|
259
|
+
def Cux(self):
|
260
|
+
return self.C[0,0]
|
261
|
+
@Cux.setter
|
262
|
+
def Cux(self, val):
|
263
|
+
self.C[0,0] = val
|
264
|
+
|
265
|
+
@property
|
266
|
+
def Cuy(self):
|
267
|
+
return self.C[1,0]
|
268
|
+
@Cuy.setter
|
269
|
+
def Cuy(self, val):
|
270
|
+
self.C[1,0] = val
|
271
|
+
|
272
|
+
@property
|
273
|
+
def Cuz(self):
|
274
|
+
return self.C[2,0]
|
275
|
+
@Cuz.setter
|
276
|
+
def Cuz(self, val):
|
277
|
+
self.C[2,0] = val
|
278
|
+
|
279
|
+
@property
|
280
|
+
def Cvx(self):
|
281
|
+
return self.C[0,1]
|
282
|
+
@Cvx.setter
|
283
|
+
def Cvx(self, val):
|
284
|
+
self.C[0,1] = val
|
285
|
+
|
286
|
+
@property
|
287
|
+
def Cvy(self):
|
288
|
+
return self.C[1,1]
|
289
|
+
@Cvy.setter
|
290
|
+
def Cvy(self, val):
|
291
|
+
self.C[1,1] = val
|
292
|
+
|
293
|
+
@property
|
294
|
+
def Cvz(self):
|
295
|
+
return self.C[2,1]
|
296
|
+
@Cvz.setter
|
297
|
+
def Cvz(self, val):
|
298
|
+
self.C[2,1] = val
|
299
|
+
|
300
|
+
@property
|
301
|
+
def Cwx(self):
|
302
|
+
return self.C[0,2]
|
303
|
+
@Cwx.setter
|
304
|
+
def Cwx(self, val):
|
305
|
+
self.C[0,2] = val
|
306
|
+
|
307
|
+
@property
|
308
|
+
def Cwy(self):
|
309
|
+
return self.C[1,2]
|
310
|
+
@Cwy.setter
|
311
|
+
def Cwy(self, val):
|
312
|
+
self.C[1,2] = val
|
313
|
+
|
314
|
+
@property
|
315
|
+
def Cwz(self):
|
316
|
+
return self.C[2,2]
|
317
|
+
@Cwz.setter
|
318
|
+
def Cwz(self, val):
|
319
|
+
self.C[2,2] = val
|
320
|
+
|
321
|
+
@property
|
322
|
+
def Lux(self):
|
323
|
+
return self.Lx[0]
|
324
|
+
@Lux.setter
|
325
|
+
def Lux(self, val):
|
326
|
+
self.Lx[0] = val
|
327
|
+
|
328
|
+
@property
|
329
|
+
def Lvx(self):
|
330
|
+
return self.Lx[1]
|
331
|
+
@Lvx.setter
|
332
|
+
def Lvx(self, val):
|
333
|
+
self.Lx[1] = val
|
334
|
+
|
335
|
+
@property
|
336
|
+
def Lwx(self):
|
337
|
+
return self.Lx[2]
|
338
|
+
@Lwx.setter
|
339
|
+
def Lwx(self, val):
|
340
|
+
self.Lx[2] = val
|
341
|
+
|
342
|
+
# Alternative constructor
|
343
|
+
@classmethod
|
344
|
+
def from_json(cls, json_file, **kwargs):
|
345
|
+
|
346
|
+
with open(json_file, 'r') as fileobj:
|
347
|
+
data = json.load(fileobj)
|
348
|
+
|
349
|
+
direction = data.pop('direction')
|
350
|
+
U0 = data.pop('U0')
|
351
|
+
|
352
|
+
if 'scaling' in data:
|
353
|
+
scaling = eval(data.pop('scaling'))
|
354
|
+
else:
|
355
|
+
scaling = None
|
356
|
+
|
357
|
+
# Update options if provided (to enable overriding options from screening setup)
|
358
|
+
if 'options' in data:
|
359
|
+
options = data['options']
|
360
|
+
else:
|
361
|
+
options = {}
|
362
|
+
|
363
|
+
return cls(U0, direction, scaling=scaling, options=options, **data)
|
wawi/model/_dry.py
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
'''
|
4
|
+
DRY MODES SUBMODULE
|
5
|
+
'''
|
6
|
+
|
7
|
+
class ModalDry:
|
8
|
+
def __init__(self, phi, phi_x=dict(), local_phi=False, k=None, omega_n=None, m=1.0, xi0=0.0, n_modes=None, m_min=0.0):
|
9
|
+
|
10
|
+
self._n_modes = n_modes
|
11
|
+
self.phi_full = {key: np.array(phi[key]) for key in phi}
|
12
|
+
self.local_phi = local_phi # is assumed for all relevant phi
|
13
|
+
self.phi_x = phi_x
|
14
|
+
|
15
|
+
n_none = np.sum([var_i is None for var_i in [m, k, omega_n]])
|
16
|
+
if n_none != 1:
|
17
|
+
raise ValueError('Exactly two of the variables m, k and omega_n has to be input. This is to ensure consistency and sufficient info. Force m=None if onlyk and omega_n are specified')
|
18
|
+
|
19
|
+
# Variations of input of m,k and omega_n
|
20
|
+
if m is not None and omega_n is not None:
|
21
|
+
self._k = np.array(m)*np.array(omega_n)**2
|
22
|
+
self._m = np.array(m)
|
23
|
+
elif k is not None and omega_n is not None:
|
24
|
+
self._k = np.array(k)
|
25
|
+
self._m = np.array(k)/np.array(omega_n)**2
|
26
|
+
else:
|
27
|
+
self._k = np.array(k)
|
28
|
+
self._m = np.array(m)
|
29
|
+
|
30
|
+
self.m_min = m_min
|
31
|
+
self._xi0 = xi0
|
32
|
+
|
33
|
+
if self._m is not None and np.ndim(self._m) == 0:
|
34
|
+
self._m = np.array([self._m]*self.n_modes)
|
35
|
+
|
36
|
+
@property
|
37
|
+
def mode_ix(self):
|
38
|
+
if hasattr(self, 'm_min'):
|
39
|
+
return np.where((self._m>self.m_min)[:self.n_modes])[0]
|
40
|
+
else:
|
41
|
+
return np.arange(0,self.n_modes)
|
42
|
+
|
43
|
+
@property
|
44
|
+
def omega_n(self):
|
45
|
+
return (self.k/self.m)**0.5
|
46
|
+
|
47
|
+
@property
|
48
|
+
def wn(self):
|
49
|
+
return self.omega_n
|
50
|
+
|
51
|
+
@property
|
52
|
+
def omega_d(self):
|
53
|
+
return np.sqrt(1 - self.xi0) * self.omega_n
|
54
|
+
|
55
|
+
@property
|
56
|
+
def wd(self):
|
57
|
+
return self.omega_d
|
58
|
+
|
59
|
+
@property
|
60
|
+
def fn(self):
|
61
|
+
return self.omega_n/2/np.pi
|
62
|
+
|
63
|
+
@property
|
64
|
+
def Tn(self):
|
65
|
+
return 2*np.pi/self.omega_n
|
66
|
+
|
67
|
+
@property
|
68
|
+
def fd(self):
|
69
|
+
return self.omega_d/2/np.pi
|
70
|
+
|
71
|
+
@property
|
72
|
+
def Td(self):
|
73
|
+
return 2*np.pi/self.omega_d
|
74
|
+
|
75
|
+
|
76
|
+
@property
|
77
|
+
def n_modes(self):
|
78
|
+
if self._n_modes is None:
|
79
|
+
return list(self.phi_full.values())[0].shape[1]
|
80
|
+
else:
|
81
|
+
return self._n_modes
|
82
|
+
|
83
|
+
@n_modes.setter
|
84
|
+
def n_modes(self, n):
|
85
|
+
self._n_modes = n
|
86
|
+
|
87
|
+
@property
|
88
|
+
def xi0(self):
|
89
|
+
if self._xi0 is None:
|
90
|
+
return 0.0
|
91
|
+
elif np.ndim(self._xi0) == 0:
|
92
|
+
return np.array([self._xi0]*self.n_modes)[self.mode_ix]
|
93
|
+
else:
|
94
|
+
if len(self._xi0) == len(self._m): #full model
|
95
|
+
return self._xi0[:self.n_modes][self.mode_ix]
|
96
|
+
elif len(self._xi0) == self.n_modes: #truncated
|
97
|
+
return self._xi0[self.mode_ix]
|
98
|
+
elif len(self._xi0) == len(self.mode_ix): #truncated & filtered
|
99
|
+
return self._xi0
|
100
|
+
else:
|
101
|
+
raise ValueError('''Specified xi0 must be scalar or with same length as total number of modes,
|
102
|
+
number of truncated modes (by n_modes), or filtered number of modes (by n_modes and m_min)
|
103
|
+
''')
|
104
|
+
|
105
|
+
@xi0.setter
|
106
|
+
def xi0(self, xi0):
|
107
|
+
self._xi0 = xi0
|
108
|
+
|
109
|
+
@property
|
110
|
+
def m(self):
|
111
|
+
return self._m[self.mode_ix]
|
112
|
+
|
113
|
+
@m.setter
|
114
|
+
def m(self, m):
|
115
|
+
self._m = m
|
116
|
+
|
117
|
+
@property
|
118
|
+
def k(self):
|
119
|
+
return self._k[self.mode_ix]
|
120
|
+
|
121
|
+
@k.setter
|
122
|
+
def k(self, k):
|
123
|
+
self._k = k
|
124
|
+
|
125
|
+
def get_phi(self, key='full', use_n_modes=True):
|
126
|
+
if use_n_modes:
|
127
|
+
return self.phi_full[key][:, self.mode_ix]
|
128
|
+
else:
|
129
|
+
return self.phi_full[key]
|
130
|
+
|
131
|
+
@property
|
132
|
+
def K(self):
|
133
|
+
return np.diag(self.k)
|
134
|
+
|
135
|
+
@property
|
136
|
+
def C(self):
|
137
|
+
return (2*np.sqrt(self.K*self.M)*np.diag(self.xi0))
|
138
|
+
|
139
|
+
@property
|
140
|
+
def M(self):
|
141
|
+
return np.diag(self.m)
|