Download this example

Download this example as a Jupyter Notebook or as a Python script.


AMI Postprocessing#

This example demonstrates advanced postprocessing of AMI simulations.

Keywords: Circuit, AMI.

Perform imports and define constants#

Perform required imports.

Note: Numpy and Matplotlib are required to run this example.

[1]:
import os
import tempfile
import time

import ansys.aedt.core
import numpy as np
from matplotlib import pyplot as plt

Define constants.

[2]:
AEDT_VERSION = "2024.2"
NG_MODE = False  # Open AEDT UI when it is launched.

Create temporary directory and download example files#

Create a temporary directory where downloaded data or dumped data can be stored. If you’d like to retrieve the project data for subsequent use, the temporary folder name is given by temp_folder.name.

[3]:
temp_folder = tempfile.TemporaryDirectory(suffix=".ansys")

Download example data#

The download_file() method retrieves example data from the PyAnsys example-data repository.

  • The fist argument is the folder name where the example files are located in the GitHub repository.

  • The 2nd argument is the file to retrieve.

  • The 3rd argument is the destination folder.

Files are placed in the destination folder.

[4]:
project_path = ansys.aedt.core.downloads.download_file(
    "ami", name="ami_usb.aedtz", destination=temp_folder.name
)

Launch AEDT with Circuit and enable Pandas as the output format#

All outputs obtained with the get_solution_data() method are in the Pandas format. Launch AEDT with Circuit. The ansys.aedt.core.Desktop class initializes AEDT and starts the specified version in the specified mode.

[5]:
ansys.aedt.core.settings.enable_pandas_output = True
circuit = ansys.aedt.core.Circuit(
    project=os.path.join(project_path),
    non_graphical=NG_MODE,
    version=AEDT_VERSION,
    new_desktop=True,
)
PyAEDT INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
PyAEDT INFO: PyAEDT version 0.14.dev0.
PyAEDT INFO: Initializing new Desktop session.
PyAEDT INFO: Log on console is enabled.
PyAEDT INFO: Log on file C:\Users\ansys\AppData\Local\Temp\pyaedt_ansys_5502ac63-47d3-43de-aa4b-11b20dc438e0.log is enabled.
PyAEDT INFO: Log on AEDT is enabled.
PyAEDT INFO: Debug logger is disabled. PyAEDT methods will not be logged.
PyAEDT INFO: Launching PyAEDT with gRPC plugin.
PyAEDT INFO: New AEDT session is starting on gRPC port 59235
PyAEDT INFO: AEDT installation Path C:\Program Files\AnsysEM\v242\Win64
PyAEDT INFO: Ansoft.ElectronicsDesktop.2024.2 version started with process ID 4092.
PyAEDT INFO: Archive C:\Users\ansys\AppData\Local\Temp\tmpqpfkbbrx.ansys\ami\ami_usb.aedtz has been restored to project Project_LWE
PyAEDT INFO: Active Design set to 0;Circuit1
PyAEDT INFO: Aedt Objects correctly read

Solve AMI setup#

Solve the transient setup.

[6]:
circuit.analyze()
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Circuit Design correctly changed.
PyAEDT INFO: Solving all design setups.
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Circuit Design correctly changed.
PyAEDT INFO: Design setup None solved correctly in 0.0h 0.0m 21.0s
[6]:
True

Get AMI report#

Get AMI report data.

[7]:
plot_name = "WaveAfterProbe<b_input_43.int_ami_rx>"
circuit.solution_type = "NexximAMI"
original_data = circuit.post.get_solution_data(
    expressions=plot_name,
    setup_sweep_name="AMIAnalysis",
    domain="Time",
    variations=circuit.available_variations.nominal,
)
original_data_value = original_data.full_matrix_real_imag[0]
original_data_sweep = original_data.primary_sweep_values
print(original_data_value)
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 1sec
PyAEDT INFO: Solution Data Correctly Loaded.
           WaveAfterProbe<b_input_43.int_ami_rx>
0.000000                             -553.298382
0.003125                             -553.298382
0.006250                             -553.298382
0.009375                             -553.298382
0.012500                             -553.298382
...                                          ...
99.984375                             -25.138119
99.987500                              19.046320
99.990625                              60.268984
99.993750                              98.348353
99.996875                             133.328724

