Download this example
Download this example as a Jupyter Notebook or as a Python script.
PCIE virtual compliance#
This example shows how to generate a compliance report in PyAEDT using the VirtualCompliance
class.
Keywords: Circuit, Automatic report, virtual compliance.
Perform imports and define constants#
Import the required packages.
[1]:
import os.path
import tempfile
import time
[2]:
import ansys.aedt.core
from ansys.aedt.core.visualization.post.compliance import VirtualCompliance
Define constants#
[3]:
AEDT_VERSION = "2024.2"
NUM_CORES = 4
NG_MODE = False # Open AEDT UI when it is launched.
Create temporary directory#
Create a temporary directory where downloaded data or dumped data can be stored. In this example, the temporary directory in where the example is stored and simulation data is saved. If you’d like to retrieve the project data for subsequent use, the temporary folder name is given by temp_folder.name
.
[4]:
temp_folder = tempfile.TemporaryDirectory(suffix=".ansys")
Download example data#
[5]:
download_folder = ansys.aedt.core.downloads.download_file(
source="pcie_compliance", destination=temp_folder.name
)
project_folder = os.path.join(download_folder, "project")
project_path = os.path.join(project_folder, "PCIE_GEN5_only_layout.aedtz")
Launch AEDT and solve layout#
Open the HFSS 3D Layout project and analyze it using the SIwave solver. Before solving, this code ensures that the model is solved from DC to 70GHz and that causality and passivity are enforced.
[6]:
h3d = ansys.aedt.core.Hfss3dLayout(
project=project_path, version=AEDT_VERSION, non_graphical=NG_MODE
)
h3d.remove_all_unused_definitions()
h3d.edit_cosim_options(simulate_missing_solution=False)
h3d.setups[0].sweeps[0].props["EnforcePassivity"] = True
h3d.setups[0].sweeps[0].props["Sweeps"]["Data"] = "LIN 0MHz 70GHz 0.1GHz"
h3d.setups[0].sweeps[0].props["EnforceCausality"] = True
h3d.setups[0].sweeps[0].update()
h3d.analyze(cores=NUM_CORES)
h3d = ansys.aedt.core.Hfss3dLayout()
touchstone_path = h3d.export_touchstone()
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.12.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_07aeb39f-eb8f-44ed-b9d4-541afa855816.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 55471
PyAEDT INFO: AEDT installation Path C:\Program Files\AnsysEM\v242\Win64
PyAEDT INFO: Ansoft.ElectronicsDesktop.2024.2 version started with process ID 5148.
PyAEDT INFO: Archive C:\Users\ansys\AppData\Local\Temp\tmpobhzvuhb.ansys\pcie_compliance\project\PCIE_GEN5_only_layout.aedtz has been restored to project Project_AXC
PyAEDT INFO: Active Design set to 1;main_cutout
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: Parsing C:/Users/ansys/AppData/Local/Temp/tmpobhzvuhb.ansys/pcie_compliance/project/Project_AXC.aedt.
PyAEDT INFO: File C:/Users/ansys/AppData/Local/Temp/tmpobhzvuhb.ansys/pcie_compliance/project/Project_AXC.aedt correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: aedt file load time 0.1262528896331787
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/HFSS 3D Layout Design correctly changed.
PyAEDT INFO: Solving all design setups.
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/HFSS 3D Layout Design correctly changed.
PyAEDT INFO: Design setup None solved correctly in 0.0h 1.0m 58.0s
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: No project is defined. Project Project_AXC exists and has been read.
PyAEDT INFO: Active Design set to 1;main_cutout
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: Exporting Touchstone C:\Users\ansys\AppData\Local\Temp\tmpobhzvuhb.ansys\pcie_compliance\project\Project_AXC.pyaedt\main_cutout\SIwaveSYZ1_Sweep1.S32p
PyAEDT INFO: Touchstone correctly exported to C:\Users\ansys\AppData\Local\Temp\tmpobhzvuhb.ansys\pcie_compliance\project\Project_AXC.pyaedt\main_cutout\SIwaveSYZ1_Sweep1.S32p
Create LNA project#
Use the LNA (linear network analysis) setup to retrieve Touchstone files and generate frequency domain reports.
[7]:
circuit = ansys.aedt.core.Circuit(project=h3d.project_name, design="Touchstone")
status, diff_pairs, comm_pairs = circuit.create_lna_schematic_from_snp(
input_file=touchstone_path,
start_frequency=0,
stop_frequency=70,
auto_assign_diff_pairs=True,
separation=".",
pattern=["component", "pin", "net"],
analyze=True,
)
insertion = circuit.get_all_insertion_loss_list(
drivers=diff_pairs,
receivers=diff_pairs,
drivers_prefix_name="X1",
receivers_prefix_name="U1",
math_formula="dB",
nets=["RX0", "RX1", "RX2", "RX3"],
)
return_diff = circuit.get_all_return_loss_list(
excitations=diff_pairs,
excitation_name_prefix="X1",
math_formula="dB",
nets=["RX0", "RX1", "RX2", "RX3"],
)
return_comm = circuit.get_all_return_loss_list(
excitations=comm_pairs,
excitation_name_prefix="COMMON_X1",
math_formula="dB",
nets=["RX0", "RX1", "RX2", "RX3"],
)
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Added design 'Touchstone' of type Circuit Design.
PyAEDT INFO: Aedt Objects correctly read
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Added design 'LNA' of type Circuit Design.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
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 8.0s
Create TDR project#
Create a TDR project to compute transient simulation and retrieve the TDR measurement on a differential pair. The original circuit schematic is duplicated and modified to achieve this target.
[8]:
result, tdr_probe_name = circuit.create_tdr_schematic_from_snp(
input_file=touchstone_path,
tx_schematic_pins=["X1.A2.PCIe_Gen4_RX0_P"],
tx_schematic_differential_pins=["X1.A3.PCIe_Gen4_RX0_N"],
termination_pins=["U1.AP26.PCIe_Gen4_RX0_P", "U1.AN26.PCIe_Gen4_RX0_N"],
differential=True,
rise_time=35,
use_convolution=True,
analyze=True,
design_name="TDR",
)
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Added design 'TDR' of type Circuit Design.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
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 8.0s
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 1sec
Create AMI project#
Create an Ibis AMI project to compute an eye diagram simulation and retrieve eye mask violations.
[9]:
_, eye_curve_tx, eye_curve_rx = circuit.create_ami_schematic_from_snp(
input_file=touchstone_path,
ibis_tx_file=os.path.join(project_folder, "models", "pcieg5_32gt.ibs"),
tx_buffer_name="1p",
rx_buffer_name="2p",
tx_schematic_pins=["U1.AM25.PCIe_Gen4_TX0_CAP_P"],
rx_schematic_pins=["X1.B2.PCIe_Gen4_TX0_P"],
tx_schematic_differential_pins=["U1.AL25.PCIe_Gen4_TX0_CAP_N"],
rx_schematic_differentialial_pins=["X1.B3.PCIe_Gen4_TX0_N"],
ibis_tx_component_name="Spec_Model",
use_ibis_buffer=False,
differential=True,
bit_pattern="random_bit_count=2.5e3 random_seed=1",
unit_interval="31.25ps",
use_convolution=True,
analyze=True,
design_name="AMI",
)
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Added design 'AMI' of type Circuit Design.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Circuit Design correctly changed.
PyAEDT INFO: Solving design setup Transient
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Circuit Design correctly changed.
PyAEDT INFO: Design setup Transient solved correctly in 0.0h 0.0m 19.0s
[10]:
circuit.save_project()
PyAEDT INFO: Project Project_AXC Saved correctly
[10]:
True
Create virtual compliance report#
Initialize the VirtualCompliance
class and set up the main project information needed to generate the report.
[11]:
template = os.path.join(download_folder, "pcie_gen5_templates", "main.json")
[12]:
v = VirtualCompliance(circuit.desktop_class, str(template))
Customize project and design#
Define the path to the project file and the design names to be used in each report generation.
[13]:
v.project_file = circuit.project_file
v.reports["insertion losses"].design_name = "LNA"
v.reports["return losses"].design_name = "LNA"
v.reports["common mode return losses"].design_name = "LNA"
v.reports["tdr from circuit"].design_name = "TDR"
v.reports["eye1"].design_name = "AMI"
v.reports["eye3"].design_name = "AMI"
v.parameters["erl"].design_name = "LNA"
v.specs_folder = os.path.join(download_folder, "readme_pictures")
Define trace names#
Change the trace name with projects and users. Reuse the compliance template and update traces accordingly.
[14]:
v.reports["insertion losses"].traces = insertion
[15]:
v.reports["return losses"].traces = return_diff
[16]:
v.reports["common mode return losses"].traces = return_comm
[17]:
v.reports["eye1"].traces = eye_curve_tx
v.reports["eye3"].traces = eye_curve_tx
v.reports["tdr from circuit"].traces = tdr_probe_name
v.parameters["erl"].trace_pins = [
[
"X1.A5.PCIe_Gen4_RX1_P",
"X1.A6.PCIe_Gen4_RX1_N",
"U1.AR25.PCIe_Gen4_RX1_P",
"U1.AP25.PCIe_Gen4_RX1_N",
],
[7, 8, 18, 17],
]
Generate PDF report#
Generate the reports and produce a PDF report.
[18]:
v.create_compliance_report()
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Exporting Touchstone C:\Users\ansys\AppData\Local\Temp\tmpobhzvuhb.ansys\pcie_compliance\project\Project_AXC.pyaedt\LNA\MySetupAuto_MySetupAuto.S32p
PyAEDT INFO: Touchstone correctly exported to C:\Users\ansys\AppData\Local\Temp\tmpobhzvuhb.ansys\pcie_compliance\project\Project_AXC.pyaedt\LNA\MySetupAuto_MySetupAuto.S32p
PyAEDT INFO: Exporting Touchstone C:\Users\ansys\AppData\Local\Temp\tmpobhzvuhb.ansys\pcie_compliance\project\Project_AXC.pyaedt\LNA\MySetupAuto_MySetupAuto.S32p
PyAEDT INFO: Touchstone correctly exported to C:\Users\ansys\AppData\Local\Temp\tmpobhzvuhb.ansys\pcie_compliance\project\Project_AXC.pyaedt\LNA\MySetupAuto_MySetupAuto.S32p
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: Creating report insertion losses for trace dB(S(X1_PCIe_Gen4_RX0_P,U1_PCIe_Gen4_RX0_P))
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Parsing C:/Users/ansys/AppData/Local/Temp/tmpobhzvuhb.ansys/pcie_compliance/project/Project_AXC.aedt.
PyAEDT INFO: File C:/Users/ansys/AppData/Local/Temp/tmpobhzvuhb.ansys/pcie_compliance/project/Project_AXC.aedt correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: aedt file load time 0.20584630966186523
PyAEDT INFO: Report Plot_QHZRVF created successfully.
PyAEDT INFO: Checking lines violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report insertion losses for trace dB(S(X1_PCIe_Gen4_RX0_P,U1_PCIe_Gen4_RX0_P))
PyAEDT INFO: Creating report insertion losses for trace dB(S(X1_PCIe_Gen4_RX1_P,U1_PCIe_Gen4_RX1_P))
PyAEDT INFO: Report Plot_HKYKEE created successfully.
PyAEDT INFO: Checking lines violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report insertion losses for trace dB(S(X1_PCIe_Gen4_RX1_P,U1_PCIe_Gen4_RX1_P))
PyAEDT INFO: Creating report insertion losses for trace dB(S(X1_PCIe_Gen4_RX2_P,U1_PCIe_Gen4_RX2_P))
PyAEDT INFO: Report Plot_4PGREN created successfully.
PyAEDT INFO: Checking lines violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report insertion losses for trace dB(S(X1_PCIe_Gen4_RX2_P,U1_PCIe_Gen4_RX2_P))
PyAEDT INFO: Creating report insertion losses for trace dB(S(X1_PCIe_Gen4_RX3_P,U1_PCIe_Gen4_RX3_P))
PyAEDT INFO: Report Plot_C3ZJRX created successfully.
PyAEDT INFO: Checking lines violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report insertion losses for trace dB(S(X1_PCIe_Gen4_RX3_P,U1_PCIe_Gen4_RX3_P))
PyAEDT INFO: Creating report return losses
PyAEDT INFO: Report Plot_PU9WLQ created successfully.
PyAEDT INFO: Checking lines violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report return losses
PyAEDT INFO: Creating report common mode return losses
PyAEDT INFO: Report Plot_H78SEO created successfully.
PyAEDT INFO: Checking lines violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report common mode return losses
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: Creating report tdr from circuit for trace O(A10482:zdiff)
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Report Plot_WT6WQA created successfully.
PyAEDT INFO: Checking lines violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report tdr from circuit for trace O(A10482:zdiff)
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: Creating report eye1 for trace b_input_29090
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Report Plot_DE6SAL created successfully.
PyAEDT INFO: Checking eye violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Adding eye measurements.
PyAEDT INFO: Successfully parsed report eye1 for trace b_input_29090
PyAEDT INFO: Creating report eye3 for trace b_input_29090
PyAEDT INFO: Report Plot_6BWY85 created successfully.
PyAEDT INFO: Checking eye violations
PyAEDT INFO: Solution Data Correctly Loaded.
PyAEDT INFO: Successfully parsed report eye3 for trace b_input_29090
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
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.12.dev0.
PyAEDT INFO: Returning found Desktop session with PID 5148!
PyAEDT INFO: Project Project_AXC set to active.
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: ModelerCircuit class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: ModelerNexxim class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Report has been saved in C:/Users/ansys/AppData/Local/Temp/tmpobhzvuhb.ansys/pcie_compliance/project/Project_AXC.pyaedt\PCIExpress_0MWAQ3\compliance_test.pdf
[18]:
'C:/Users/ansys/AppData/Local/Temp/tmpobhzvuhb.ansys/pcie_compliance/project/Project_AXC.pyaedt\\PCIExpress_0MWAQ3\\compliance_test.pdf'
Release AEDT#
[19]:
h3d.release_desktop()
# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory.
time.sleep(3)
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.
[20]:
temp_folder.cleanup()
Download this example
Download this example as a Jupyter Notebook or as a Python script.