• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sandialabs / sdynpy / 13036987848

29 Jan 2025 05:23PM UTC coverage: 15.675%. Remained the same
13036987848

push

github

dprohe
Bugfix due to breaking change in qtpy/PyQt5

1 of 2 new or added lines in 2 files covered. (50.0%)

1 existing line in 1 file now uncovered.

2606 of 16625 relevant lines covered (15.68%)

0.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.11
/src/sdynpy/fileio/sdynpy_rattlesnake.py
1
# -*- coding: utf-8 -*-
2
"""
3
Load in time data from Rattlesnake runs
4

5
Copyright 2022 National Technology & Engineering Solutions of Sandia,
6
LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
7
Government retains certain rights in this software.
8

9
This program is free software: you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation, either version 3 of the License, or
12
(at your option) any later version.
13

14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
GNU General Public License for more details.
18

19
You should have received a copy of the GNU General Public License
20
along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
"""
22

23
import netCDF4 as nc4
3✔
24
import numpy as np
3✔
25
from ..core.sdynpy_coordinate import (coordinate_array, outer_product,
3✔
26
                                      CoordinateArray, _string_map)
27
from ..core.sdynpy_data import data_array, FunctionTypes
3✔
28
from ..core.sdynpy_system import System
3✔
29
import pandas as pd
3✔
30
import sys
3✔
31
import openpyxl as opxl
3✔
32
import os
3✔
33
import warnings
3✔
34

35

36
def read_rattlesnake_output(file, coordinate_override_column=None, read_only_indices=None,
3✔
37
                            read_variable='time_data'):
38
    """
39
    Reads in a Rattlesnake data file and returns the time history array as well
40
    as the channel table
41

42
    Parameters
43
    ----------
44
    file : str or nc4.Dataset
45
        Path to the file to read in or an already open
46

47
    Returns
48
    -------
49
    data_array : TimeHistoryArray
50
        Time history data in the Rattlesnake output file
51
    channel_table : DataFrame
52
        Pandas Dataframe containing the channel table information
53

54
    """
55
    if isinstance(file, str):
×
56
        ds = nc4.Dataset(file, 'r')
×
57
    elif isinstance(file, nc4.Dataset):
×
58
        ds = file
×
59
    if read_only_indices is None:
×
60
        read_only_indices = slice(None)
×
61
    output_data = np.array(ds[read_variable][...][read_only_indices])
×
62
    abscissa = np.arange(output_data.shape[-1]) / ds.sample_rate
×
63
    if coordinate_override_column is None:
×
64
        nodes = [int(''.join(char for char in node if char in '0123456789'))
×
65
                 for node in ds['channels']['node_number'][...][read_only_indices]]
66
        directions = np.array(ds['channels']['node_direction'][...][read_only_indices], dtype='<U3')
×
67
        coordinates = coordinate_array(nodes, directions)[:, np.newaxis]
×
68
    else:
69
        coordinates = coordinate_array(string_array=ds['channels'][coordinate_override_column][read_only_indices])[
×
70
            :, np.newaxis]
71
    array = {name: np.array(variable[:]) for name, variable in ds['channels'].variables.items()}
×
72
    channel_table = pd.DataFrame(array)
×
73
    comment1 = np.char.add(np.char.add(np.array(ds['channels']['channel_type'][...][read_only_indices], dtype='<U80'),
×
74
                                       np.array(' :: ')),
75
                           np.array(ds['channels']['unit'][...][read_only_indices], dtype='<U80'))
76
    comment2 = np.char.add(np.char.add(np.array(ds['channels']['physical_device'][...][read_only_indices], dtype='<U80'),
×
77
                                       np.array(' :: ')),
78
                           np.array(ds['channels']['physical_channel'][...][read_only_indices], dtype='<U80'))
79
    comment3 = np.char.add(np.char.add(np.array(ds['channels']['feedback_device'][...][read_only_indices], dtype='<U80'),
×
80
                                       np.array(' :: ')),
81
                           np.array(ds['channels']['feedback_channel'][...][read_only_indices], dtype='<U80'))
82
    comment4 = np.array(ds['channels']['comment'][...][read_only_indices], dtype='<U80')
×
83
    comment5 = np.array(ds['channels']['make'][...][read_only_indices], dtype='<U80')
×
84
    for key in ('model', 'serial_number', 'triax_dof'):
×
85
        comment5 = np.char.add(comment5, np.array(' '))
×
86
        comment5 = np.char.add(comment5, np.array(ds['channels'][key][...][read_only_indices], dtype='<U80'))
