h2lib 13.1.506__py3-none-win_amd64.whl → 13.1.1701__py3-none-win_amd64.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.
h2lib/HAWC2Lib.dll CHANGED
Binary file
h2lib/_h2lib.py CHANGED
@@ -6,6 +6,28 @@ import sys
6
6
 
7
7
  from multiclass_interface.multiprocess_interface import MultiProcessClassInterface, ProcessClass
8
8
  import numpy as np
9
+ from pathlib import Path
10
+
11
+ _ERROR_CODES = {
12
+ 1: ValueError("WRONG_NUMBER_OF_COLUMNS"),
13
+ 2: ValueError("MAIN_BODY_NOT_FOUND"),
14
+ 3: RuntimeError("NOT_IMPLEMENTED_ERROR"),
15
+ 4: RuntimeError("STRUCTURE_IS_CONFIDENTIAL"),
16
+ 5: IndexError("BODY_DOES_NOT_EXIST"),
17
+ 6: IndexError("ELEMENT_DOES_NOT_EXIST"),
18
+ 7: ValueError("WRONG_NUMBER_OF_BODIES"),
19
+ 100: RuntimeError("STATIC_SOLVER_DID_NOT_CONVERGE"),
20
+ 101: RuntimeError("STATIC_SOLVER_NOT_INITIALIZED"),
21
+ 300: ValueError("TOO_FEW_SECTIONS_IN_C2DEF"),
22
+ 301: ValueError("BEAM_TOO_SHORT"),
23
+ 302: ValueError("DIFFERENT_NSEC"),
24
+ 500: ValueError("RELATIVE_ROTATION_NOT_FOUND"),
25
+ 700: RuntimeError("SYSTEM_NOT_LINEARIZED"),
26
+ 701: RuntimeError("SYSTEM_EIGENANALYSIS_NOT_DONE"),
27
+ 702: ValueError("TOO_MANY_MODES_REQUESTED"),
28
+ 703: ValueError("WRONG_TOTAL_DOF"),
29
+ 704: ValueError("WRONG_REDUCED_DOF")
30
+ }
9
31
 
10
32
 
11
33
  def H2Lib(suppress_output=False, subprocess=True):
@@ -26,9 +48,9 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
26
48
  # doubles the speed of single instances and 2N of N instances on linux
27
49
  os.environ['MKL_THREADING_LAYER'] = 'sequential'
28
50
  filename = os.path.abspath(filename)
29
- library_bin = os.path.abspath(os.path.join(os.path.dirname(sys.executable), '../Library/bin'))
30
- if os.path.isdir(library_bin): # pragma: no cover
31
- os.add_dll_directory(library_bin)
51
+ for f in [sys.base_prefix, sys.prefix]:
52
+ if os.path.isdir(os.path.join(f, 'Library/bin')):
53
+ os.add_dll_directory(os.path.join(f, 'Library/bin'))
32
54
  DLLWrapper.__init__(self, filename, cwd=cwd, cdecl=True)
33
55
  self.suppress_output = suppress_output
34
56
  self._initialized = False
@@ -66,6 +88,23 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
66
88
  s = " " * 255
67
89
  return H2LibSignatures.get_version(self, s)[0][0].strip()
68
90
 
91
+ def stop_on_error(self, flag):
92
+ """
93
+ Control if HAWC2 will terminate the execution upon encountering an error. The default HAWC2 behavior is to stop.
94
+
95
+ Parameters
96
+ ----------
97
+ flag : bool
98
+ If set to `True` an error will cause HAWC2 to terminate the execution with status code 1.
99
+ If set to `False` HAWC2 will still print a log message but not stop.
100
+
101
+ Returns
102
+ -------
103
+ None.
104
+
105
+ """
106
+ H2LibSignatures.stop_on_error(self, bool(flag))
107
+
69
108
  def get_wind_speed(self, pos_g):
70
109
  return self.get_lib_function('get_wind_speed')(np.asarray(pos_g, dtype=np.float64),
71
110
  np.asarray([0, 0, 0], dtype=np.float64))[0][1]
@@ -98,6 +137,134 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
98
137
  self.time = np.round(H2LibSignatures.run(self, np.float64(time), restype=np.float64)[1], 6)
99
138
  return self.time
100
139
 
