The procedure for requesting AWR support has changed. Please read all about the new AWR product support process.
Page tree
Skip to end of metadata
Go to start of metadata

Introduction


One of the advantages of Python scripting with the AWR Design Environment (AWRDE) is the flexibility in graphing that is available with Python. Different viewpoints of the same data can be a big benefit in solving engineering problems. Presenting data in highly customizable graphs is a further advantage of extracting data from AWRDE and plotting the results in Python.

Some of the graphing capabilities that Python can offer are:

  • Contour plots
  • 3D plots beyond what is available in AWRDE
  • Adding shaded regions to highlight significant findings

Another area that may be of interest is to read trace data from AWRDE into Python then perform some post processing of the trace data such as filtering, smoothing, etc. and then export the results back into AWRDE. Whether plotting data in Python or performing some sort of data processing in Python, importing and exporting data between AWRDE and Python is involved. This article explains some of the graphing related techniques using Python with AWRDE including:

  • Reading scalar and complex data
  • Reading the x-axis data whether it be frequency, power or some other swept variable
  • Reading multiple traces as a result of a swept variable simulation
  • Writing data from Python into AWRDE
  • Creating graphs and measurements using Python and the AWRDE API
  • Using Python modules to plot the data

All of this information is available in the AWR API Scripting Guide , Working with Graphs section. However as mentioned in AWR Scripting in Python: Using the AWRDE API Scripting Guide the API Scripting guide is written for the SAX Basic implementation of Visual Basic for Applications (VBA).  The AWR API Scripting Guide will still be required for a complete list of the command syntax options. It is the intent that the example code in this article aids in interpreting the SAX Basic syntax found in the AWR API Scripting Guide into Python syntax.

This article assumes that Python, a Python IDE and the pyawr module have been installed. See AWR Scripting in Python: Getting Started and Installation for installation instructions. Additionally a Python plotting module will need to be installed. This article uses the popular Matplotlib module. Please refer to https://matplotlib.org/ for instructions on installing Matplotlib as well as detailed tutorials, API syntax, and plotting examples.

Setup

At the beginning of the Python script, the appropriate modules must be installed. The following examples use numpy, matplotlib and pyawr:

Python modules
import numpy as np              #Import numpy math module
import pyawr.mwoffice as mwo    #Import pyawr interface to AWRDE
import matplotlib.pyplot as plt #Import pyplot module from Matplotlib

Next a link between Python and AWRDE must be established:

AWRDE link
awrde = mwo.CMWOffice()  #Establish link to AWRDE
project = awrde.Project  #Create "project" variable

The project variable is a convenience item. More information on linking Python to AWRDE can be found in AWR Scripting in Python: Using the AWRDE API Scripting Guide.

The coding examples in this article use the AWRDE installed project LPF_Lumped.emp.

Matplotlib

Matplotlib is an open-source Python module that is highly configurable and can be used to present data in almost any type of graph imaginable. Higher level plotting functions are available in the submodule pyplot and this is what will be used for the following examples. For even more advanced graphics programming, other submodules are available within the umbrella of the matplotlib module. The intent of this article is to mostly show the AWRDE graphing API commands, not to cover matplotib programming, please refer to the above matplotlib link for matplotlib commands beyond what is introduced here.

Before showing the AWRDE graph API commands, a couple of very basic matplotib plotting examples will be shown. Later on data originating from AWRDE will be used in these types of graphs. For the simplest of graphs, only two matplotlib lines of code are required: one to plot the data and the other to display the graph window.  In the following example, x and y vectors are created for a sine wave:

Basic graph
#Create data
 x_ay = np.linspace(0,100,101)    #Create vector from 0 to 100 with 101 points
 y_ay = np.sin(2*np.pi*x_ay/100)  #Sine wave with 1 period over the x_ay range
 
 plt.plot(x_ay, y_ay)  #pyplot plot function
 plt.show()            #Display plot window

This code produces this graph:

Further embellishments include adding titles, axis labels, grids and setting the axis ranges:

Embellished graph
    #Create data
    x_ay = np.linspace(0,100,101)        #Create vector from 0 to 100 with 101 points
    y_ay = np.sin(2*np.pi*x_ay/100)      #Sine wave with 1 period over the x_ay range
    #
    plt.xlim([0,100])                    #x-axis range
    plt.ylim([-2,2])                     #y-axis range
    plt.title('Sine Wave', fontsize=16, fontweight='bold') #Plot title
    plt.xlabel('x-axis values', fontsize=14)  #x-axis label
    plt.ylabel('y-axis values', fontsize=14)  #y-axis label
    plt.grid(True, linestyle=':')             #Enable grid. Set linestyle to dots
    #
    plt.plot(x_ay, y_ay, color='r', linewidth=2) #plot with red trace color and specified width
    plt.show()                                   #Display plot window

Producing this output:

Working with Graphs

List All the Graphs in the Project

The collection variable Graphs contains of all the graphs in the project. The following code shows how to loop through each graph in the project and print the name of the graph and its type

List Graphs in Project
	for graph in project.Graphs:           #Loop through all the Graphs in the project
        print(graph.Name)                  #Print graph name
        print(mwo.mwGraphType(graph.Type)) #Print graph type (Rectangular, Smith, etc.)

From above, the variable project is equal to awrde.Project

This code produces this result from the installed example LPF_Lumped.emp

Edit Graph Properties

Shown here are a few examples of editing graph properties

Edit graph properties
	graph = project.Graphs('Passband and Stopband')  #Set the graph variable to a particular graph
    graph.Title = 's21 and s11'                      #Change graph title
    x_axis = graph.Axes[0]                           #Assign x-axis to variable
    x_axis.MinimumScale = 200                        #Set x-axis minimum to 200 MHz
    x_axis.MaximumScale = 700                        #Set x-axis maximum to 700 MHz
    x_axis.MajorGridlinesStep = 50                   #Set x-axis step size to 50 MHz


Create a New Graph

These commands add a Smith chart to the LPF_Lumped.emp example project

Create new graph
	if project.Graphs.Exists('LPF Smith Chart'):   #Check to see if the Graph exists
        project.Graphs.Remove('LPF Smith Chart')   #Delete graph if it exists
    #
    #Add new graph with graph name: 'LPF Smith Chart' and type Smith Chart
    graph = project.Graphs.Add('LPF Smith Chart', mwo.mwGraphType.mwGT_SmithChart)    

In case the graph already exists, the first two lines delete the graph first.

Working with Measurements

List of Measurements in a Graph

Measurements collection contains all the measurements within a single graph. This code demonstrate returning the number of measurements  in a graph, then for each measurement return: measurement name, source document and enabled state

Measurement listing
	graph = project.Graphs('Passband and Stopband')
    NumberOfMeasurements = graph.Measurements.Count  #Number of traces on graph
    
    for meas in graph.Measurements:  #Loop through all the measurements in the graph
        Name = meas.Name             #Name as shown in project tree for the measurement
        SourceDocument = meas.Source #Schematic where measurement originates (Source Document)
        IsEnabled = meas.Enabled     #Enabled or disabled status


Adding, Deleting and Accessing a Single Measurement

When adding a measurement, the pass parameters are source document (schematic where measurement originates) and the measurement name. Deleting the measurement uses a single pass parameter which is source document and measurement name separated by a colon. Accessing a measurement can use either index or measurement name.

Adding, deleting, accessing a measurement
    graph = project.Graphs('Passband and Stopband')
    
    #Add measurement to graph
    graph.Measurements.Add('LPF','DB(|S(1,2)|)')  #Parameters are (Source Document, Measurement Name)
 
    #Remove measurement from graph
    graph.Measurements.Remove('LPF:DB(|S(1,2)|)')
    
    #Access a measurement
    meas = graph.Measurements[0]                    #Access by index
    meas = graph.Measurements('LPF:DB(|S(2,1)|)')   #Access by measurement name
     
    #Toggle enable/disable
    meas.Enabled = True
    meas.Enabled = False


Reading Measurement Scalar Data

Scalar data is single Y data point such as magnitude, phase, real, etc. An example of of multiple Y data points would be complex data (for example from a Smith chart). The first method is to read the entire measurement into an array at once. The meas.TraceValues() method takes a single value of 1 as the pass parameter for data that does not include swept variables. This function returns an array of tuples with each data point an (x ,y) tuple. The numpy function asarray() can convert this array into a standard array