×
87
    time_data = data_array(FunctionTypes.TIME_RESPONSE,
×
88
                           abscissa,
89
                           output_data,
90
                           coordinates,
91
                           comment1,
92
                           comment2,
93
                           comment3,
94
                           comment4,
95
                           comment5)
96
    if isinstance(file, str):
×
97
        ds.close()
×
98
    return time_data, channel_table
×
99

100

101
def read_system_id_data(file):
3✔
102
    if isinstance(file, str):
×
103
        file = np.load(file)
×
104
    df = file['sysid_frequency_spacing']
×
105
    if np.isnan(file['response_transformation_matrix']):
×
106
        try:
×
107
            response_dofs = coordinate_array(
×
108
                [int(v) for v in file['channel_node_number'][file['response_indices']]],
109
                file['channel_node_direction'][file['response_indices']])
110
        except Exception:
×
111
            response_dofs = coordinate_array(file['response_indices']+1, 0)
×
112
    else:
113
        response_dofs = coordinate_array(np.arange(file['response_transformation_matrix'].shape[0])+1, 0)
×
114
    if np.isnan(file['reference_transformation_matrix']):
×
115
        try:
×
116
            reference_dofs = coordinate_array(
×
117
                [int(v) for v in file['channel_node_number'][file['reference_indices']]],
118
                file['channel_node_direction'][file['reference_indices']])
119
        except Exception:
×
120
            reference_dofs = coordinate_array(file['reference_indices']+1, 0)
×
121
    else:
122
        reference_dofs = coordinate_array(np.arange(file['reference_transformation_matrix'].shape[0])+1, 0)
×
123
    ordinate = np.moveaxis(file['frf_data'], 0, -1)
×
124
    frfs = data_array(FunctionTypes.FREQUENCY_RESPONSE_FUNCTION,
×
125
                      df*np.arange(ordinate.shape[-1]), ordinate,
126
                      outer_product(response_dofs, reference_dofs))
127
    ordinate = np.moveaxis(file['response_cpsd'], 0, -1)
×
128
    response_cpsd = data_array(FunctionTypes.POWER_SPECTRAL_DENSITY,
×
129
                               df*np.arange(ordinate.shape[-1]), ordinate,
130
                               outer_product(response_dofs, response_dofs))
131
    ordinate = np.moveaxis(file['reference_cpsd'], 0, -1)
×
132
    reference_cpsd = data_array(FunctionTypes.POWER_SPECTRAL_DENSITY,
×
133
                                df*np.arange(ordinate.shape[-1]), ordinate,
134
                                outer_product(reference_dofs, reference_dofs))
135
    ordinate = np.moveaxis(file['response_noise_cpsd'], 0, -1)
×
136
    response_noise_cpsd = data_array(FunctionTypes.POWER_SPECTRAL_DENSITY,
×
137
                                     df*np.arange(ordinate.shape[-1]), ordinate,
138
                                     outer_product(response_dofs, response_dofs))
139
    ordinate = np.moveaxis(file['reference_noise_cpsd'], 0, -1)
×
140
    reference_noise_cpsd = data_array(FunctionTypes.POWER_SPECTRAL_DENSITY,
×
141
                                      df*np.arange(ordinate.shape[-1]), ordinate,
142
                                      outer_product(reference_dofs, reference_dofs))
143
    ordinate = np.moveaxis(file['coherence'], 0, -1)
×
144
    coherence = data_array(FunctionTypes.MULTIPLE_COHERENCE,
×
145
                           df*np.arange(ordinate.shape[-1]), ordinate,
146
                           outer_product(response_dofs))
147
    return frfs, response_cpsd, reference_cpsd, response_noise_cpsd, reference_noise_cpsd, coherence
×
148

149

150
def read_random_spectral_data(file, coordinate_override_column=None):
3✔
151
    if isinstance(file, str):
×
152
        ds = nc4.Dataset(file, 'r')
×
153
    elif isinstance(file, nc4.Dataset):
×
154
        ds = file
×
155
    coordinate_override_column = None
×
156

157
    environment = [group for group in ds.groups if not group == 'channels'][0]
×
158

159
    # Get the channels in the group
160
    if coordinate_override_column is None:
×
161
        nodes = [int(''.join(char for char in node if char in '0123456789'))
×
162
                 for node in ds['channels']['node_number']]
163
        directions = np.array(ds['channels']['node_direction'][:], dtype='<U3')
×
164
        coordinates = coordinate_array(nodes, directions)
×
165
    else:
166
        coordinates = coordinate_array(string_array=ds['channels'][coordinate_override_column])
×
167
    drives = ds['channels']['feedback_device'][:] != ''