140
+ def linearize(self):
141
+ """
142
+ Linearize the system, as done by the system eigen-analysis.
143
+
144
+ Returns
145
+ -------
146
+ n_tdofs : int
147
+ Total number of degrees of freedom in the system.
148
+ n_rdofs : int
149
+ Number of degrees of freedom in the reduced order system.
150
+
151
+ """
152
+ n_tdofs = -1
153
+ n_rdofs = -1
154
+ res = H2LibSignatures.linearize(self, n_tdofs, n_rdofs)[0]
155
+ return res
156
+
157
+ def do_system_eigenanalysis(self, n_modes, include_damping=True):
158
+ """
159
+ Do the system eigen-analysis.
160
+
161
+ Parameters
162
+ ----------
163
+ n_modes : int
164
+ Number of modes to output.
165
+ include_damping : bool, optional
166
+ `True` to include damping, `False` otherwise. The default is `True`.
167
+
168
+ Raises
169
+ ------
170
+ RuntimeError
171
+ Call `linearize` first.
172
+ ValueError
173
+ If too many modes are requested.
174
+
175
+ Returns
176
+ -------
177
+ natural_frequencies : (n_modes,) ndarray
178
+ Natural frequencies, in Hz.
179
+ damping_ratios : (n_modes,) ndarray
180
+ Damping ratios (nondimensional).
181
+ Only returned if `include_damping=True`.
182
+
183
+ """
184
+ natural_frequencies = np.zeros((n_modes,), dtype=np.float64, order="F")
185
+ damping_ratios = np.zeros((n_modes,), dtype=np.float64, order="F")
186
+ error_code = -1
187
+
188
+ _, _, natural_frequencies, damping_ratios, error_code = (
189
+ H2LibSignatures.do_system_eigenanalysis(
190
+ self,
191
+ include_damping,
192
+ n_modes,
193
+ natural_frequencies,
194
+ damping_ratios,
195
+ error_code,
196
+ )[0]
197
+ )
198
+
199
+ if error_code > 0:
200
+ raise _ERROR_CODES[error_code]
201
+
202
+ if include_damping:
203
+ return natural_frequencies, damping_ratios
204
+ else:
205
+ return natural_frequencies
206
+
207
+ def get_system_eigenvalues_and_eigenvectors(
208
+ self, n_modes, n_rdofs, include_damping=True
209
+ ):
210
+ """
211
+ Get the system eigenvalues and eigenvectors from system_eigenanalysis.
212
+
213
+ This function must be called after `do_system_eigenanalysis`, with the same value of `include_damping`.
214
+
215
+ Parameters
216
+ ----------
217
+ n_modes : int
218
+ Number of modes to output.
219
+ n_rdofs : int
220
+ Number of degrees of freedom in the reduced order system.
221
+ As returned by `linearize`.
222
+ include_damping : bool, optional
223
+ `True` to include damping, `False` otherwise. The default is `True`.
224
+
225
+ Raises
226
+ ------
227
+ RuntimeError
228
+ If the structure is confidential or if `linearize` and `do_system_eigenanalysis` have not been called first.
229
+ ValueError
230
+ Either `n_modes` or `n_rdofs` is wrong.
231
+
232
+ Returns
233
+ -------
234
+ eigenvalues : (n_modes,) ndarray
235
+ Eigenvalues. Real array without damping and complex array otherwise.
236
+ eigenvectors : (n_modes, ny)
237
+ Eigenvectors. Real array without damping and complex array otherwise.
238
+ Only returned if the structure is not confidential.
239
+
240
+ """
241
+ if include_damping:
242
+ ny = 2 * n_rdofs
243
+ dtype = np.complex128
244
+ f = H2LibSignatures.get_system_eigval_eigvec_with_damping
245
+ else:
246
+ ny = n_rdofs
247
+ dtype = np.float64
248
+ f = H2LibSignatures.get_system_eigval_eigvec_without_damping
249
+
250
+ error_code = -1
251
+ eigenvalues = np.zeros((n_modes,), dtype=dtype, order="F")
252
+ eigenvectors = np.zeros((n_modes, ny), dtype=dtype, order="F")
253
+
254
+ _, _, eigenvalues, eigenvectors, error_code = f(
255
+ self, n_modes, ny, eigenvalues, eigenvectors, error_code
256
+ )[0]
257
+
258
+ if error_code > 0:
259
+ if error_code == 4:
260
+ # The structure is confidential.
261
+ # The eigenvectors have not been returned by the Fortran code.
262
+ return eigenvalues
263
+ else:
264
+ raise _ERROR_CODES[error_code]
265
+
266
+ return eigenvalues, eigenvectors
267
+
101
268
  def solver_static_init(self):
102
269
  """
103
270
  Initialize the static solver.
@@ -126,9 +293,7 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
126
293
  error_code = -1
127
294
  error_code = H2LibSignatures.solver_static_update(self, error_code)[0][0]
128
295
  if error_code > 0:
129
- if error_code == 101:
130
- raise RuntimeError("STATIC_SOLVER_NOT_INITIALIZED")
131
- raise RuntimeError(error_code) # pragma: no cover
296
+ raise _ERROR_CODES[error_code]
132
297
 
133
298
  def solver_static_solve(self):
134
299
  """
@@ -147,9 +312,7 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
147
312
  error_code = -1
148
313
  error_code = H2LibSignatures.solver_static_solve(self, error_code)[0][0]
149
314
  if error_code > 0:
150
- if error_code == 101:
151
- raise RuntimeError("STATIC_SOLVER_NOT_INITIALIZED")
152
- raise RuntimeError(error_code) # pragma: no cover
315
+ raise _ERROR_CODES[error_code]
153
316
 
154
317
  def solver_static_delete(self):