Scalar data read (Method 1)
	graph = project.Graphs('Passband and Stopband')
    meas = graph.Measurements('LPF:DB(|S(2,1)|)')
    
    #Method 1: Read measurment into an array
    MeasData_Tuple = meas.TraceValues(1)         #Returns set of (x,y) tuples for each data point
    MeasData_ay = np.asarray(MeasData_Tuple)     #numpy function to covert tuple into an array
    Freq_ay = MeasData_ay[:,0]                   #Extract x data vector
    s21_ay = MeasData_ay[:,1]                    #Extract y data vector
    plt.plot(Freq_ay, s21_ay)                    #Matplotlib plot function
    plt.show()                                   #Display graph window

Resulting Python graph:

The second method is to read individual data points. For instance only select data points may want to be read instead of the entire measurement array.

Scalar data read (Method 2)
	graph = project.Graphs('Passband and Stopband')
    meas = graph.Measurements('LPF:DB(|S(2,1)|)')
    
    #Method 2: Read individual measuremnt data points
    Freq_list = list()
    s21_list = list()    
    NumDataPoints = meas.XPointCount           #Number of data points in the measurement
    for i in range(1, NumDataPoints, 10):      #Loop through every 10th data point
        Freq_list.append(meas.XValue(i))       #Read x-data point
        s21_list.append(meas.YValue(i,1))      #Read y-data point     
    plt.plot(Freq_list, s21_list, marker='o')
    plt.show()

The XValue() and YValue() methods are indexed beginning at a value of 1. The resulting graph looks like:



Reading Multi-value Y-axis Data

For y-axis data that may contain two values, for example complex data, the measurement read process is similar to that of scalar data. For the trace array read (Method 1) , the tuple contains three elements: (x, y_real and y_imaginary). The code here adds a Smith Chart to the LPF_Lumped.emp example and reads the complex data measurement:

Complex data read
    #Add new Smith Chart graph
    if project.Graphs.Exists('LPF Smith Chart'):   #Check to see if the Graph exists
        project.Graphs.Remove('LPF Smith Chart')   #Delete graph if it exists
    #end if 
	#
	#Add new Smith Chart graph   
    graph = project.Graphs.Add('LPF Smith Chart',mwo.mwGraphType.mwGT_SmithChart)
    meas = graph.Measurements.Add('LPF','S(1,1)')    #Add measurement
    project.Simulator.Analyze()                      #Simulate
    #
    MeasData_Tuple = meas.TraceValues(1)         #Returns set of (x, y_real, y_imag) tuples
                                                 #for each data point
    MeasData_ay = np.asarray(MeasData_Tuple)     #numpy function to covert tuple into an array
    Freq_ay = MeasData_ay[:,0]                   #Extract x-axis vector
    s11_Real_ay = MeasData_ay[:,1]               #Extract y-axis real vector
    s11_Imag_ay = MeasData_ay[:,2]               #Extract y-axis imaginary vector
    Legend_list = list()                         #Create list for plot legend
    #
    plt.plot(Freq_ay, s11_Real_ay, color='b')    #Plot real data
    Legend_list.append('s11 Real')               #Update legend
    plt.plot(Freq_ay, s11_Imag_ay, color='g')    #Plot imaginary data
    Legend_list.append('s11 Imaginary')          #Update legend
    #
    #Add legend to graph
    LegendHandle = plt.legend(Legend_list, loc='upper left', ncol=1)
    plt.setp(LegendHandle.get_texts(), fontsize=10)
    #
    plt.show()                                 

Resulting Python graph:

The syntax for reading individual points for the y-axis data would be:

    Freq_list.append(meas.XValue(i))            #Read x-data point
    s11_real_list.append(meas.YValue(i,1))      #Read y-data real point 
    s11_imag_list.append(meas.YValue(i,2))      #Read y-data imaginary point


Reading Swept Variable Data

Swept variable simulation is an example of multiple traces in a single measurement. This code first adds a SWPVAR element to the LPF schematic in the LPF_Lumped.emp example and then reads in all the traces from the swept variable s11 measurement.