×
168

169
    # Cull down to just those in the environment
170
    environment_index = np.where(ds['environment_names'][:] == environment)[0][0]
×
171
    environment_channels = ds['environment_active_channels'][:, environment_index].astype(bool)
×
172

173
    drives = drives[environment_channels]
×
174
    coordinates = coordinates[environment_channels]
×
175

176
    control_indices = ds[environment]['control_channel_indices'][:]
×
177

178
    control_coordinates = coordinates[control_indices]
×
179

180
    drive_coordinates = coordinates[drives]
×
181

182
    # Load the spectral data
183
    frequencies = np.array(ds[environment]['specification_frequency_lines'][:])
×
184

185
    spec_cpsd = np.moveaxis(
×
186
        np.array(ds[environment]['specification_cpsd_matrix_real'][:]
187
                 + 1j*ds[environment]['specification_cpsd_matrix_imag'][:]),
188
        0, -1)
189

190
    response_cpsd = np.moveaxis(
×
191
        np.array(ds[environment]['response_cpsd_real'][:]
192
                 + 1j*ds[environment]['response_cpsd_imag'][:]),
193
        0, -1)
194

195
    drive_cpsd = np.moveaxis(
×
196
        np.array(ds[environment]['drive_cpsd_real'][:]
197
                 + 1j*ds[environment]['drive_cpsd_imag'][:]),
198
        0, -1)
199

200
    response_coordinates = outer_product(control_coordinates, control_coordinates)
×
201
    drive_coordinates = outer_product(drive_coordinates, drive_coordinates)
×
202

203
    comment1 = np.char.add(np.char.add(np.array(ds['channels']['channel_type'][:], dtype='<U80'),
×
204
                                       np.array(' :: ')),
205
                           np.array(ds['channels']['unit'][:], dtype='<U80'))
206
    comment2 = np.char.add(np.char.add(np.array(ds['channels']['physical_device'][:], dtype='<U80'),
×
207
                                       np.array(' :: ')),
208
                           np.array(ds['channels']['physical_channel'][:], dtype='<U80'))
209
    comment3 = np.char.add(np.char.add(np.array(ds['channels']['feedback_device'][:], dtype='<U80'),
×
210
                                       np.array(' :: ')),
211
                           np.array(ds['channels']['feedback_channel'][:], dtype='<U80'))
212
    comment4 = np.array(ds['channels']['comment'][:], dtype='<U80')
×
213
    comment5 = np.array(ds['channels']['make'][:], dtype='<U80')
×
214
    for key in ('model', 'serial_number', 'triax_dof'):
×
215
        comment5 = np.char.add(comment5, np.array(' '))
×
216
        comment5 = np.char.add(comment5, np.array(ds['channels'][key][:], dtype='<U80'))
×
217

218
    comment1 = comment1[environment_channels]
×
219
    comment2 = comment2[environment_channels]
×
220
    comment3 = comment3[environment_channels]
×
221
    comment4 = comment4[environment_channels]
×
222
    comment5 = comment5[environment_channels]
×
223

224
    comment1_response = np.empty((response_coordinates.shape[0], response_coordinates.shape[1]), dtype=comment1.dtype)
×
225
    comment2_response = np.empty((response_coordinates.shape[0], response_coordinates.shape[1]), dtype=comment1.dtype)
×
226
    comment3_response = np.empty((response_coordinates.shape[0], response_coordinates.shape[1]), dtype=comment1.dtype)
×
227
    comment4_response = np.empty((response_coordinates.shape[0], response_coordinates.shape[1]), dtype=comment1.dtype)
×
228
    comment5_response = np.empty((response_coordinates.shape[0], response_coordinates.shape[1]), dtype=comment1.dtype)
×
229
    for i, idx in enumerate(control_indices):
×
230
        for j, jdx in enumerate(control_indices):
×
231
            comment1_response[i, j] = comment1[idx] + ' // ' + comment1[jdx]
×
232
            comment2_response[i, j] = comment2[idx] + ' // ' + comment2[jdx]
×
233
            comment3_response[i, j] = comment3[idx] + ' // ' + comment3[jdx]
×
234
            comment4_response[i, j] = comment4[idx] + ' // ' + comment4[jdx]
×
235
            comment5_response[i, j] = comment5[idx] + ' // ' + comment5[jdx]
×
236

237
    comment1_drive = np.empty((drive_coordinates.shape[0], drive_coordinates.shape[1]), dtype=comment1.dtype)
×
238
    comment2_drive = np.empty((drive_coordinates.shape[0], drive_coordinates.shape[1]), dtype=comment1.dtype)