155
318
  """
@@ -179,9 +342,7 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
179
342
  error_code = -1
180
343
  error_code = H2LibSignatures.solver_static_run(self, error_code)[0][0]
181
344
  if error_code > 0:
182
- if error_code == 100:
183
- raise RuntimeError("STATIC_SOLVER_DID_NOT_CONVERGE")
184
- raise RuntimeError(error_code) # pragma: no cover
345
+ raise _ERROR_CODES[error_code]
185
346
 
186
347
  def add_sensor(self, sensor_line):
187
348
  """Add sensor to hawc2. The sensor will be accessible from h2lib but will not show up in the output file of HAWC2
@@ -231,6 +392,167 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
231
392
  def set_variable_sensor_value(self, id, value):
232
393
  return H2LibSignatures.set_variable_sensor_value(self, id, np.float64(value))
233
394
 
395
+ def set_orientation_base(
396
+ self,
397
+ main_body,
398
+ mbdy_eulerang_table=None,
399
+ angles_in_deg=True,
400
+ reset_orientation=False,
401
+ mbdy_ini_rotvec_d1=None,
402
+ ):
403
+ """
404
+ Set an `orientation` / `base` command.
405
+
406
+ Function equivalent to the HAWC2 command `orientation` / `base`.
407
+ For further details see the HAWC2 documentation.
408
+ We assume that this base orientation is already present in the htc file,
409
+ and therefore modify it here instead of creating a new one.
410
+
411
+ Parameters
412
+ ----------
413
+ main_body : str
414
+ Main body name. Same as HAWC2 `mbdy` parameter.
415
+ mbdy_eulerang_table : (:, 3) ndarray, optional
416
+ A sequence of Euler angles, one per row.
417
+ Equivalent to HAWC2 command `mbdy_eulerang`.
418
+ A 1D array with 3 elements will be interpreted as 1 row.
419
+ This table is additive with respect to the orientation / base command in the htc file,
420
+ unless the flag `reset_orientation` is used.
421
+ The default is `[0, 0, 0]`, which means that no rotation is applied.
422
+ angles_in_deg : bool, optional
423
+ `True` if the angles in `mbdy_eulerang_table` are provided in degrees.
424
+ `False` if they are in radians.
425
+ The default is `True`.
426
+ reset_orientation : bool, optional,
427
+ If `True` this function will reset the orientation to the global frame
428
+ before applying `mbdy_eulerang_table`. The default is `False`.
429
+ mbdy_ini_rotvec_d1 : (4) ndarray, optional
430
+ Angular velocity. First 3 elements for the direction and last for the magnitude.
431
+ Equivalent to HAWC2 command `mbdy_ini_rotvec_d1`.
432
+ The default is 0 speed.
433
+
434
+ Raises
435
+ ------
436
+ ValueError
437
+ If the `orientation` / `base` command cannot be found.
438
+
439
+ Returns
440
+ -------
441
+ None.
442
+
443
+ """
444
+ if mbdy_eulerang_table is None:
445
+ mbdy_eulerang_table = np.zeros((1, 3), dtype=np.float64, order="F")
446
+ if mbdy_ini_rotvec_d1 is None:
447
+ mbdy_ini_rotvec_d1 = np.zeros((4,), dtype=np.float64, order="F")
448
+ # 1D arrays are converted to 2D with 1 row.
449
+ mbdy_eulerang_table = np.atleast_2d(mbdy_eulerang_table)
450
+ error_code = -1
451
+ error_code = H2LibSignatures.set_orientation_base(
452
+ self,
453
+ main_body,
454
+ mbdy_eulerang_table.shape[0],
455
+ np.asfortranarray(mbdy_eulerang_table.astype(np.float64)),
456
+ angles_in_deg,
457
+ reset_orientation,
458
+ np.asfortranarray(mbdy_ini_rotvec_d1.astype(np.float64)),
459
+ error_code,
460
+ )[0][-1]
461
+
462
+ if error_code > 0:
463
+ raise _ERROR_CODES[error_code]
464
+
465
+ def set_orientation_relative(
466
+ self,
467
+ main_body_1,
468
+ node_1,
469
+ main_body_2,
470
+ node_2,
471
+ mbdy2_eulerang_table=None,
472
+ angles_in_deg=True,
473
+ reset_orientation=False,
474
+ mbdy2_ini_rotvec_d1=None,
475
+ ):
476
+ """
477
+ Set an `orientation` / `relative` command.
478
+
479
+ Function equivalent to the HAWC2 command `orientation` / `relative`.
480
+ For further details see the HAWC2 documentation.
481
+ We assume that this relative orientation is already present in the htc file,
482
+ and therefore modify it here instead of creating a new one.
483
+
484
+ Parameters
485
+ ----------
486
+ main_body_1 : str
487
+ Main body name to which the next main body is attached.
488
+ node_1 : int, str
489
+ Node number of `main_body_1` that is used for connection, starting from 0.
490
+ `"last"` can be specified which ensures that the last node on the main_body
491
+ is used, and `-1` refers to the origin of the main body coordinate system.
492
+ main_body_2 : str
493
+ Main_body name of the `main_body` that is positioned
494
+ in space by the relative command.
495
+ node_2 : int, str
496
+ Node number of `main_body_2` that is used for connection, starting from 0.
497
+ `"last"` can be specified which ensures that the last node on the main_body
498
+ is used, and `-1` refers to the origin of the main body coordinate system.
499
+ mbdy2_eulerang_table : : (:, 3) ndarray, optional
500
+ A sequence of Euler angles, one per row.
501
+ Equivalent to HAWC2 command `mbdy2_eulerang`.
502
+ A 1D array with 3 elements will be interpreted as 1 row.
503
+ This table is additive with respect to the orientation / relative command in the htc file,
504
+ unless the flag `reset_orientation` is used.
505
+ The default is `[0, 0, 0]`, which means that no rotation is applied.
506
+ angles_in_deg : bool, optional
507
+ `True` if the angles in `mbdy2_eulerang_table` are provided in degrees.
508
+ `False` if they are in radians.
509
+ The default is `True`.
510
+ reset_orientation : bool, optional,
511
+ If `True` this function will reset the orientation to no rotation
512
+ before applying `mbdy2_eulerang_table`. The default is `False`.
513
+ mbdy2_ini_rotvec_d1 : (4) ndarray, optional
514
+ Angular velocity. First 3 elements for the direction and last for the magnitude.
515
+ Equivalent to HAWC2 command `mbdy2_ini_rotvec_d1`.
516
+ The default is 0 speed.
517
+
518
+ Raises
519
+ ------
520
+ ValueError
521
+ If the `orientation` / `relative` command cannot be found,
522
+ or if the main bodies do not exist.
523
+
524
+ Returns
525
+ -------
526
+ None.
527
+
528
+ """
529
+ if mbdy2_eulerang_table is None:
530
+ mbdy2_eulerang_table = np.zeros((1, 3), dtype=np.float64, order="F")
531
+ if mbdy2_ini_rotvec_d1 is None:
532
+ mbdy2_ini_rotvec_d1 = np.zeros((4,), dtype=np.float64, order="F")
533
+ # 1D arrays are converted to 2D with 1 row.
534
+ mbdy2_eulerang_table = np.atleast_2d(mbdy2_eulerang_table)
535
+ # Convert node_1 and 2 to int.
536
+ if node_1 == "last":
537
+ node_1 = -2
538
+ if node_2 == "last":
539
+ node_2 = -2
540
+ error_code = -1
541
+ error_code = H2LibSignatures.set_orientation_relative(
542
+ self,
543
+ main_body_1,
544
+ node_1 + 1,
545
+ main_body_2,
546
+ node_2 + 1,
547
+ mbdy2_eulerang_table.shape[0],
548
+ np.asfortranarray(mbdy2_eulerang_table.astype(np.float64)),
549
+ bool(angles_in_deg),
550
+ reset_orientation,
551
+ np.asfortranarray(mbdy2_ini_rotvec_d1.astype(np.float64)),
552
+ error_code)[0][-1]
553
+ if error_code > 0:
554
+ raise _ERROR_CODES[error_code]
555
+
234
556
  def init_windfield(self, Nxyz, dxyz, box_offset_yz, transport_speed):
