Open Mahyarasadi opened 1 year ago
Apologies, I must have missed this issue popping up, so I'm sorry if this is too late to help.
This is relatively simple in SDynPy. You should create a TimeHistoryArray
object for your signals from which you can compute frequency response functions relatively easily. I assume you start from a NumPy array where you have acceleration and force signals? You can see on the Showcase documentation page how you can construct these functions. Also in that same page, you can see how you can compute FRFs from time histories.
I'll show a more complete example here. I will use the demo package to generate some signals that will create meaningful FRFs, strip them down into just NumPy arrays, which I assume is your starting point, then re-assemble them back into SDynPy objects and compute FRFs. It might look like a lot of code, but the initial portion is just to generate some time data to play with.
Here is a snippet of code to generate acceleration and force signals from a plate object that SDynPy includes in its demo package, and then reduce down to just the NumPy arrays that you have.
#%% Import SDynPy, Numpy, and Matplotlib
import sdynpy as sdpy
import numpy as np
import matplotlib.pyplot as plt
#%% Pull Geometry and System from the Demo package
from sdynpy.demo.beam_plate import geometry,system
#%% Select degrees of freedom
response_dofs = sdpy.coordinate_array(
node = geometry.node.id,
direction = 3)
excitation_dofs = sdpy.coordinate_array(
string_array = ['41Z+','23Z+','1Z+'])
geometry.plot_coordinate(response_dofs,label_dofs=True)
geometry.plot_coordinate(excitation_dofs, label_dofs = True)
#%% Perform a test
# Reduce to modal system of equations for faster integration
modes = system.eigensolution(num_modes = 50)
# Add damping
modes.damping = 0.01
# Create a modal system from the modes
modal_system = modes.system()
# Simulate a modal test using burst random excitation
responses, references = modal_system.simulate_test(
bandwidth = 1000,
frame_length = 4000,
num_averages = 5,
excitation = 'burst random',
references = excitation_dofs,
responses = response_dofs,
signal_fraction = 0.5
)
#%% Extract just the ordinates as numpy arrays and get the time step as well
dt = np.mean(np.diff(responses.abscissa))
responses = responses.ordinate
references = references.ordinate
At this point, I have my accelerations stored in the variables responses
, my forces stored in the variable references
, and a time step dt
. All are NumPy arrays, except for dt
which is a scalar. I believe this is the point that you are starting from. My responses have shape
of (45, 20000)
(45 channels, 20,000 time steps), and my responses have shape
of (3, 20000)
(3 shakers, 20,000 time steps).
The first thing we will do is to construct a TimeHistoryArray
from these data. While we could do this using the TimeHistoryArray
constructor, there is a helper function data_array
that will make this easier, similarly to how you can construct a Numpy np.ndarray
using its constructor, but it's easier to use the helper function np.array
.
Looking at the data_array
function, we can see that we need to specify a FunctionTypes
, then the abscissa (independent variable, in this case the time steps), the ordinate (the acceleration or force data), coordinate information (i.e. the node and direction associated with each signal, see coordinate_array
), and then any comments we would like to include (optional). This will look something like this:
# Set up coordinate informations
response_nodes = np.arange(45)+1 # gives the numbers 1-45
reference_nodes = [41, 23, 1] # Nodes at which my shakers are oriented
direction = 'Z+' # Both my shakers and accelerometers were pointed in the Z direction
# Construct CoordinateArray objects that the TimeHistoryArray is looking for
response_coordinates = sdpy.coordinate_array(response_nodes,direction)
reference_coordinates = sdpy.coordinate_array(reference_nodes,direction)
# Create the TimeHistoryArrays
responses_sd = sdpy.data_array(
sdpy.data.FunctionTypes.TIME_RESPONSE, # The function type
np.arange(responses.shape[1])*dt, # The abscissa, which will be broadcast out to the size of the ordinate
responses, # The ordinate, which are my accelerations
response_coordinates[:, np.newaxis] # response coordinates, which needs to be nchannels x 1, hence the np.newaxis
)
references_sd = sdpy.data_array(
sdpy.data.FunctionTypes.TIME_RESPONSE, # The function type
np.arange(references.shape[1])*dt, # The abscissa, which will be broadcast out to the size of the ordinate
references, # The ordinate, which are my forces
reference_coordinates [:, np.newaxis] # reference coordinates, which needs to be nchannels x 1, hence the np.newaxis
)
At this point you should be able to easily plot the responses by using their plot
method.
responses_sd.plot()
We can now compute FRFs from the time histories using the TransferFunctionArray.from_time_data
static method. Look at the documentation for the calling signature. We will need to specify the reference data, response data, and samples per measurement frame. There are other options if you wanted to apply a window function or use overlap, but those are not applicable for a burst random signal.
frfs = sdpy.TransferFunctionArray.from_time_data(references_sd,responses_sd,
samples_per_average = 4000)
Because there are a lot of FRFs (45x3), it is useful to use SDynPy's GUIPlot
to quickly pop through all of them to make sure they look good. This allows you to graphically select which function to look at. Also note that the coordinate information has been propagated from the time histories to the FRFs, which makes bookkeeping easier for you.
sdpy.GUIPlot(frfs)
Hope this helps!
Hi, I would like to fit acceleration time signals to an FRF object. Looking in the example files, a file has been mentioned which is not in the standard package
filename = r'rattlesnake_modal_data.nc4'
Is there any source or other example with the files to learn how to format my time signal measurements?