[32000 rows x 1 columns]

Plot data#

Create a plot based on solution data.

[8]:
fig = original_data.plot()
../../../../_images/examples_high_frequency_layout_signal_integrity_ami_16_0.png

Extract wave form#

Use the WaveAfterProbe plot type to extract the waveform using an AMI receiver clock probe. The signal is extracted at a specific clock flank with additional half unit interval.

[9]:
probe_name = "b_input_43"
source_name = "b_output4_42"
plot_type = "WaveAfterProbe"
setup_name = "AMIAnalysis"
ignore_bits = 100
unit_interval = 0.1e-9
sample_waveform = circuit.post.sample_ami_waveform(
    setup=setup_name,
    probe=probe_name,
    source=source_name,
    variation_list_w_value=circuit.available_variations.nominal,
    unit_interval=unit_interval,
    ignore_bits=ignore_bits,
    plot_type=plot_type,
)
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Solution Data Correctly Loaded.

Plot waveform and samples#

Create the plot from a start time to stop time in seconds.

[10]:
tstop = 55e-9
tstart = 50e-9
scale_time = ansys.aedt.core.constants.unit_converter(
    1,
    unit_system="Time",
    input_units="s",
    output_units=original_data.units_sweeps["Time"],
)
scale_data = ansys.aedt.core.constants.unit_converter(
    1,
    unit_system="Voltage",
    input_units="V",
    output_units=original_data.units_data[plot_name],
)

tstop_ns = scale_time * tstop
tstart_ns = scale_time * tstart

for time_value in original_data_value[plot_name].index:
    if tstart_ns <= time_value[0]:
        start_index_original_data = time_value[0]
        break
for time_value in original_data_value[plot_name][start_index_original_data:].index:
    if time_value[0] >= tstop_ns:
        stop_index_original_data = time_value[0]
        break
for time_value in sample_waveform[0].index:
    if tstart <= time_value:
        sample_index = sample_waveform[0].index == time_value
        start_index_waveform = sample_index.tolist().index(True)
        break
for time_value in sample_waveform[0].index:
    if time_value >= tstop:
        sample_index = sample_waveform[0].index == time_value
        stop_index_waveform = sample_index.tolist().index(True)
        break

original_data_zoom = original_data_value[
    start_index_original_data:stop_index_original_data
]
sampled_data_zoom = (
    sample_waveform[0].values[start_index_waveform:stop_index_waveform] * scale_data
)
sampled_time_zoom = (
    sample_waveform[0].index[start_index_waveform:stop_index_waveform] * scale_time
)

fig, ax = plt.subplots()
ax.plot(sampled_time_zoom, sampled_data_zoom, "r*")
ax.plot(
    np.array(list(original_data_zoom.index.values)),
    original_data_zoom.values,
    color="blue",
)
ax.set_title("WaveAfterProbe")
ax.set_xlabel(original_data.units_sweeps["Time"])
ax.set_ylabel(original_data.units_data[plot_name])
plt.show()
../../../../_images/examples_high_frequency_layout_signal_integrity_ami_20_0.png

Plot slicer scatter#

Create the plot from a start time to stop time in seconds.

[11]:
fig, ax2 = plt.subplots()
ax2.plot(sample_waveform[0].index, sample_waveform[0].values, "r*")
ax2.set_title("Slicer Scatter: WaveAfterProbe")
ax2.set_xlabel("s")
ax2.set_ylabel("V")
plt.show()
../../../../_images/examples_high_frequency_layout_signal_integrity_ami_22_0.png

Plot scatter histogram#

Create the plot from a start time to stop time in seconds.

[12]:
fig, ax4 = plt.subplots()
ax4.set_title("Slicer Histogram: WaveAfterProbe")
ax4.hist(sample_waveform[0].values, orientation="horizontal")
ax4.set_ylabel("V")
ax4.grid()
plt.show()
../../../../_images/examples_high_frequency_layout_signal_integrity_ami_24_0.png

Get transient report data#