×
239
    comment3_drive = np.empty((drive_coordinates.shape[0], drive_coordinates.shape[1]), dtype=comment1.dtype)
×
240
    comment4_drive = np.empty((drive_coordinates.shape[0], drive_coordinates.shape[1]), dtype=comment1.dtype)
×
241
    comment5_drive = np.empty((drive_coordinates.shape[0], drive_coordinates.shape[1]), dtype=comment1.dtype)
×
242
    drive_indices = np.where(drives)[0]
×
243
    for i, idx in enumerate(drive_indices):
×
244
        for j, jdx in enumerate(drive_indices):
×
245
            comment1_drive[i, j] = comment1[idx] + ' // ' + comment1[jdx]
×
246
            comment2_drive[i, j] = comment2[idx] + ' // ' + comment2[jdx]
×
247
            comment3_drive[i, j] = comment3[idx] + ' // ' + comment3[jdx]
×
248
            comment4_drive[i, j] = comment4[idx] + ' // ' + comment4[jdx]
×
249
            comment5_drive[i, j] = comment5[idx] + ' // ' + comment5[jdx]
×
250

251
    # Save the data to SDynpy objects
252
    response_cpsd = data_array(FunctionTypes.POWER_SPECTRAL_DENSITY,
×
253
                               frequencies, response_cpsd, response_coordinates,
254
                               comment1_response, comment2_response, comment3_response,
255
                               comment4_response, comment5_response)
256
    spec_cpsd = data_array(FunctionTypes.POWER_SPECTRAL_DENSITY,
×
257
                           frequencies, spec_cpsd, response_coordinates,
258
                           comment1_response, comment2_response, comment3_response,
259
                           comment4_response, comment5_response)
260
    drive_cpsd = data_array(FunctionTypes.POWER_SPECTRAL_DENSITY,
×
261
                            frequencies, drive_cpsd, drive_coordinates,
262
                            comment1_drive, comment2_drive, comment3_drive,
263
                            comment4_drive, comment5_drive)
264
    return response_cpsd, spec_cpsd, drive_cpsd
×
265

266

267
def read_modal_data(file, coordinate_override_column=None, read_only_indices=None):
3✔
268
    if isinstance(file, str):
×
269
        ds = nc4.Dataset(file, 'r')
×
270
    elif isinstance(file, nc4.Dataset):
×
271
        ds = file
×
272
    if read_only_indices is None:
×
273
        read_only_indices = slice(None)
×
274
    # Get parameters
275
    num_channels = ds.groups['channels'].variables['physical_device'].size
×
276
    group_key = [g for g in ds.groups if not g == 'channels'][0]
×
277
    group = ds.groups[group_key]
×
278
    sample_rate = ds.sample_rate
×
279
    samples_per_frame = group.samples_per_frame
×
280
    num_averages = group.num_averages
×
281
    # Load in the time data
282
    try:
×
283
        output_data = np.array(ds['time_data'][...][read_only_indices]).reshape(num_channels, num_averages, samples_per_frame).transpose(1, 0, 2)
×
284
    except ValueError:
×
285
        warnings.warn('Number of averages in the time data does not match the number of averages specified in the test settings.  Your test may be incomplete.')
×
286
        output_data = np.array(ds['time_data'][...][read_only_indices]).reshape(num_channels, -1, samples_per_frame).transpose(1, 0, 2)
×
287
    abscissa = np.arange(samples_per_frame) / sample_rate
×
288
    if coordinate_override_column is None:
×
289
        nodes = [int(''.join(char for char in node if char in '0123456789'))
×
290
                 for node in ds['channels']['node_number'][...][read_only_indices]]
291
        directions = np.array(ds['channels']['node_direction'][...][read_only_indices], dtype='<U3')
×
292
        coordinates = coordinate_array(nodes, directions)[:, np.newaxis]
×
293
    else:
294
        coordinates = coordinate_array(string_array=ds['channels'][coordinate_override_column][read_only_indices])[
×
295
            :, np.newaxis]
296
    array = {name: np.array(variable[:]) for name, variable in ds['channels'].variables.items()}
×
297
    channel_table = pd.DataFrame(array)
×
298
    comment1 = np.char.add(np.char.add(np.array(ds['channels']['channel_type'][...][read_only_indices], dtype='<U80'),
×
299
                                       np.array(' :: ')),
300
                           np.array(ds['channels']['unit'][...][read_only_indices], dtype='<U80'))