SWPVAR data read
    #Add SWPVAR to schematic to sweep the inductace value of INDQ.L3
    schem = project.Schematics('LPF')               #Point schem variable to the LPF schematic
    schem.Equations.Add('L3 = 33', 1000, -1300)     #Add equation at specific x,y location in schematic
    element = schem.Elements('INDQ.L3')             #Point element variable to inductor L3
    element.Parameters('L').ValueAsString = 'L3'    #Replace L parameter with the eqquation varaible
    element = schem.Elements.Add('SWPVAR',1000,-2000)   #Add SWPVAR element at specific location
    element.Parameters('Values').ValueAsString = 'swpstp(25,35,5)' #Set sweep parameters
    element.Parameters('VarName').ValueAsString = '"L3"'           #Set sweep variable
    project.Simulator.Analyze()                     #Simulate
    #
    graph = project.Graphs('Passband and Stopband')
    meas = graph.Measurements('LPF:DB(|S(1,1)|)')
    Ydata_list = list()
    SweepLabel_list = list()
    Legend_list = list()
    NumTraces = meas.TraceCount                      #Number of traces in the measurement
    for trace_idx in range(1, NumTraces+1):          #Loop through traces beginning at index 1
        MeasData_Tuple = meas.TraceValues(trace_idx) #Read data is Tuple for each data point
        MeasData_ay = np.asarray(MeasData_Tuple)     #numpy function to covert to array
        if trace_idx == 1:
            Freq_ay = MeasData_ay[:,0]
        #end if
        Ydata_list.append(MeasData_ay[:,1])
        #
        #Sweep label value
        SweepLabel_list.append(meas.SweepLabels(trace_idx).Item(1).Value)
    #
    #Sweep label name
    SweepLabel_Name = meas.SweepLabels(1).Item(1).Name
    #
    for trace_idx in range(NumTraces):
        plt.plot(Freq_ay, Ydata_list[trace_idx])
        Legend_list.append(SweepLabel_Name+' = '+str(SweepLabel_list[trace_idx]))
    #Add legend to graph
    #
    LegendHandle = plt.legend(Legend_list, loc='upper left', ncol=1)
    plt.setp(LegendHandle.get_texts(), fontsize=10)
    #
    plt.show()

Resulting Python graph:

Working with Markers

Markers are not part of the Measurement collection, but rather part of the Graph collect. The following code shows how to add a marker to a particular measurement within the graph, get the marker values, set marker to min and max y-axis positions and finally how to use the search marker method

Markers
	graph = project.Graphs('Passband and Stopband')
    #
    #Add marker on the S(2,1) measurement, trace index 1, x-axis value=300 MHz
    marker = graph.Markers.Add('LPF:DB(|S(2,1)|)', 1, 300e6)
    print('Maker Name:',marker.Name)                    #Marker name (m1, m2, etc.)
    print('Marker Measurement:',marker.Measurement)     #Marker measurement Name
    print('Maker DataValue(1):',marker.DataValue(1))    #Marker x-axis value
    print('Maker DataValue(2):',marker.DataValue(2))    #Marker y-axis value
    #
    marker.MoveToMinimum()     #Move Marker to minimum y-axis point
    marker.MoveToMaximum()     #Move Marker to maximum y-axis point
    #
    #Search marker -3 dB relative to max marker position. Search right along x-axis
    marker.Search(-3, mwo.mwMarkerSearchMode.mwMST_Delta,\
                  mwo.mwMarkerSearchDirection.mwMSD_SearchRight,\
                  mwo.mwMarkerSearchVariable.mwMSV_Y)    

Exporting Data from Python to AWRDE

Exporting data from AWRDE involves writing a text file into the AWRDE project directory. Then plot the data in AWRDE using Data > PlotCol measurement. This code create sine wave x,y points in Python, writes the text file, imports the data in AWRDE and finally creates a graph and plots the imported data:

Exporting data
	#Create data in Python
    x_ay = np.linspace(0,100,101)       #Create vector from 0 to 100 with 101 points
    y_ay = np.sin(2*np.pi*x_ay/100)     #Sine wave with 1 period over the x_ay range
    #
    #Write data to text file in the AWRDE project directory
    Path = project.Path                                #Project directory path
    fw = open(Path+'SineWave.txt', 'w+')               #Low level Python file write
    for i in range(len(x_ay)):
        fw.write(str(x_ay[i])+'    '+str(y_ay[i])+'\n') #Write space delimited x,y per line
    fw.close()
    #
    #Import data file into AWRDE as text data file type
    project.DataFiles.Import('SineWave','SineWave.txt',mwo.mwDataFileType.mwDFT_TXT)
    
    #Add new rectangular graph
    graph = project.Graphs.Add('SineWave',mwo.mwGraphType.mwGT_Rectangular)
    
    #Add measurement to graph
    graph.Measurements.Add('SineWave','PlotCol(1,2)') 
    
    project.Simulator.Analyze()     #Simultate

The resulting AWRDE graph:


  • No labels