235
557
  """Initialize wind field which afterwards can be set using set_windfield
236
558
 
@@ -325,10 +647,12 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
325
647
  return self._aero_sections_data_shape[rotor]
326
648
 
327
649
  def get_aerosections_position(self, rotor=0):
650
+ """Global xyz position of aero sections. Shape=(#blades, #sections, 3)"""
328
651
  position = np.zeros(self.aero_sections_data_shape(rotor), dtype=np.float64, order='F')
329
652
  return H2LibSignatures.get_aerosections_position(self, rotor + 1, position)[0][1]
330
653
 
331
654
  def set_aerosections_windspeed(self, uvw, rotor=0):
655
+ """Update wind speed at aero sections. uvw shape=(#blades, #sections, 3)"""
332
656
  return H2LibSignatures.set_aerosections_windspeed(self, rotor + 1, np.asarray(uvw, np.float64))
333
657
 
334
658
  def get_aerosections_forces(self, rotor=0):
@@ -417,9 +741,7 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
417
741
  0
418
742
  ]
419
743
  if error_code > 0:
420
- if error_code == 4:
421
- raise RuntimeError("STRUCTURE_IS_CONFIDENTIAL")
422
- raise RuntimeError(error_code) # pragma: no cover
744
+ raise _ERROR_CODES[error_code]
423
745
  return nbdy, ncst
424
746
 
425
747
  def get_number_of_elements(self):
@@ -445,15 +767,9 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
445
767
  self, nbdy, nelem, error_code
446
768
  )[0]
447
769
 
448
- if error_code > 0:
449
- if error_code == 4: # pragma: no cover
450
- # This cannot happen because if the structure is confidential we will
451
- # get this error from get_number_of_bodies_and_constraints().
452
- raise RuntimeError("STRUCTURE_IS_CONFIDENTIAL") # pragma: no cover
453
- elif error_code == 7: # pragma: no cover
454
- # This cannot happen because we call get_number_of_bodies_and_constraints().
455
- raise ValueError("WRONG_NUMBER_OF_BODIES") # pragma: no cover
456
- raise RuntimeError(error_code) # pragma: no cover
770
+ if error_code > 0: # pragma: no cover
771
+ # This cannot happen because exceptions are raised by get_number_of_bodies_and_constraints().
772
+ raise _ERROR_CODES[error_code] # pragma: no cover
457
773
  return nelem