301
    comment2 = np.char.add(np.char.add(np.array(ds['channels']['physical_device'][...][read_only_indices], dtype='<U80'),
×
302
                                       np.array(' :: ')),
303
                           np.array(ds['channels']['physical_channel'][...][read_only_indices], dtype='<U80'))
304
    comment3 = np.char.add(np.char.add(np.array(ds['channels']['feedback_device'][...][read_only_indices], dtype='<U80'),
×
305
                                       np.array(' :: ')),
306
                           np.array(ds['channels']['feedback_channel'][...][read_only_indices], dtype='<U80'))
307
    comment4 = np.array(ds['channels']['comment'][...][read_only_indices], dtype='<U80')
×
308
    comment5 = np.array(ds['channels']['make'][...][read_only_indices], dtype='<U80')
×
309
    for key in ('model', 'serial_number', 'triax_dof'):
×
310
        comment5 = np.char.add(comment5, np.array(' '))
×
311
        comment5 = np.char.add(comment5, np.array(ds['channels'][key][...][read_only_indices], dtype='<U80'))
×
312
    time_data = data_array(FunctionTypes.TIME_RESPONSE,
×
313
                           abscissa,
314
                           output_data,
315
                           coordinates,
316
                           comment1,
317
                           comment2,
318
                           comment3,
319
                           comment4,
320
                           comment5)
321
    # Response and Reference Indices
322
    kept_indices = np.arange(num_channels)[read_only_indices]
×
323
    reference_indices = np.array(group.variables['reference_channel_indices'][:])
×
324
    response_indices = np.array(group.variables['response_channel_indices'][:])
×
325
    keep_response_indices = np.array([i for i, index in enumerate(response_indices) if index in kept_indices])
×
326
    keep_reference_indices = np.array([i for i, index in enumerate(reference_indices) if index in kept_indices])
×
327
    frequency_lines = np.arange(group.dimensions['fft_lines'].size)*sample_rate/samples_per_frame
×
328
    coherence_data = np.array(group['coherence'][:, keep_response_indices]).T
×
329
    comment1 = np.char.add(np.char.add(np.array(ds['channels']['channel_type'][...][response_indices[keep_response_indices]], dtype='<U80'),
×
330
                                       np.array(' :: ')),
331
                           np.array(ds['channels']['unit'][...][response_indices[keep_response_indices]], dtype='<U80'))
332
    comment2 = np.char.add(np.char.add(np.array(ds['channels']['physical_device'][...][response_indices[keep_response_indices]], dtype='<U80'),
×
333
                                       np.array(' :: ')),
334
                           np.array(ds['channels']['physical_channel'][...][response_indices[keep_response_indices]], dtype='<U80'))
335
    comment3 = np.char.add(np.char.add(np.array(ds['channels']['feedback_device'][...][response_indices[keep_response_indices]], dtype='<U80'),
×
336
                                       np.array(' :: ')),
337
                           np.array(ds['channels']['feedback_channel'][...][response_indices[keep_response_indices]], dtype='<U80'))
338
    comment4 = np.array(ds['channels']['comment'][...][response_indices[keep_response_indices]], dtype='<U80')
×
339
    comment5 = np.array(ds['channels']['make'][...][response_indices[keep_response_indices]], dtype='<U80')
×
340
    for key in ('model', 'serial_number', 'triax_dof'):
×
341
        comment5 = np.char.add(comment5, np.array(' '))
×
342
        comment5 = np.char.add(comment5, np.array(ds['channels'][key][...][response_indices[keep_response_indices]], dtype='<U80'))
×
343
    coherence_data = data_array(FunctionTypes.MULTIPLE_COHERENCE,
×
344
                                frequency_lines,
345
                                coherence_data,
346
                                coordinates[response_indices[keep_response_indices]],
347
                                comment1,
348
                                comment2,
349
                                comment3,
350
                                comment4,
351
                                comment5)
352
    # Frequency Response Functions
353
    frf_data = np.moveaxis(np.array(group['frf_data_real'])[:, keep_response_indices[:, np.newaxis], keep_reference_indices]
×
354
                           + np.array(group['frf_data_imag'])[:, keep_response_indices[:, np.newaxis], keep_reference_indices]*1j, 0, -1)
355
    frf_coordinate = outer_product(coordinates[response_indices[keep_response_indices], 0],
×
356
                                   coordinates[reference_indices[keep_reference_indices], 0])
357
    # print(response_indices[keep_response_indices])
358
    # print(reference_indices[keep_reference_indices])
359
    response_comment1 = np.char.add(np.char.add(np.array(ds['channels']['channel_type'][...][response_indices[keep_response_indices]], dtype='<U80'),
×
360
                                                np.array(' :: ')),
361
                                    np.array(ds['channels']['unit'][...][response_indices[keep_response_indices]], dtype='<U80'))
