-
Notifications
You must be signed in to change notification settings - Fork 3
OpenCLWorkerFFTTutorial
The purpose of this tutorial is to provide an overview of the current capabilities of the OpenCLWorker, as well as provide examples for how users should use it within their models. This guide assumes basic proficiency MEDEA modelling, including the ability to generate interfaces containing Components, Aggregates and Vectors, along with the ability to specify ControlFlow and DataFlow links within component behaviours. It is also assumed that the OpenCL runtime is available on any nodes that the user wishes to assign OpenCLWorker-dependent components to, which for most modern GPUs should be as simple as ensuring that the machine has up-to-date drivers (Installing OpenCL GPU Drivers).
The contents of this guide are as follows:
- Create a simple FFT_Processor component that will run the FFT operation of the OpenCL Worker when stimulated by an incoming message containing data, then print a message containing the results of the FFT.
- Demonstrate how to generate data that can be passed into the FFT by creating a new Sensor component to stimulate the FFT_Processor.
- Extend the FFT_Processor component to perform a brief analysis of the data produced by the FFT operation and display the results.
- Inside the Interfaces aspect, create an Aggregate called SensorSample
- Inside SensorSample, create a Member called SensorID of type Integer with key set to true
- Inside SensorSample, create a Vector called SampleVector
- Inside SampleVector, create a Member called SampleValue of type Float
- Inside the Interfaces aspect, create a Component called FFT_Processor
- Inside FFT_Processor, create a SubscriberPort using Aggregate SensorSample, then name it FFT_Sample_Port
- Inside FFT_Processor, create an Attribute called FFT_Length of type Integer
- Inside the Behaviour aspect, select the ComponentImpl FFT_Processor
- Inside FFT_Processor, create a ClassInstance of OpenCL_Worker
- Inside FFT_Sample_Port, create a FunctionCall of FFT
- Connect the SampleVector inside SensorSample inside FFT_Sample_Port to Data inside FFT 's Input Parameters
Before we can make use of our new FFT_Processor, we will need to create a Component that will provide it with data to process. In this example we will generate a Vector of samples that represent a signal with a frequency of 13 Hz sampled over a period of one second. To do this we need to set the value of the ith sample accordingly: samples[i] = cos(freq * i * 2*pi / fftLength), where freq would be 13 and fftLength would be 32.
In order to set the value of each sample we will have to iterate through each element of the vector, so we will now create the body of the while loop.
- Inside the Interfaces aspect, create a Component called Sensor
- Inside Sensor, create an PublisherPort using Aggregate SensorSample, then name it Sensor_Sample_Port
- Inside Sensor, create an Attribute called FFT_Length of type Integer
- Inside Sensor, create an Attribute called Sensor_ID of type Integer
- Inside the Behaviour Aspect, select the Sensor ComponentImpl and create a PeriodicPort
- Inside the PeriodicPort, create a Variable called NewSample (Which will hold the samples we are generating before they are transmitting)
- Inside NewSample, create a Vector called NewSampleVector
- Inside NewSampleVector, create a Member called NewSampleValue and set the type to Float
- Inside Sensor, create a ClassInstance of Vector_Operations
- Inside the PeriodicPort, create a FunctionCall of type Resize
- Connect NewSampleVector to the Vector InputParameter within the Resize FunctionCall
- Connect the FFT_Length Attribute to the Size InputParameter within the Resize FunctionCall
- Set the DefaultValue InputParameter within the Resize FunctionCall to
0 - Inside the PeriodicPort, create a ForLoop
- Inside the ForLoop, connect VariableParameter i to the lhs inside the BooleanExpression
- Connect the matching rhs inside the BooleanExpression to the Sensor 's FFT_Length AttributeImpl
- Inside the PeriodicPort, create a PublisherPortImpl of Sensor_Sample_Port
- Connect the Sensor_ID AttributeImpl to the SensorID Member of the SensorSample Aggregate within the Sensor_Sample_Port
- Connect the NewSample Variable within the PeriodicPort to the SampleVector Member of the SensorSample Aggregate within the Sensor_Sample_Port
- Inside the Sensor, create a ClassInstance of Utility_Worker
- Inside the ForLoop, create a FunctionCall from Utility_Worker of type Evaluate Complexity
- Inside EvaluateComplexity 's Input Parameters, create two Optional Parameter
- Set the value of Complexity Function Input Parameter inside Evaluate Complexity to
"cos(13 * i * (2*pi) / L)"(according to the equation mentioned at the start of the section) - Connect the i VariableParameter inside the ForLoop 's BooleanExpression to the first Optional Parameter inside EvaluateComplexity
- Connect the FFT_Length AttributeImpl to the second Optional Parameter inside EvaluateComplexity
- Inside the ForLoop, create a FunctionCall from Vector_Operations of type Set
- Connect the NewSample Variable to the Input Parameter Vector inside Set
- Connect the i Optional Parameter inside the ForLoop 's BooleanExpression to the Input Parameter Index within Set
- Connect the Return Parameter Operations inside Evaluate Complexity to Input Parameter Value inside Set
- The Sensor will now produce a message containing our 13 Hz samples vector each second!
Now that we have valid data to process, we can turn our attention back to the FFT_Processor and begin to analyse the result of the FFT operation. While the data passed in was a simple array of float values, the result treats each pair of floats as the real and imaginary components of the complex number associated with each frequency, with our array of length 32 illustrated below:
Array Index
|------|
freq = 0Hz | real | 0
| im | 1
|------|
freq = 1Hz | real | 2
| im | 3
|------|
freq = 2Hz | real | 4
| im | 5
|------|
... ... ...
|------|
freq = 13Hz | real | 26
| im | 27
|------|
freq = 14Hz | real | 28
| im | 29
|------|
freq = 15Hz | real | 30
| im | 31
|------|
For the purposes of this analysis we will simply calculate the magnitude for each frequency and print it, which should display a spike at frequency 13 that matches with the frequency generated by our Sensor Component.
- Inside FFT_Processor 's FFT_Sample_Port, create a ForLoop
- Inside the ForLoop, connect VariableParameter i to the lhs inside the BooleanExpression
- Connect the matching rhs inside the BooleanExpression to the Sensor 's FFT_Length AttributeImpl
- There is no need to change the values of the iteration Setter within the ForLoop, leave it at i += 1
- Inside Sensor, create a ClassInstance of Vector_Operations
- Inside the ForLoop, create a FunctionCall from Vector_Operations of type Get
- Connect SampleVector inside FFT_Sample_Port 's SensorSample to Input Parameter Vector inside Get
- Connect the i VariableParameter within the ForLoop to Input Parameter Index inside Get
- Inside the ForLoop, create a Setter and change the InputParameter operator to +=
- Connect the i VariableParameter within the ForLoop to the lhs within the Setter
- Set the value of the rhs within the Setter to
1 - Inside the ForLoop, create another FunctionCall from Vector_Operations of type Get
- Connect SampleVector inside FFT_Sample_Port 's SensorSample to Input Parameter Vector inside the new Get
- Connect the i VariableParameter within the ForLoop to Input Parameter Index inside the second Get
- Inside FFT_Processor, create a ClassInstance of Utility_Worker
- Inside the ForLoop, create a FunctionCall from Utility_Worker of type Evaluate Complexity
- Inside Evaluate Complexity 's Input Parameters, create two Optional Parameters
- Set the value of the Complexity Function Input Parameter inside Evaluate Complexity to
"sqrt(r^2 + i^2)" - Connect the Return Parameters' Value within the first Get to the first Optional Parameter within Evaluate Complexity 's Input Parameters
- Connect the Return Parameters' Value within the second Get to the second Optional Parameter within Evaluate Complexity 's Input Parameters
- Inside the ForLoop, create another FunctionCall from Utility_Worker of type Evaluate Complexity
- Inside this new Evaluate Complexity 's Input Parameters, create an Optional Parameter
- Set the value of the Complexity Function Input Parameter inside the second Evaluate Complexity to
"(n-1)/2" - Connect the i VariableParameter within the ForLoop to the Optional Parameter within the second Evaluate Complexity 's Input Parameters
- Inside the ForLoop, create a FunctionCall from Utility_Worker of type Log
- Set the value of the Message Format Input Parameter inside Log to
"%f Hz frequency: %f \r\n" - Inside Log 's Input Parameters, create two Optional Parameters
- Connect the Return Parameter Operations within the second Evaluate Complexity to the first Optional Parameter within Log 's Input Parameters
- Connect the Return Parameter Operations within the first Evaluate Complexity to the second Optional Parameter within Log 's Input Parameters
- The FFT_Processor will now print out the magnitudes of the FFT result when run
- Inside Assemblies aspect, create a ComponentAssembly called FFT_Assembly
- Inside FFT_Assembly, create a ComponentInstance of Sensor called sensor1
- Inside Sensor set the value of Attribute Sensor_ID to
1 - Inside Sensor set the value of Attribute FFT_Length to
32 - Inside FFT_Assembly, create a ComponentInstance of FFT_Processor called processor
- Inside processor set the value of Attribute FFT_Length to
32 - Connect PublisherPortInstance Sensor_Sample_Port to the SubscriberPortInstance FFT_Sample_Port
- Deploy FFT_Assembly to a HardwareNode that has an OpenCL device
- If the HardwareNode has no contents (AMD, NVIDIA, Intel), then it does not support OpenCL
Download for completed tutorial: Tutorial.graphml
The resulting output after initialisation should take the form:
0.000000 Hz frequency: 0.000000
1.000000 Hz frequency: 0.000000
2.000000 Hz frequency: 0.000000
3.000000 Hz frequency: 0.000000
4.000000 Hz frequency: 0.000000
5.000000 Hz frequency: 0.000000
6.000000 Hz frequency: 0.000000
7.000000 Hz frequency: 0.000000
8.000000 Hz frequency: 0.000000
9.000000 Hz frequency: 0.000000
10.000000 Hz frequency: 0.000000
11.000000 Hz frequency: 0.000000
12.000000 Hz frequency: 0.000000
13.000000 Hz frequency: 16.000000
14.000000 Hz frequency: 0.000000
15.000000 Hz frequency: 0.000000
Generated on 11 Feb 2021 @ 17:34:14 by Jenkins - MediaWiki2GitHubWiki
- Model Entities
- Loading and Saving Projects
- Modelling Quality of Service
- Workers
- Validation and Code Generation Functionality
- Jenkins Functionality
- Logging Functionality
- Toolbars
- Data Table
- Dock
- Search
- Notification Manager
- View Manager
- Settings
- App Shortcuts
- Code Editor
- Code Browser
- Execution Monitor
- QoS Browser
- Trigger Browser