458
774
 
459
775
  def get_timoshenko_location(self, ibdy, ielem):
@@ -506,14 +822,7 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
506
822
  0
507
823
  ]
508
824
  if error_code > 0:
509
- if error_code == 4:
510
- raise RuntimeError("STRUCTURE_IS_CONFIDENTIAL")
511
- elif error_code == 5:
512
- raise IndexError("BODY_DOES_NOT_EXIST")
513
- elif error_code == 6:
514
- raise IndexError("ELEMENT_DOES_NOT_EXIST")
515
- raise RuntimeError(error_code) # pragma: no cover
516
-
825
+ raise _ERROR_CODES[error_code]
517
826
  return l, r1, r12, tes
518
827
 
519
828
  def get_body_rotation_tensor(self, ibdy):
@@ -544,14 +853,55 @@ class H2LibThread(H2LibSignatures, DLLWrapper):
544
853
  self, ibdy + 1, amat, error_code
545
854
  )[0]
546
855
  if error_code > 0:
547
- if error_code == 4:
548
- raise RuntimeError("STRUCTURE_IS_CONFIDENTIAL")
549
- elif error_code == 5:
550
- raise IndexError("BODY_DOES_NOT_EXIST")
551
- raise RuntimeError(error_code) # pragma: no cover
552
-
856
+ raise _ERROR_CODES[error_code]
553
857
  return amat
554
858
 
859
+ def get_system_matrices(self, n_tdofs, n_rdofs):
860
+ """
861
+ Get the system structural matrices computed during the system_eigenanalysis.
862
+
863
+ This function must be called after `linearize()`.
864
+
865
+ Parameters
866
+ ----------
867
+ n_tdofs : int
868
+ Total number of degrees of freedom in the system.
869
+ n_rdofs : int
870
+ Number of degrees of freedom in the reduced order system.
871
+
872
+ Raises
873
+ ------
874
+ RuntimeError
875
+ If the linearizetion has not been done or the structure is confidential.
876
+ ValueError
877
+ If the total or reduced number of degrees of freedom is wrong.
878
+
879
+ Returns
880
+ -------
881
+ M : (n_rdofs, n_rdofs) ndarray
882
+ Mass matrix.
883
+ C : (n_rdofs, n_rdofs) ndarray
884
+ Damping matrix.
885
+ K : (n_rdofs, n_rdofs) ndarray
886
+ Stiffness matrix.
887
+ R : (n_tdofs, n_rdofs) ndarray
888
+ Transformation between reduced and all DOFs.
889
+
890
+ """
891
+ M = np.zeros((n_rdofs, n_rdofs), dtype=np.float64, order="F")
892
+ C = np.zeros((n_rdofs, n_rdofs), dtype=np.float64, order="F")
893
+ K = np.zeros((n_rdofs, n_rdofs), dtype=np.float64, order="F")
894
+ R = np.zeros((n_tdofs, n_rdofs), dtype=np.float64, order="F")
895
+ error_code = -1
896
+
897
+ _, _, M, C, K, R, error_code = H2LibSignatures.get_system_matrices(
898
+ self, n_tdofs, n_rdofs, M, C, K, R, error_code
899
+ )[0]
900
+
901
+ if error_code > 0:
902
+ raise _ERROR_CODES[error_code]
903
+ return M, C, K, R
904
+
555
905
 
556
906
  @contextmanager
557
907
  def set_LD_LIBRARY_PATH():
h2lib/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # This file is autogenerated and should not be modified manually
2
- __version__ = '13.1.506'
3
- h2lib_version = '13.1.506'
4
- hawc2_version = '13.1.8'
2
+ __version__ = '13.1.1701'
3
+ h2lib_version = '13.1.1701'
4
+ hawc2_version = '13.1.17'
h2lib/dll_wrapper.py CHANGED
@@ -6,7 +6,7 @@ import platform
6
6
  import os
7
7
  import ctypes
8
8
  from _ctypes import POINTER
9
- from ctypes import c_int, c_double, c_char, c_char_p, c_long, c_longlong
9
+ from ctypes import c_int, c_double, c_char, c_char_p, c_long, c_longlong, Structure
10
10
  from contextlib import contextmanager
11
11
  import tempfile
12
12
  try:
@@ -22,6 +22,24 @@ c_long_p = POINTER(ctypes.c_longlong)
22
22
  c_double_p = POINTER(ctypes.c_double)
23
23
  c_float_p = POINTER(ctypes.c_float)
24
24
 