362
    response_comment2 = np.char.add(np.char.add(np.array(ds['channels']['physical_device'][...][response_indices[keep_response_indices]], dtype='<U80'),
×
363
                                                np.array(' :: ')),
364
                                    np.array(ds['channels']['physical_channel'][...][response_indices[keep_response_indices]], dtype='<U80'))
365
    response_comment3 = np.char.add(np.char.add(np.array(ds['channels']['feedback_device'][...][response_indices[keep_response_indices]], dtype='<U80'),
×
366
                                                np.array(' :: ')),
367
                                    np.array(ds['channels']['feedback_channel'][...][response_indices[keep_response_indices]], dtype='<U80'))
368
    response_comment4 = np.array(ds['channels']['comment'][...][response_indices[keep_response_indices]], dtype='<U80')
×
369
    response_comment5 = np.array(ds['channels']['make'][...][response_indices[keep_response_indices]], dtype='<U80')
×
370
    for key in ('model', 'serial_number', 'triax_dof'):
×
371
        response_comment5 = np.char.add(response_comment5, np.array(' '))
×
372
        response_comment5 = np.char.add(response_comment5, np.array(ds['channels'][key][...][response_indices[keep_response_indices]], dtype='<U80'))
×
373
    reference_comment1 = np.char.add(np.char.add(np.array(ds['channels']['channel_type'][...][reference_indices[keep_reference_indices]], dtype='<U80'),
×
374
                                                 np.array(' :: ')),
375
                                     np.array(ds['channels']['unit'][...][reference_indices[keep_reference_indices]], dtype='<U80'))
376
    reference_comment2 = np.char.add(np.char.add(np.array(ds['channels']['physical_device'][...][reference_indices[keep_reference_indices]], dtype='<U80'),
×
377
                                                 np.array(' :: ')),
378
                                     np.array(ds['channels']['physical_channel'][...][reference_indices[keep_reference_indices]], dtype='<U80'))
379
    reference_comment3 = np.char.add(np.char.add(np.array(ds['channels']['feedback_device'][...][reference_indices[keep_reference_indices]], dtype='<U80'),
×
380
                                                 np.array(' :: ')),
381
                                     np.array(ds['channels']['feedback_channel'][...][reference_indices[keep_reference_indices]], dtype='<U80'))
382
    reference_comment4 = np.array(ds['channels']['comment'][...][reference_indices[keep_reference_indices]], dtype='<U80')
×
383
    reference_comment5 = np.array(ds['channels']['make'][...][reference_indices[keep_reference_indices]], dtype='<U80')
×
384
    for key in ('model', 'serial_number', 'triax_dof'):
×
385
        reference_comment5 = np.char.add(reference_comment5, np.array(' '))
×
386
        reference_comment5 = np.char.add(reference_comment5, np.array(ds['channels'][key][...][reference_indices[keep_reference_indices]], dtype='<U80'))
×
387
    response_comment1, reference_comment1 = np.broadcast_arrays(response_comment1[:, np.newaxis], reference_comment1)
×
388
    comment1 = np.char.add(np.char.add(response_comment1, np.array(' / ')), reference_comment1)
×
389
    response_comment2, reference_comment2 = np.broadcast_arrays(response_comment2[:, np.newaxis], reference_comment2)
×
390
    comment2 = np.char.add(np.char.add(response_comment2, np.array(' / ')), reference_comment2)
×
391
    response_comment3, reference_comment3 = np.broadcast_arrays(response_comment3[:, np.newaxis], reference_comment3)
×
392
    comment3 = np.char.add(np.char.add(response_comment3, np.array(' / ')), reference_comment3)
×
393
    response_comment4, reference_comment4 = np.broadcast_arrays(response_comment4[:, np.newaxis], reference_comment4)
×
394
    comment4 = np.char.add(np.char.add(response_comment4, np.array(' / ')), reference_comment4)
×
395
    response_comment5, reference_comment5 = np.broadcast_arrays(response_comment5[:, np.newaxis], reference_comment5)
×
396
    comment5 = np.char.add(np.char.add(response_comment5, np.array(' / ')), reference_comment5)
×
397
    frf_data = data_array(FunctionTypes.FREQUENCY_RESPONSE_FUNCTION,
×
398
                          frequency_lines,
399
                          frf_data,
400
                          frf_coordinate,
401
                          comment1,
402
                          comment2,
403
                          comment3,
404
                          comment4,
405
                          comment5)
406
    return time_data, frf_data, coherence_data, channel_table