[13]:
plot_name = "V(b_input_43.int_ami_rx.eye_probe.out)"
circuit.solution_type = "NexximTransient"
original_data = circuit.post.get_solution_data(
    expressions=plot_name,
    setup_sweep_name="NexximTransient",
    domain="Time",
    variations=circuit.available_variations.nominal,
)
PyAEDT INFO: Solution Data Correctly Loaded.

Extract sample waveform#

Extract a waveform at a specific clock time plus a half unit interval.

[14]:
original_data.enable_pandas_output = False
original_data_value = original_data.data_real()
original_data_sweep = original_data.primary_sweep_values
waveform_unit = original_data.units_data[plot_name]
waveform_sweep_unit = original_data.units_sweeps["Time"]
tics = np.arange(20e-9, 100e-9, 1e-10, dtype=float)

sample_waveform = circuit.post.sample_waveform(
    waveform_data=original_data_value,
    waveform_sweep=original_data_sweep,
    waveform_unit=waveform_unit,
    waveform_sweep_unit=waveform_sweep_unit,
    unit_interval=unit_interval,
    clock_tics=tics,
    pandas_enabled=False,
)

Plot waveform#

Create the plot from a start time to stop time in seconds.

[15]:
tstop = 40.0e-9
tstart = 25.0e-9
scale_time = ansys.aedt.core.constants.unit_converter(
    1, unit_system="Time", input_units="s", output_units=waveform_sweep_unit
)
scale_data = ansys.aedt.core.constants.unit_converter(
    1, unit_system="Voltage", input_units="V", output_units=waveform_unit
)

tstop_ns = scale_time * tstop
tstart_ns = scale_time * tstart

for time_value in original_data_sweep:
    if tstart_ns <= time_value:
        start_index_original_data = original_data_sweep.index(time_value)
        break
for time_value in original_data_sweep[start_index_original_data:]:
    if time_value >= tstop_ns:
        stop_index_original_data = original_data_sweep.index(time_value)
        break
cont = 0
for frame in sample_waveform:
    if tstart <= frame[0]:
        start_index_waveform = cont
        break
    cont += 1
for frame in sample_waveform[start_index_waveform:]:
    if frame[0] >= tstop:
        stop_index_waveform = cont
        break
    cont += 1

original_data_zoom = original_data_value[
    start_index_original_data:stop_index_original_data
]
original_sweep_zoom = original_data_sweep[
    start_index_original_data:stop_index_original_data
]
original_data_zoom_array = np.array(
    list(map(list, zip(original_sweep_zoom, original_data_zoom)))
)
original_data_zoom_array[:, 0] *= 1
sampled_data_zoom_array = np.array(
    sample_waveform[start_index_waveform:stop_index_waveform]
)
sampled_data_zoom_array[:, 0] *= scale_time
sampled_data_zoom_array[:, 1] *= scale_data

fig, ax = plt.subplots()
ax.plot(sampled_data_zoom_array[:, 0], sampled_data_zoom_array[:, 1], "r*")
ax.plot(original_sweep_zoom, original_data_zoom_array[:, 1], color="blue")
ax.set_title(plot_name)
ax.set_xlabel(waveform_sweep_unit)
ax.set_ylabel(waveform_unit)
plt.show()
../../../../_images/examples_high_frequency_layout_signal_integrity_ami_30_0.png

Plot slicer scatter#

Create the plot from a start time to stop time in seconds.

[16]:
sample_waveform_array = np.array(sample_waveform)
fig, ax2 = plt.subplots()
ax2.plot(sample_waveform_array[:, 0], sample_waveform_array[:, 1], "r*")
ax2.set_title("Slicer Scatter: " + plot_name)
ax2.set_xlabel("s")
ax2.set_ylabel("V")
plt.show()
../../../../_images/examples_high_frequency_layout_signal_integrity_ami_32_0.png

Release AEDT#

Release AEDT and close the example.

[17]:
circuit.save_project()
circuit.release_desktop()
# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory.
time.sleep(3)
PyAEDT INFO: Project Project_LWE Saved correctly
PyAEDT INFO: Desktop has been released and closed.

Clean up#

All project files are saved in the folder temp_folder.name. If you’ve run this example as a Jupyter notebook, you can retrieve those project files. The following cell removes all temporary files, including the project folder.

[18]:
temp_folder.cleanup()

Download this example

Download this example as a Jupyter Notebook or as a Python script.