25
+
26
+ # Add support for complex numbers to ctypes.
27
+ # This solution is copied from: https://stackoverflow.com/a/65743183/3676517
28
+ class c_double_complex(Structure):
29
+ """complex is a c structure
30
+ https://docs.python.org/3/library/ctypes.html#module-ctypes suggests
31
+ to use ctypes.Structure to pass structures (and, therefore, complex)
32
+ """
33
+
34
+ _fields_ = [("real", c_double), ("imag", c_double)]
35
+
36
+ @property
37
+ def value(self):
38
+ return self.real + 1j * self.imag # fields declared above
39
+
40
+
41
+ c_double_complex_p = POINTER(c_double_complex)
42
+
25
43
  in_use = []
26
44
 
27
45
 
@@ -109,6 +127,8 @@ def wrap(self, f, *args, **kwargs):
109
127
  c_args.append(arg.ctypes.data_as(c_double_p))
110
128
  elif arg.dtype == np.float32:
111
129
  c_args.append(arg.ctypes.data_as(c_float_p))
130
+ elif arg.dtype == np.complex128:
131
+ c_args.append(arg.ctypes.data_as(c_double_complex_p))
112
132
  else:
113
133
  raise NotImplementedError(arg.dtype)
114
134
 
h2lib/h2lib_signatures.py CHANGED
@@ -11,6 +11,16 @@ class H2LibSignatures():
11
11
  end subroutine'''
12
12
  return self.get_lib_function('add_sensor')(sensor_line, index_start, index_stop)
13
13
 
14
+ def do_system_eigenanalysis(self, include_damping, n_modes, natural_frequencies, damping_ratios, error_code):
15
+ '''subroutine do_system_eigenanalysis(include_damping, n_modes, natural_frequencies, damping_ratios, error_code) &
16
+ logical(kind=c_bool), intent(in) :: include_damping
17
+ integer(kind=4), intent(in) :: n_modes
18
+ real(kind=c_double), dimension(n_modes), intent(out) :: natural_frequencies
19
+ real(kind=c_double), dimension(n_modes), intent(out) :: damping_ratios
20
+ integer(kind=8), intent(out) :: error_code
21
+ end subroutine'''
22
+ return self.get_lib_function('do_system_eigenanalysis')(include_damping, n_modes, natural_frequencies, damping_ratios, error_code)
23
+
14
24
  def echo_version(self, ):
15
25
  '''subroutine echo_version() BIND(C, NAME='echo_version')
16
26
  !DEC$ ATTRIBUTES DLLEXPORT :: echo_version
@@ -218,6 +228,38 @@ end subroutine'''
218
228
  end subroutine'''
219
229
  return self.get_lib_function('get_sensor_values')(ids, values, n)
220
230
 
231
+ def get_system_eigval_eigvec_with_damping(self, n_modes, ny, eigenvalues, eigenvectors, error_code):
232
+ '''subroutine get_system_eigval_eigvec_with_damping(n_modes, ny, eigenvalues, eigenvectors, error_code) &
233
+ integer(kind=4), intent(in) :: n_modes
234
+ integer(kind=4), intent(in) :: ny
235
+ complex(kind=c_double_complex), dimension(n_modes), intent(out) :: eigenvalues
236
+ complex(kind=c_double_complex), dimension(ny, n_modes), intent(out) :: eigenvectors
237
+ integer(kind=8), intent(out) :: error_code
238
+ end subroutine'''
239
+ return self.get_lib_function('get_system_eigval_eigvec_with_damping')(n_modes, ny, eigenvalues, eigenvectors, error_code)
240
+
241
+ def get_system_eigval_eigvec_without_damping(self, n_modes, ny, eigenvalues, eigenvectors, error_code):
242
+ '''subroutine get_system_eigval_eigvec_without_damping(n_modes, ny, eigenvalues, eigenvectors, error_code) &
243
+ integer(kind=4), intent(in) :: n_modes
244
+ integer(kind=4), intent(in) :: ny
245
+ real(kind=c_double), dimension(n_modes), intent(out) :: eigenvalues
246
+ real(kind=c_double), dimension(ny, n_modes), intent(out) :: eigenvectors
247
+ integer(kind=8), intent(out) :: error_code
248
+ end subroutine'''
249
+ return self.get_lib_function('get_system_eigval_eigvec_without_damping')(n_modes, ny, eigenvalues, eigenvectors, error_code)
250
+
251
+ def get_system_matrices(self, n_tdofs, n_rdofs, M, C, K, R, error_code):
252
+ '''subroutine get_system_matrices(n_tdofs, n_rdofs, M, C, K, R, error_code) &
253
+ integer(kind=4), intent(in) :: n_tdofs
254
+ integer(kind=4), intent(in) :: n_rdofs
255
+ real(kind=c_double), dimension(n_rdofs, n_rdofs), intent(out) :: M
256
+ real(kind=c_double), dimension(n_rdofs, n_rdofs), intent(out) :: C
257
+ real(kind=c_double), dimension(n_rdofs, n_rdofs), intent(out) :: K
258
+ real(kind=c_double), dimension(n_tdofs, n_rdofs), intent(out) :: R
259
+ integer(kind=8), intent(out) :: error_code
260
+ end subroutine'''
261
+ return self.get_lib_function('get_system_matrices')(n_tdofs, n_rdofs, M, C, K, R, error_code)
262
+
221
263
  def get_time(self, time):