×
407

408

409
def read_transient_control_data(file, coordinate_override_column=None):
3✔
410
    if isinstance(file, str):
×
411
        ds = nc4.Dataset(file, 'r')
×
412
    elif isinstance(file, nc4.Dataset):
×
413
        ds = file
×
414
    coordinate_override_column = None
×
415

416
    environment = [group for group in ds.groups if not group == 'channels'][0]
×
417

418
    # Get the channels in the group
419
    if coordinate_override_column is None:
×
420
        nodes = [int(''.join(char for char in node if char in '0123456789'))
×
421
                 for node in ds['channels']['node_number']]
422
        directions = np.array(ds['channels']['node_direction'][:], dtype='<U3')
×
423
        coordinates = coordinate_array(nodes, directions)
×
424
    else:
425
        coordinates = coordinate_array(string_array=ds['channels'][coordinate_override_column])
×
426
    drives = ds['channels']['feedback_device'][:] != ''
×
427

428
    # Cull down to just those in the environment
429
    environment_index = np.where(ds['environment_names'][:] == environment)[0][0]
×
430
    environment_channels = ds['environment_active_channels'][:, environment_index].astype(bool)
×
431

432
    drives = drives[environment_channels]
×
433
    coordinates = coordinates[environment_channels]
×
434

435
    control_indices = ds[environment]['control_channel_indices'][:]
×
436

437
    control_coordinates = coordinates[control_indices]
×
438

439
    drive_coordinates = coordinates[drives]
×
440

441
    # Load the time data
442
    timesteps = np.arange(ds[environment].dimensions['signal_samples'].size)/ds.sample_rate
×
443

444
    spec_signal = np.array(ds[environment]['control_signal'][...])
×
445

446
    response_signal = np.array(ds[environment]['control_response'][...])
×
447

448
    drive_signal = np.array(ds[environment]['control_drives'][...])
×
449

450
    response_coordinates = control_coordinates[:, np.newaxis]
×
451
    drive_coordinates = drive_coordinates[:, np.newaxis]
×
452

453
    comment1 = np.char.add(np.char.add(np.array(ds['channels']['channel_type'][:], dtype='<U80'),
×
454
                                       np.array(' :: ')),
455
                           np.array(ds['channels']['unit'][:], dtype='<U80'))
456
    comment2 = np.char.add(np.char.add(np.array(ds['channels']['physical_device'][:], dtype='<U80'),
×
457
                                       np.array(' :: ')),
458
                           np.array(ds['channels']['physical_channel'][:], dtype='<U80'))
459
    comment3 = np.char.add(np.char.add(np.array(ds['channels']['feedback_device'][:], dtype='<U80'),
×
460
                                       np.array(' :: ')),
461
                           np.array(ds['channels']['feedback_channel'][:], dtype='<U80'))
462
    comment4 = np.array(ds['channels']['comment'][:], dtype='<U80')
×
463
    comment5 = np.array(ds['channels']['make'][:], dtype='<U80')
×
464
    for key in ('model', 'serial_number', 'triax_dof'):
×
465
        comment5 = np.char.add(comment5, np.array(' '))
×
466
        comment5 = np.char.add(comment5, np.array(ds['channels'][key][:], dtype='<U80'))
×
467

468
    comment1 = comment1[environment_channels]
×
469
    comment2 = comment2[environment_channels]
×
470
    comment3 = comment3[environment_channels]
×
471
    comment4 = comment4[environment_channels]
×
472
    comment5 = comment5[environment_channels]
×
473

474
    # Save the data to SDynpy objects
475
    response_signal = data_array(FunctionTypes.TIME_RESPONSE,
×
476
                                 timesteps, response_signal, response_coordinates,
477
                                 comment1[control_indices], comment2[control_indices], comment3[control_indices],
478
                                 comment4[control_indices], comment5[control_indices])
479
    spec_signal = data_array(FunctionTypes.TIME_RESPONSE,
×
480
                             timesteps, spec_signal, response_coordinates,
481
                             comment1[control_indices], comment2[control_indices], comment3[control_indices],
482
                             comment4[control_indices], comment5[control_indices])
483
    drive_signal = data_array(FunctionTypes.TIME_RESPONSE,
×
484
                              timesteps, drive_signal, drive_coordinates,
485
                              comment1[drives], comment2[drives], comment3[drives],
486
                              comment4[drives], comment5[drives])
487
    return response_signal, spec_signal, drive_signal
×
488

489