222
264
  '''subroutine
223
265
  subroutine'''
@@ -254,6 +296,13 @@ end subroutine'''
254
296
  end subroutine'''
255
297
  return self.get_lib_function('init_windfield')(Nxyz, dxyz, box_offset_yz, transport_speed)
256
298
 
299
+ def linearize(self, n_tdofs, n_rdofs):
300
+ '''subroutine linearize(n_tdofs, n_rdofs) bind(C, name="linearize")
301
+ integer(kind=8), intent(out) :: n_tdofs
302
+ integer(kind=8), intent(out) :: n_rdofs
303
+ end subroutine'''
304
+ return self.get_lib_function('linearize')(n_tdofs, n_rdofs)
305
+
257
306
  def loop(self, N, restype):
258
307
  '''function loop(N) bind(C, Name='loop')
259
308
  !DEC$ ATTRIBUTES DLLEXPORT :: loop
@@ -306,6 +355,45 @@ end subroutine'''
306
355
  end subroutine'''
307
356
  return self.get_lib_function('set_aerosections_windspeed')(rotor, uvw)
308
357
 
358
+ def set_orientation_base(self, main_body_name,
359
+ n_rows, mbdy_eulerang_table, angles_in_deg, reset_orientation, mbdy_ini_rotvec_d1,
360
+ error_code):
361
+ '''subroutine set_orientation_base(main_body_name, &
362
+ character(kind=c_char, len=1), dimension(256), intent(in) :: main_body_name
363
+ real(kind=c_double), dimension(n_rows, 3), intent(in) :: mbdy_eulerang_table
364
+ logical, intent(in) :: angles_in_deg
365
+ logical, intent(in) :: reset_orientation
366
+ real(kind=c_double), dimension(4), intent(in) :: mbdy_ini_rotvec_d1
367
+ end subroutine'''
368
+ return self.get_lib_function('set_orientation_base')(main_body_name,
369
+ n_rows, mbdy_eulerang_table, angles_in_deg, reset_orientation, mbdy_ini_rotvec_d1,
370
+ error_code)
371
+
372
+ def set_orientation_relative(self, main_body_1_name, node_1, main_body_2_name, node_2,
373
+ n_rows, mbdy2_eulerang_table, angles_in_deg,
374
+ reset_orientation,
375
+ mbdy2_ini_rotvec_d1,
376
+ error_code):
377
+ '''subroutine set_orientation_relative(main_body_1_name, node_1, main_body_2_name, node_2, &
378
+ character(kind=c_char, len=1), dimension(256), intent(in ) :: main_body_1_name ! Defined as an array of length 1 characters because of bind.
379
+ character(kind=c_char, len=1), dimension(256), intent(in ) :: main_body_2_name ! Defined as an array of length 1 characters because of bind.
380
+ integer(kind=8), intent(in ) :: node_1
381
+ integer(kind=8), intent(in ) :: node_2
382
+ real(kind=c_double), dimension(n_rows, 3), intent(in ) :: mbdy2_eulerang_table
383
+ logical, intent(in ) :: angles_in_deg
384
+ character(kind=c_char, len=256) :: mbdy_1_name ! Same as main_body_1_name, but as string instead of an array of characters.
385
+ character(kind=c_char, len=256) :: mbdy_2_name ! Same as main_body_2_name, but as string instead of an array of characters.
386
+ type(Tmain_body_input), pointer :: main_body_1 ! The main body pointer associated to main_body_1_name.
387
+ type(Tmain_body_input), pointer :: main_body_2 ! The main body pointer associated to main_body_1_name.
388
+ integer(kind=4) :: node_1_local, node_2_local ! Internal copy of node_1 and node_2.
389
+ real*8, dimension(3) :: eulerang ! Euler angles associated to 1 row of mbdy2_eulerang_table.
390
+ end subroutine'''
391
+ return self.get_lib_function('set_orientation_relative')(main_body_1_name, node_1, main_body_2_name, node_2,
392
+ n_rows, mbdy2_eulerang_table, angles_in_deg,
393
+ reset_orientation,
394
+ mbdy2_ini_rotvec_d1,
395
+ error_code)
396
+
309
397
  def set_variable_sensor_value(self, id, value):
310
398
  '''subroutine set_variable_sensor_value(id, value) bind(C, name="set_variable_sensor_value")
311
399
  integer*8, intent(in) :: id
@@ -363,6 +451,12 @@ end subroutine'''
363
451
  end function'''
364
452
  return self.get_lib_function('step')(restype=restype)
365
453
 
454
+ def stop_on_error(self, flag):
455
+ '''subroutine stop_on_error(flag) bind(C, name="stop_on_error")
456
+ logical, intent(in) :: flag
457
+ end subroutine'''
458
+ return self.get_lib_function('stop_on_error')(flag)
459
+
366
460
  def test_hdf5(self, ):
367
461
  '''subroutine test_hdf5() BIND(C, NAME='test_hdf5')
368
462
  !DEC$ ATTRIBUTES DLLEXPORT :: test_hdf5
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: h2lib
3
- Version: 13.1.506
4
- Summary: Python interface to HAWC2 (13.1.8)
3
+ Version: 13.1.1701
4
+ Summary: Python interface to HAWC2 (13.1.17)
5
5
  Download-URL:
6
6
  Author: Mads M. Pedersen, S.G.Horcas and N.G.Ramos
7
7
  Author-email: mmpe@dtu.dk
@@ -11,11 +11,10 @@ Project-URL: Documentation, https://hawc2.pages.windenergy.dtu.dk/HAWC2Lib/
11
11
  Project-URL: Source, https://gitlab.windenergy.dtu.dk/HAWC2/HAWC2Lib
12
12
  Project-URL: Tracker, https://gitlab.windenergy.dtu.dk/HAWC2/HAWC2Lib/-/issues
13
13
  Requires-Dist: numpy
14
- Requires-Dist: intel-fortran-rt ==2021.3.0
15
- Requires-Dist: mkl ==2021.3.0
16
- Requires-Dist: multiclass-interface >=1.5
17
- Provides-Extra: mpi
18
- Requires-Dist: mpi4py ; extra == 'mpi'
14
+ Requires-Dist: intel-fortran-rt==2021.3.0
15
+ Requires-Dist: mkl==2021.3.0
16
+ Requires-Dist: multiclass_interface>=1.5
19
17
  Provides-Extra: test
20
- Requires-Dist: h2lib-tests ; extra == 'test'
21
-
18
+ Requires-Dist: h2lib_tests; extra == "test"
19
+ Provides-Extra: mpi
20
+ Requires-Dist: mpi4py; extra == "mpi"
@@ -0,0 +1,10 @@
1
+ h2lib/HAWC2Lib.dll,sha256=2f3ye1UAvxStkuQdfZ45KsPuWatlk6OKQIRGFRM-qD4,30801920
2
+ h2lib/__init__.py,sha256=f3fO4I6IEFRM9LaV2O3w9Pioj3GPI8qRl7P5Tg5ONtE,528
3
+ h2lib/_h2lib.py,sha256=oCWMTpkui3KIxuZrQcyecBAv-KBKx0exTf3UC1WTJzc,35053
4
+ h2lib/_version.py,sha256=aoq7oNXs_pDNUhj3g7OPtlrQRPyElXusqp2iqfeyvps,149
5
+ h2lib/dll_wrapper.py,sha256=CfuRfDPEmmfYlEGKUmiXiuMhNiMcf24ripPHgqd8CiE,12761
6
+ h2lib/h2lib_signatures.py,sha256=1CuaSNfaTgnbhqcSaehQGNVHhKpvLFwgs2UucKWVQJE,24289
7
+ h2lib-13.1.1701.dist-info/METADATA,sha256=LiH31EtB7-lMnREX8IcRQ3Sj1V7F5HhJJ8W_yEjiF7s,722
8
+ h2lib-13.1.1701.dist-info/WHEEL,sha256=pWXrJbnZSH-J-PhYmKs2XNn4DHCPNBYq965vsBJBFvA,101
9
+ h2lib-13.1.1701.dist-info/top_level.txt,sha256=y_a-tUqphEZQ_0nsWSMaSb21P8Lsd8hUxUdE9g2Dcbk,6
10
+ h2lib-13.1.1701.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp312-cp312-win_amd64
5
5
 
@@ -1,10 +0,0 @@
1
- h2lib/HAWC2Lib.dll,sha256=_ECzaJeq1hmHTNyfA2oiyBf5qZKkeyepyF4Y8cyKLyc,30728192
2
- h2lib/__init__.py,sha256=f3fO4I6IEFRM9LaV2O3w9Pioj3GPI8qRl7P5Tg5ONtE,528
3
- h2lib/_h2lib.py,sha256=AVZODCr2c-NEosVTeqjZNU206MpnAVpmn_t3PolwjqM,22203
4
- h2lib/_version.py,sha256=rV968fg9Lfv32R9TQD6ussF8dTsA74ZCbW8VTanCuus,146
5
- h2lib/dll_wrapper.py,sha256=RC5_gJ1cwHXVfUaSvmsatyQrvks-aZJFAiXrG4B-FEI,12061
6
- h2lib/h2lib_signatures.py,sha256=mqAqgg93ZEiglibECu8yDk-hy3MU8XDfAeXg-aokLk8,17961
7
- h2lib-13.1.506.dist-info/METADATA,sha256=D3B2YjuywPU_q7nHchHr-2t5b2xOAU471mbaBI1j5UY,727
8
- h2lib-13.1.506.dist-info/WHEEL,sha256=3vidnDuZ-QSnHIxLhNbI1gIM-KgyEcMHuZuv1mWPd_Q,101
9
- h2lib-13.1.506.dist-info/top_level.txt,sha256=y_a-tUqphEZQ_0nsWSMaSb21P8Lsd8hUxUdE9g2Dcbk,6
10
- h2lib-13.1.506.dist-info/RECORD,,