490
def create_synthetic_test(spreadsheet_file_name: str,
3✔
491
                          system_filename: str, system: System,
492
                          excitation_coordinates: CoordinateArray,
493
                          response_coordinates: CoordinateArray,
494
                          rattlesnake_directory: str,
495
                          displacement_derivative=2,
496
                          sample_rate: int = None,
497
                          time_per_read: float = None,
498
                          time_per_write: float = None,
499
                          integration_oversample: int = 10,
500
                          environments: list = [],
501
                          channel_comment_data: list = None,
502
                          channel_serial_number_data: list = None,
503
                          channel_triax_dof_data: list = None,
504
                          channel_engineering_unit_data: list = None,
505
                          channel_warning_level_data: list = None,
506
                          channel_abort_level_data: list = None,
507
                          channel_active_in_environment_data: dict = None
508
                          ):
509
    A, B, C, D = system.to_state_space(displacement_derivative == 0,
×
510
                                       displacement_derivative == 1,
511
                                       displacement_derivative == 2,
512
                                       True,
513
                                       response_coordinates,
514
                                       excitation_coordinates)
515
    np.savez(system_filename, A=A, B=B, C=C, D=D)
×
516
    # Load in Rattlesnake to create a template for the test
517
    sys.path.insert(0, rattlesnake_directory)
×
518
    import components as rs
×
519
    environment_data = []
×
520
    for environment_type, environment_name in environments:
×
521
        # Find the identifier
522
        environment_type = rs.environments.ControlTypes[environment_type.upper()]
×
523
        environment_data.append((environment_type, environment_name))
×
524
    rs.ui_utilities.save_combined_environments_profile_template(spreadsheet_file_name, environment_data)
×
525
    sys.path.pop(0)
×
526
    # Populate the channel table
527
    workbook = opxl.load_workbook(spreadsheet_file_name)
×
528
    worksheet = workbook.get_sheet_by_name('Channel Table')
×
529
    index = 3
×
530
    for i, channel in enumerate(response_coordinates):
×
531
        worksheet.cell(index, 1, i+1)
×
532
        worksheet.cell(index, 2, channel.node)
×
533
        worksheet.cell(index, 3, _string_map[channel.direction])
×
534
        worksheet.cell(index, 12, 'Virtual')
×
535
        worksheet.cell(index, 14, 'Accel')
×
536
        index += 1
×
537
    for i, channel in enumerate(excitation_coordinates):
×
538
        worksheet.cell(index, 1, len(response_coordinates)+i+1)
×
539
        worksheet.cell(index, 2, channel.node)
×
540
        worksheet.cell(index, 3, _string_map[channel.direction])
×
541
        worksheet.cell(index, 12, 'Virtual')
×
542
        worksheet.cell(index, 14, 'Force')
×
543
        worksheet.cell(index, 20, 'Shaker')
×
544
        index += 1
×
545
    # Go through the various channel table data that could have been optionally
546
    # provided
547
    for column, data in [(4, channel_comment_data),
×
548
                         (5, channel_serial_number_data),
549
                         (6, channel_triax_dof_data),
550
                         (8, channel_engineering_unit_data),
551
                         (22, channel_warning_level_data),
552
                         (23, channel_abort_level_data)]:
553
        if data is None:
×
554
            continue
×
555
        for row_index, value in enumerate(data):
×
556
            worksheet.cell(3+row_index, column, value)
×
557
    # Now fill out the environment table
558
    if channel_active_in_environment_data is not None:
×
559
        for environment_index, (environment_type, environment_name) in enumerate(environment_data):
×
560
            for row_index, value in enumerate(channel_active_in_environment_data[environment_name]):
×
561
                if value:
×
562
                    worksheet.cell(3+row_index, 24+environment_index, 'X')
×
563
    else:
564
        for environment_index, (environment_type, environment_name) in enumerate(environment_data):
×
565
            for row_index in range(response_coordinates.size + excitation_coordinates.size):
×
566
                worksheet.cell(3+row_index, 24+environment_index, 'X')
×
567
    worksheet = workbook.get_sheet_by_name('Hardware')
×
568
    worksheet.cell(1, 2, 5)
×
569
    worksheet.cell(2, 2, os.path.abspath(system_filename))
×
570
    if sample_rate is not None:
×
571
        worksheet.cell(3, 2, sample_rate)
×
572
    if time_per_read is not None:
×
573
        worksheet.cell(4, 2, time_per_read)
×
574
    if time_per_write is not None:
×
575
        worksheet.cell(5, 2, time_per_write)
×
576
    worksheet.cell(6, 2, 1)
×
577
    worksheet.cell(7, 2, integration_oversample)
×
578
    workbook.save(spreadsheet_file_name)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc