aboutsummaryrefslogtreecommitdiff
path: root/pyspecs
diff options
context:
space:
mode:
Diffstat (limited to 'pyspecs')
-rw-r--r--pyspecs/__init__.py0
-rw-r--r--pyspecs/draw_graph_from_json.py145
-rw-r--r--pyspecs/pyspecs_example.ipynb682
-rw-r--r--pyspecs/specs.py236
-rw-r--r--pyspecs/specsparse.py301
5 files changed, 1364 insertions, 0 deletions
diff --git a/pyspecs/__init__.py b/pyspecs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pyspecs/__init__.py
diff --git a/pyspecs/draw_graph_from_json.py b/pyspecs/draw_graph_from_json.py
new file mode 100644
index 0000000..78a3c64
--- /dev/null
+++ b/pyspecs/draw_graph_from_json.py
@@ -0,0 +1,145 @@
+import sys
+import json
+import html
+import graphviz
+
+def shorten(s, length=8):
+ if type(s) == list:
+ return "[...]"
+ s = str(s)
+ if len(s) > length:
+ return s[:length-3]
+ else:
+ return s
+
+def edge_label(name, net):
+ bgcolors = {
+ "default": "white",
+ "OANALOG": "turquoise",
+ "EANALOG": "orange",
+ }
+ try:
+ bgcolor = bgcolors[net["type"]]
+ except:
+ bgcolor = bgcolors["default"]
+
+ short_name = name
+ if short_name.startswith('ROOT/'):
+ short_name = short_name[5:]
+
+ attributes = [f'<table style="rounded" border="0" cellborder="1" cellspacing="10" bgcolor="{bgcolor}" cellpadding="4" align="center">']
+ attributes.append(f'<tr><td bgcolor="grey91" port="head"><b>{html.escape(short_name)}</b></td></tr>')
+
+ attributes.append(f'<tr><td bgcolor="{bgcolor}" port="middle" border="0"><table border="0" cellborder="1" cellspacing="0">')
+ # attributes.append(f'<tr><td bgcolor="{bgcolor}"><table border="0" cellborder="1" cellspacing="0" cellpadding="0">')
+ attributes.append(f'<tr><td bgcolor="grey91">Type</td><td bgcolor="white">{net["type"]}</td></tr>')
+ attributes.append(f'<tr><td bgcolor="grey91">Bidirectional</td><td bgcolor="white">{"true" if net["bidirectional"] else "false"}</td></tr>')
+ attributes.append(f'<tr><td bgcolor="grey91">Size</td><td bgcolor="white">{net["size"]}</td></tr>')
+ attributes.append(f'<tr><td bgcolor="grey91">Readers</td><td bgcolor="white">{net["readers"]}</td></tr>')
+ attributes.append(f'<tr><td bgcolor="grey91">Writers</td><td bgcolor="white">{net["writers"]}</td></tr>')
+ attributes.append(f'</table></td></tr>')
+
+ attributes.append('</table>')
+ return f'"{name}" [label=<{"".join(attributes)}> shape=none fillcolor="{bgcolor}" margin="0.05"]'
+
+
+
+def node_label(element):
+ attributes = ['<table border="0" cellborder="1" cellspacing="0" cellpadding="4" align="center">']
+
+ short_name = element["name"]
+ if short_name.startswith('ROOT/'):
+ short_name = short_name[5:]
+ # Header
+ attributes.append(f'<tr><td bgcolor="grey91"><b>{html.escape(short_name)}</b></td></tr>')
+
+ # Nets
+ attributes.append(f'<tr><td bgcolor="darkslategray2"><table border="0" cellborder="1" cellspacing="8">')
+ if 'nets' in element and element['nets']:
+ nets = ''.join(f'<tr><td bgcolor="white" cellpadding="4" port="{html.escape(str(i))}">{str(i)}</td></tr>' for i,net in enumerate(element['nets']))
+ attributes.append(f'{nets}')
+ else:
+ attributes.append('<tr><td border="0" cellpadding="0">&empty;</td></tr>')
+ attributes.append(f'</table></td></tr>')
+
+ # Args
+ attributes.append(f'<tr><td bgcolor="palegreen"><table border="0" cellborder="1" cellpadding="2" cellspacing="0">')
+ if 'args' in element and element['args']:
+ args = ''.join(f'<tr><td bgcolor="white" cellpadding="4">{html.escape(shorten(v["value"]))}</td></tr>' for v in element['args'])
+ attributes.append(f'{args}')
+ else:
+ attributes.append('<tr><td border="0" cellpadding="0">&empty;</td></tr>')
+ attributes.append(f'</table></td></tr>')
+
+ # Kwargs
+ attributes.append(f'<tr><td bgcolor="wheat"><table border="0" cellborder="1" cellpadding="2" cellspacing="0">')
+ if 'kwargs' in element and element['kwargs']:
+ kwargs = ''.join(f'<tr><td bgcolor="grey91">{html.escape(k)}</td><td bgcolor="white">{html.escape(shorten(v["value"]))}</td></tr>' for k, v in element['kwargs'].items())
+ attributes.append(f'{kwargs}')
+ else:
+ attributes.append('<tr><td border="0" cellpadding="0">&empty;</td></tr>')
+ attributes.append(f'</table></td></tr>')
+
+ attributes.append('</table>')
+ return f'"{element["name"]}" [label=<{"".join(attributes)}> shape=none margin="0"]'
+
+def edge(element):
+ if 'nets' in element:
+ for i,net in enumerate(element['nets']):
+ # yield f'"{element["name"]}":"{str(i)}" -- "{net}":"head"'
+ yield f'"{element["name"]}":"{str(i)}" -> "{net}":"middle"[ arrowhead = none ]'
+
+def generate_graph(description):
+ result = []
+ result.append('digraph G {')
+ # result.append(' fontname="Helvetica,Arial,sans-serif"')
+ # result.append(' node [fontname="Helvetica,Arial,sans-serif"]')
+ # result.append(' edge [fontname="Helvetica,Arial,sans-serif"]')
+ result.append(' fontname="Cascadia,Courrier,mono"')
+ result.append(' node [fontname="Cascadia,Courrier,mono"]')
+ result.append(' edge [fontname="Cascadia,Courrier,mono"]')
+ result.append(' layout="sfdp"')
+ result.append(' overlap=false')
+ result.append(' splines=curved')
+
+ for name, net in description["nets"].items():
+ result.append(' ' + edge_label(name, net))
+
+ for element in description["elements"]:
+ result.append(' ' + node_label(element))
+ result.extend(' ' + e for e in edge(element))
+
+ result.append('}')
+ return '\n'.join(result)
+
+def draw_graph(json_file, out_file):
+ with open(json_file) as f:
+ description = json.load(f)
+
+ dot_src = generate_graph(description)
+ # print(dot_src)
+ graph = graphviz.Source(dot_src)
+ graph.render(outfile=out_file)
+ print(f'Results written to {out_file}')
+
+def main():
+ try:
+ json_file = sys.argv[1]
+ except IndexError:
+ print('First and only argument should be a JSON description of the circuit')
+ try:
+ out_file = sys.argv[2]
+ except IndexError:
+ out_file = json_file + ".png"
+
+ with open(json_file) as f:
+ description = json.load(f)
+
+ dot_src = generate_graph(description)
+ # print(dot_src)
+ graph = graphviz.Source(dot_src)
+ graph.render(outfile=out_file)
+ print(f'Results written to {out_file}')
+
+if __name__ == '__main__':
+ main()
diff --git a/pyspecs/pyspecs_example.ipynb b/pyspecs/pyspecs_example.ipynb
new file mode 100644
index 0000000..1ca337d
--- /dev/null
+++ b/pyspecs/pyspecs_example.ipynb
@@ -0,0 +1,682 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "fdfbe616-3959-44bb-a62e-cccb87a8bee7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import sys\n",
+ "path_to_specs = \"../\" # This needs to change if the notebook is used somewhere else\n",
+ "sys.path.insert(0, os.path.realpath(path_to_specs))\n",
+ "\n",
+ "from pyspecs import specs\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "# Dependencies:\n",
+ "# pyDigitalWaveTools 1.2 (https://pypi.org/project/pyDigitalWaveTools/)\n",
+ "# pandas\n",
+ "# numpy"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2bf6e3a5-ef41-4ae1-bc90-50eb142ef0dd",
+ "metadata": {},
+ "source": [
+ "### Calling the simulation\n",
+ "\n",
+ "There are two ways of doing it. The first one is the default, with the arguments shown in the function call.\n",
+ "\n",
+ "The second way uses custom_arguments as a dictionary of arguments. All arguments there are the same that can be passed to specs, with the same syntax except the - or -- before commands.\n",
+ "\n",
+ "You can put the verbose variable as True to see the full SPECS output"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "b5a1dc45-58ae-4892-92ce-3e0a54b57a23",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Executing SPECS from python. . .\n",
+ "Command: ../specs -f ../circuits/add_drop_pyspecs.cir -o ../traces/delete_me --abstol 1e-08 --reltol 0.0001\n",
+ "Execution finished with code: 0\n",
+ "Time in ms: 43.5395\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Simulating in the basic way\n",
+ "netlist_file = \"../circuits/add_drop_pyspecs.cir\"\n",
+ "output_file = \"../traces/delete_me\"\n",
+ "time_ms, std_out = specs.simulate(netlist_file=netlist_file, simulator_directory=path_to_specs, abstol=1e-8, reltol=1e-4, output_file=output_file, verbose=False)\n",
+ "print(\"Time in ms: \", time_ms)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "2143ab2e-f911-49db-a8dd-f6efb264bae6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Executing SPECS from python. . .\n",
+ "Command: ../specs -o ../traces/delete_me --abstol 1e-08 --reltol 0.0001 -f ../circuits/add_drop_pyspecs.cir\n",
+ "Execution finished with code: 0\n",
+ "Time in ms: 87.333\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Simulating in the custom way. This can be used for more advanced use-cases or for \n",
+ "# new features that weren't completely implemented in the wrapper yet\n",
+ "\n",
+ "test_dict = {\"f\":\"../circuits/add_drop_pyspecs.cir\", \"o\":\"../traces/delete_me\", \"abstol\":1e-8, \"reltol\":1e-4}\n",
+ "time_ms, std_out = specs.simulate(custom_arguments=test_dict, simulator_directory=path_to_specs, verbose=False)\n",
+ "print(\"Time in ms: \", time_ms)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6ea09e11-30b8-474e-9d20-4197201e74d0",
+ "metadata": {},
+ "source": [
+ "### Having a non-hierarchical dataframe as output\n",
+ "\n",
+ "This data structure has everything in the same table directly. We chose to separate the data in two dataframes:\n",
+ "- df_probes: contains the probe data (optical power, field, phase, wavelength)\n",
+ "- df_pdet: contains the photodetector data (electric current and time)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "1987d33f-6b5a-4ce5-8151-19086d691bca",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "--- Contents of simulation ---\n",
+ "In df_pdet\n",
+ "In df_probes\n",
+ "\t time\n",
+ "\t PROBE{ROOT/N00001}/power\n",
+ "\t PROBE{ROOT/N00001}/abs\n",
+ "\t PROBE{ROOT/N00001}/phase\n",
+ "\t PROBE{ROOT/N00002}/power\n",
+ "\t PROBE{ROOT/N00002}/abs\n",
+ "\t PROBE{ROOT/N00002}/phase\n",
+ "\t PROBE{ROOT/N00003}/power\n",
+ "\t PROBE{ROOT/N00003}/abs\n",
+ "\t PROBE{ROOT/N00003}/phase\n",
+ "\t PROBE{ROOT/N00004}/power\n",
+ "\t PROBE{ROOT/N00004}/abs\n",
+ "\t PROBE{ROOT/N00004}/phase\n",
+ "\t PROBE{ROOT/add}/power\n",
+ "\t PROBE{ROOT/add}/abs\n",
+ "\t PROBE{ROOT/add}/phase\n",
+ "\t PROBE{ROOT/drop}/power\n",
+ "\t PROBE{ROOT/drop}/abs\n",
+ "\t PROBE{ROOT/drop}/phase\n",
+ "\t PROBE{ROOT/in}/power\n",
+ "\t PROBE{ROOT/in}/abs\n",
+ "\t PROBE{ROOT/in}/phase\n",
+ "\t PROBE{ROOT/out}/power\n",
+ "\t PROBE{ROOT/out}/abs\n",
+ "\t PROBE{ROOT/out}/phase\n",
+ "\t ROOT/PROBE1/power\n",
+ "\t ROOT/PROBE1/abs\n",
+ "\t ROOT/PROBE1/phase\n",
+ "\t ROOT/PROBE2/power\n",
+ "\t ROOT/PROBE2/abs\n",
+ "\t ROOT/PROBE2/phase\n",
+ "\t ROOT/PROBE3/power\n",
+ "\t ROOT/PROBE3/abs\n",
+ "\t ROOT/PROBE3/phase\n",
+ "Type of simulation: TD\n",
+ "------------------------------\n"
+ ]
+ }
+ ],
+ "source": [
+ "(df_probes,df_pdet) = specs.parse_vcd_dataframe(output_file + '.vcd')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "0e91dc0f-8e8c-4e25-921b-fe1e7a46f687",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "<Figure size 432x288 with 1 Axes>"
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "y = df_probes['PROBE{ROOT/out}/power']\n",
+ "if specs.verify_wl_sweep(df_probes):\n",
+ " x = df_probes['wavelength']\n",
+ "else:\n",
+ " x = df_probes['time']\n",
+ "\n",
+ "if not specs.verify_wl_sweep(df_probes):\n",
+ " plt.step(x,y,where='post')\n",
+ "else:\n",
+ " plt.plot(x,y)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "1cc73fee",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>time</th>\n",
+ " <th>PROBE{ROOT/N00001}/power</th>\n",
+ " <th>PROBE{ROOT/N00001}/abs</th>\n",
+ " <th>PROBE{ROOT/N00001}/phase</th>\n",
+ " <th>PROBE{ROOT/N00002}/power</th>\n",
+ " <th>PROBE{ROOT/N00002}/abs</th>\n",
+ " <th>PROBE{ROOT/N00002}/phase</th>\n",
+ " <th>PROBE{ROOT/N00003}/power</th>\n",
+ " <th>PROBE{ROOT/N00003}/abs</th>\n",
+ " <th>PROBE{ROOT/N00003}/phase</th>\n",
+ " <th>...</th>\n",
+ " <th>PROBE{ROOT/out}/phase</th>\n",
+ " <th>ROOT/PROBE1/power</th>\n",
+ " <th>ROOT/PROBE1/abs</th>\n",
+ " <th>ROOT/PROBE1/phase</th>\n",
+ " <th>ROOT/PROBE2/power</th>\n",
+ " <th>ROOT/PROBE2/abs</th>\n",
+ " <th>ROOT/PROBE2/phase</th>\n",
+ " <th>ROOT/PROBE3/power</th>\n",
+ " <th>ROOT/PROBE3/abs</th>\n",
+ " <th>ROOT/PROBE3/phase</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>0.000000e+00</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>...</td>\n",
+ " <td>0.000000e+00</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000e+00</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>5.000000e-10</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.022500</td>\n",
+ " <td>0.150000</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>...</td>\n",
+ " <td>0.000000e+00</td>\n",
+ " <td>0.977500</td>\n",
+ " <td>0.988686</td>\n",
+ " <td>0.000000e+00</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>1.0</td>\n",
+ " <td>1.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>5.020000e-10</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.000000</td>\n",
+ " <td>0.022500</td>\n",
+ " <td>0.150000</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.022500</td>\n",
+ " <td>0.150000</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>0.000000e+00</td>\n",
+ " <td>0.977500</td>\n",
+ " <td>0.988686</td>\n",
+ " <td>0.000000e+00</td>\n",
+ " <td>0.000506</td>\n",
+ " <td>0.022500</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>1.0</td>\n",
+ " <td>1.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>5.040000e-10</td>\n",
+ " <td>0.021994</td>\n",
+ " <td>0.148303</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.087986</td>\n",
+ " <td>0.296625</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.022500</td>\n",
+ " <td>0.150000</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>-6.316716e-15</td>\n",
+ " <td>0.934007</td>\n",
+ " <td>0.966441</td>\n",
+ " <td>-6.316716e-15</td>\n",
+ " <td>0.000506</td>\n",
+ " <td>0.022500</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>1.0</td>\n",
+ " <td>1.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>5.060000e-10</td>\n",
+ " <td>0.021994</td>\n",
+ " <td>0.148303</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.087986</td>\n",
+ " <td>0.296625</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.087986</td>\n",
+ " <td>0.296625</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>-6.316716e-15</td>\n",
+ " <td>0.934007</td>\n",
+ " <td>0.966441</td>\n",
+ " <td>-6.316716e-15</td>\n",
+ " <td>0.001980</td>\n",
+ " <td>0.044494</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>1.0</td>\n",
+ " <td>1.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>...</th>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1246</th>\n",
+ " <td>2.990000e-09</td>\n",
+ " <td>0.000569</td>\n",
+ " <td>0.023863</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000557</td>\n",
+ " <td>0.023593</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000557</td>\n",
+ " <td>0.023593</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000013</td>\n",
+ " <td>0.003579</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000013</td>\n",
+ " <td>0.003539</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1247</th>\n",
+ " <td>2.992000e-09</td>\n",
+ " <td>0.000544</td>\n",
+ " <td>0.023326</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000532</td>\n",
+ " <td>0.023062</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000557</td>\n",
+ " <td>0.023593</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000012</td>\n",
+ " <td>0.003499</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000013</td>\n",
+ " <td>0.003539</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1248</th>\n",
+ " <td>2.994000e-09</td>\n",
+ " <td>0.000544</td>\n",
+ " <td>0.023326</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000532</td>\n",
+ " <td>0.023062</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000532</td>\n",
+ " <td>0.023062</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000012</td>\n",
+ " <td>0.003499</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000012</td>\n",
+ " <td>0.003459</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1249</th>\n",
+ " <td>2.996000e-09</td>\n",
+ " <td>0.000520</td>\n",
+ " <td>0.022801</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000508</td>\n",
+ " <td>0.022543</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000532</td>\n",
+ " <td>0.023062</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000012</td>\n",
+ " <td>0.003420</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000012</td>\n",
+ " <td>0.003459</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1250</th>\n",
+ " <td>2.998000e-09</td>\n",
+ " <td>0.000520</td>\n",
+ " <td>0.022801</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000508</td>\n",
+ " <td>0.022543</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>0.000508</td>\n",
+ " <td>0.022543</td>\n",
+ " <td>1.570796</td>\n",
+ " <td>...</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000012</td>\n",
+ " <td>0.003420</td>\n",
+ " <td>-3.141593e+00</td>\n",
+ " <td>0.000011</td>\n",
+ " <td>0.003381</td>\n",
+ " <td>-3.141593</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " <td>0.0</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "<p>1251 rows × 34 columns</p>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " time PROBE{ROOT/N00001}/power PROBE{ROOT/N00001}/abs \\\n",
+ "0 0.000000e+00 0.000000 0.000000 \n",
+ "1 5.000000e-10 0.000000 0.000000 \n",
+ "2 5.020000e-10 0.000000 0.000000 \n",
+ "3 5.040000e-10 0.021994 0.148303 \n",
+ "4 5.060000e-10 0.021994 0.148303 \n",
+ "... ... ... ... \n",
+ "1246 2.990000e-09 0.000569 0.023863 \n",
+ "1247 2.992000e-09 0.000544 0.023326 \n",
+ "1248 2.994000e-09 0.000544 0.023326 \n",
+ "1249 2.996000e-09 0.000520 0.022801 \n",
+ "1250 2.998000e-09 0.000520 0.022801 \n",
+ "\n",
+ " PROBE{ROOT/N00001}/phase PROBE{ROOT/N00002}/power \\\n",
+ "0 0.000000 0.000000 \n",
+ "1 0.000000 0.022500 \n",
+ "2 0.000000 0.022500 \n",
+ "3 1.570796 0.087986 \n",
+ "4 1.570796 0.087986 \n",
+ "... ... ... \n",
+ "1246 1.570796 0.000557 \n",
+ "1247 1.570796 0.000532 \n",
+ "1248 1.570796 0.000532 \n",
+ "1249 1.570796 0.000508 \n",
+ "1250 1.570796 0.000508 \n",
+ "\n",
+ " PROBE{ROOT/N00002}/abs PROBE{ROOT/N00002}/phase \\\n",
+ "0 0.000000 0.000000 \n",
+ "1 0.150000 1.570796 \n",
+ "2 0.150000 1.570796 \n",
+ "3 0.296625 1.570796 \n",
+ "4 0.296625 1.570796 \n",
+ "... ... ... \n",
+ "1246 0.023593 1.570796 \n",
+ "1247 0.023062 1.570796 \n",
+ "1248 0.023062 1.570796 \n",
+ "1249 0.022543 1.570796 \n",
+ "1250 0.022543 1.570796 \n",
+ "\n",
+ " PROBE{ROOT/N00003}/power PROBE{ROOT/N00003}/abs \\\n",
+ "0 0.000000 0.000000 \n",
+ "1 0.000000 0.000000 \n",
+ "2 0.022500 0.150000 \n",
+ "3 0.022500 0.150000 \n",
+ "4 0.087986 0.296625 \n",
+ "... ... ... \n",
+ "1246 0.000557 0.023593 \n",
+ "1247 0.000557 0.023593 \n",
+ "1248 0.000532 0.023062 \n",
+ "1249 0.000532 0.023062 \n",
+ "1250 0.000508 0.022543 \n",
+ "\n",
+ " PROBE{ROOT/N00003}/phase ... PROBE{ROOT/out}/phase ROOT/PROBE1/power \\\n",
+ "0 0.000000 ... 0.000000e+00 0.000000 \n",
+ "1 0.000000 ... 0.000000e+00 0.977500 \n",
+ "2 1.570796 ... 0.000000e+00 0.977500 \n",
+ "3 1.570796 ... -6.316716e-15 0.934007 \n",
+ "4 1.570796 ... -6.316716e-15 0.934007 \n",
+ "... ... ... ... ... \n",
+ "1246 1.570796 ... -3.141593e+00 0.000013 \n",
+ "1247 1.570796 ... -3.141593e+00 0.000012 \n",
+ "1248 1.570796 ... -3.141593e+00 0.000012 \n",
+ "1249 1.570796 ... -3.141593e+00 0.000012 \n",
+ "1250 1.570796 ... -3.141593e+00 0.000012 \n",
+ "\n",
+ " ROOT/PROBE1/abs ROOT/PROBE1/phase ROOT/PROBE2/power ROOT/PROBE2/abs \\\n",
+ "0 0.000000 0.000000e+00 0.000000 0.000000 \n",
+ "1 0.988686 0.000000e+00 0.000000 0.000000 \n",
+ "2 0.988686 0.000000e+00 0.000506 0.022500 \n",
+ "3 0.966441 -6.316716e-15 0.000506 0.022500 \n",
+ "4 0.966441 -6.316716e-15 0.001980 0.044494 \n",
+ "... ... ... ... ... \n",
+ "1246 0.003579 -3.141593e+00 0.000013 0.003539 \n",
+ "1247 0.003499 -3.141593e+00 0.000013 0.003539 \n",
+ "1248 0.003499 -3.141593e+00 0.000012 0.003459 \n",
+ "1249 0.003420 -3.141593e+00 0.000012 0.003459 \n",
+ "1250 0.003420 -3.141593e+00 0.000011 0.003381 \n",
+ "\n",
+ " ROOT/PROBE2/phase ROOT/PROBE3/power ROOT/PROBE3/abs ROOT/PROBE3/phase \n",
+ "0 0.000000 0.0 0.0 0.0 \n",
+ "1 0.000000 1.0 1.0 0.0 \n",
+ "2 -3.141593 1.0 1.0 0.0 \n",
+ "3 -3.141593 1.0 1.0 0.0 \n",
+ "4 -3.141593 1.0 1.0 0.0 \n",
+ "... ... ... ... ... \n",
+ "1246 -3.141593 0.0 0.0 0.0 \n",
+ "1247 -3.141593 0.0 0.0 0.0 \n",
+ "1248 -3.141593 0.0 0.0 0.0 \n",
+ "1249 -3.141593 0.0 0.0 0.0 \n",
+ "1250 -3.141593 0.0 0.0 0.0 \n",
+ "\n",
+ "[1251 rows x 34 columns]"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_probes"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "64c2170f-f879-43d2-ae1f-850ebf667601",
+ "metadata": {},
+ "source": [
+ "### Helper to recover data\n",
+ "\n",
+ "As SPECS is not a sampled simulator, but rather event-driven, unchanged values will not appear in the dataframe.\n",
+ "\n",
+ "To make it a bit easier to recover a value by time, we created the 'find_by_time' function that automates\n",
+ "some of the recovery for you. \n",
+ "\n",
+ "NOTE: if you want to do it for multiple values, it is better to implement it using the Pandas framework in your own way. Check the Pandas dataframe.ffill() function. Using find_by_time in loops will result in very slow python code."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "d042f4a5-de7d-425d-b772-f245e6346871",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.07090737655723536"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "target_time = 2.5668e-9\n",
+ "specs.find_by_time(df_probes,'PROBE{ROOT/N00001}/power',target_time)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "75ede147-1232-46f5-b3b1-4a9dc75a28b7",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/pyspecs/specs.py b/pyspecs/specs.py
new file mode 100644
index 0000000..ca0d6f9
--- /dev/null
+++ b/pyspecs/specs.py
@@ -0,0 +1,236 @@
+import numpy as np
+import matplotlib.pyplot as plt
+import pandas as pd
+import subprocess
+import time
+from .specsparse import *
+
+"""
+Ideas:
+ - generate value files
+ - generate netlists based on graph representations
+ - simulate
+ - paralel run
+ - others?
+
+"""
+
+default_settings = {
+ "o": "traces/delete_me.vcd",
+ "abstol": 1e-8,
+ "reltol": 1e-4
+}
+
+
+
+def create_arguments_netlist(netlist_file, output_file, abstol, reltol):
+ """ Function called by specs.simulate(), generates
+ the argument string for the command-line tool.
+
+ Returns:
+ arguments (list of str):
+ Formatted list of arguments required by SPECS
+ """
+
+ arguments_dictionary = { "f":netlist_file,
+ "o":output_file,
+ "abstol":abstol,
+ "reltol":reltol
+ }
+
+ arguments = create_arguments_dictionary(arguments_dictionary)
+
+ return arguments
+
+
+def create_arguments_dictionary(arguments_dictionary:dict):
+ """ Function called by specs.simulate(), generates
+ the argument string for the command-line tool.
+
+ Args:
+ arguments_dictionary (dict):
+ The keys of this dictionary are keywords expected
+ by SPECS, while their contents hold their value.
+
+ Example: {"f":"circuit.txt", "o":"data.vcd","
+ "abstol":1e-8, "reltol":1e-4
+ }
+
+ Returns:
+ arguments (list of str):
+ Formatted list of arguments required by SPECS
+ """
+
+ arguments = []
+
+ for keyword_string in arguments_dictionary:
+ value = arguments_dictionary[keyword_string]
+
+ # Long keywords are prepended with double dash
+ if len(keyword_string) > 1:
+ keyword_string = "--" + keyword_string
+ else:
+ keyword_string = "-" + keyword_string
+
+ arguments.append(keyword_string)
+ if value is not None:
+ arguments.append(str(value))
+
+ return arguments
+
+def run_and_time(simulator_command, arguments, verbose):
+ """Function that runs SPECS and times it.
+
+ This is BLOCKING execution, so not intended for paralel
+ usage. For this case, use par_run().
+
+ Args:
+ simulator_command (str):
+ The command to call SPECS comprised of
+ its directory and simulator name. Ex: ./specs
+ arguments (str):
+ Command-line arguments passed to SPECS
+ verbose (bool):
+ If true, will print SPECS output to the Python shell
+ Returns:
+ time_ms:
+ Execution time of SPECS for that run, in miliseconds (ms)
+ """
+
+ print("Executing SPECS from python. . .")
+
+ #spec_env = {"SC_COPYRIGHT_MESSAGE":"DISABLE"}
+
+ process_call = simulator_command + arguments
+
+ tic_ns = time.perf_counter_ns()
+ print("Command: ", ' '.join(process_call))
+ #p = subprocess.Popen(process_call, env=spec_env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ p = subprocess.Popen(process_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ specs_stdout, specs_stderr = p.communicate()
+ p.wait()
+ toc_ns = time.perf_counter_ns()
+
+ time_ms = (toc_ns - tic_ns)*1e-6
+
+ specs_return_code = p.returncode
+ print("Execution finished with code: ", specs_return_code)
+
+ if (not specs_return_code == 0) or (verbose):
+ print("SPECS had an error. Here is the full output: ")
+ for line in specs_stdout.splitlines():
+ print(line.decode(encoding='utf-8', errors='ignore'))
+
+ return time_ms, (specs_return_code, specs_stdout)
+
+def simulate(netlist_file=None,
+ custom_arguments=None,
+ simulator_directory="",
+ verbose=False,
+ **kwargs):
+ """Function that call SPECS from the Python shell
+
+ Args:
+ netlist_file (str):
+ File containing the netlist description
+ of the circuit to simulate. Should contain
+ a simulation directive within it (.tran, .dc, .op).
+
+ The netlist is a text file that can be:
+ - Created using the KiCAD library of components in
+ its schematic editor (eeschema)
+ - Created and edited by hand by knowing the syntax
+ - Generated proceduraly using scripts. It's a basic .txt
+
+ It can be achieved with custom arguments by defining
+ {"f": "path/to/file"} in the dict custom_arguments.
+
+ Defaults to "".
+
+ output_file (str, optional):
+ VCD file containing all the recorded events.
+ Can be read with GTKWAVE or parsed with parse_vcd()
+ Defaults to "traces/delete_me.vcd".
+
+ abstol (double, optional):
+ Field absolute tolerance for propagating events in the simulator.
+ Is relevant when running circuits with feedback.
+
+ It can be achieved with custom arguments by defining
+ {"abstol": 1e-8} in the dict custom_arguments.
+
+ Defaults to 1e-8.
+
+ reltol (double, optional):
+ Field relative tolerance for propagating events in the simulator.
+ Is relevant when running circuits with feedback.
+
+ It can be achieved with custom arguments by defining
+ {"reltol": 1e-4} in the dict custom_arguments.
+
+ Defaults to 1e-4.
+
+ custom_arguments (dict, optional):
+ <<< ADVANCED USE >>>
+ It is possible to call SPECS with many other arguments.
+ You can consult them by running specs without arguments
+ in the command line interface.
+
+ If custom_arguments is defined, you need to specify
+ the netlist as a part of the dictionary.
+
+ There is no specific order that the arguments must follow.
+
+ simulator_directory (str, optional):
+ SPECS can either be included in the PATH variable
+ or exist as a standalone executable. In the latter case,
+ it is necessary to define its location.
+
+ Examples:
+ simulator_directory = "./"
+ Means that the executable is in the same
+ directory as this script."
+ simulator_directory = "/home/user/Documents/"
+ Means that the SPECS executable is in that
+ specific path.
+
+ Defaults to "".
+
+ verbose (bool, optional):
+ If true, will print all the outputs generated by SPECS
+ to the Python shell.
+ """
+
+ assert((netlist_file is not None) or (custom_arguments is not None))
+
+
+ if 'abstol' in kwargs:
+ abstol = kwargs['abstol']
+ else:
+ abstol = default_settings['abstol']
+
+ if 'reltol' in kwargs:
+ reltol = kwargs['reltol']
+ else:
+ reltol = default_settings['reltol']
+
+ if 'output_file' in kwargs:
+ output_file = kwargs['output_file']
+ else:
+ output_file = default_settings['o']
+
+ arguments = []
+ if (netlist_file is not None):
+ arguments = create_arguments_netlist(netlist_file, output_file, abstol, reltol)
+
+ if (custom_arguments is not None):
+ current_settings = default_settings.copy()
+ current_settings.update(custom_arguments) # overwrites defaults with the user input where needed
+ arguments += create_arguments_dictionary(current_settings)
+
+ simulator_name = "specs"
+ simulator_command = [simulator_directory + simulator_name]
+
+ outputs = run_and_time(simulator_command, arguments, verbose)
+
+ return outputs
diff --git a/pyspecs/specsparse.py b/pyspecs/specsparse.py
new file mode 100644
index 0000000..91f4f4f
--- /dev/null
+++ b/pyspecs/specsparse.py
@@ -0,0 +1,301 @@
+from pyDigitalWaveTools.vcd.parser import VcdParser
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+import json
+
+def parse_vcd_dataframe(filename):
+ """Parses a SPECS VCD file and returns simulation data as a DataFrame.
+
+ Args:
+ filename (str):
+ The path to the VCD file.
+
+ Returns:
+ simulation_data_df:
+ The simulation data with signals as columns and time/wavelength as rows.
+ """
+ with open(filename) as vcd_file:
+ vcd = VcdParser()
+ vcd.parse(vcd_file)
+ data = vcd.scope.toJson()
+
+ # print(json.dumps(data, indent=4, sort_keys=True))
+ simulation_data_df = pd.DataFrame() # initialize empty dataframe
+
+ top_level_name = 'SystemC'
+ top_scope = None
+ for child in data['children']:
+ if is_scope(child) and child['name'] == top_level_name:
+ top_scope = child
+ break
+ if top_scope is None:
+ raise RuntimeError(f'Top level scope not found in {filename}')
+
+ assert('name' in top_scope.keys())
+ assert('type' in top_scope.keys())
+ assert('children' in top_scope.keys())
+ assert(top_scope['type']['name'] == 'struct')
+
+ scopes = []
+ probes = []
+ pdets = []
+ values = []
+ for child in top_scope['children']:
+ if is_scope(child):
+ scopes.append(child)
+ if is_probe(child):
+ probes.append(child)
+ if is_pdet(child):
+ pdets.append(child)
+ else:
+ values.append(child)
+
+ # print(f'top level values: {[x["name"] for x in values]}')
+ # print(f'top level scopes: {[x["name"] for x in scopes]}')
+ # print(f'top level probes: {[x["name"] for x in probes]}')
+
+ is_wl_sweep = check_wl_sweep(probes)
+ has_wl = False
+ save_wl = False
+
+ df_probes = pd.DataFrame()
+ if len(probes) > 0:
+ for i,probe in enumerate(probes):
+ probe_name = probe['name']
+ for j,signal in enumerate(probe['children']):
+ signal_name = signal['name']
+ full_name = probe_name + '/' + signal_name
+
+ save_wl = is_wl_sweep and not has_wl
+ if (not signal_name=='wavelength' or (signal_name=='wavelength' and save_wl)):
+ current_signal_df = pd.DataFrame()
+ current_signal_df['tick'] = vcd_get_ticks_vector(signal)
+
+ if signal_name=='wavelength':
+ has_wl = False # this variable set to true will save WL vector only once
+ full_name = signal_name # wavelength should be saved independent of probe
+
+ current_signal_df[full_name] = vcd_get_data_vector(signal)
+
+ if i==0 and j==0:
+ df_probes = current_signal_df
+ else:
+ df_probes = df_probes.merge(current_signal_df, how='outer')
+
+ df_probes = df_probes.sort_values('tick')
+ df_probes = df_probes.reset_index(drop=True)
+
+ if not is_wl_sweep: # making sure that time goes first
+ df_probes.insert(0, 'time', df_probes['tick'] * vcd_get_multiplier(vcd))
+ else: # making sure that wl goes first
+ first_column = df_probes.pop('wavelength')
+ df_probes.insert(0, 'wavelength', first_column)
+
+ # we don't need ticks anymore
+ df_probes = df_probes.drop(columns=['tick'])
+
+ # getting rid of the NaNs: for time domain we repeat the last valid value,
+ # for freq domain we interpolate linearly
+ if is_wl_sweep:
+ df_probes = df_probes.drop(df_probes.loc[df_probes['wavelength']== 0].index.values)
+ df_probes = df_probes.interpolate()
+ else:
+ df_probes = df_probes.fillna(method='ffill')
+
+ # Same with pdetectors
+ df_pdet = pd.DataFrame()
+ if not is_wl_sweep and len(pdets) > 0:
+ for i,pdet in enumerate(pdets):
+ pdet_name = pdet['name']
+ for j,signal in enumerate(pdet['children']):
+ if signal["type"]["name"] == "struct":
+ continue
+ signal_name = signal['name']
+ full_name = pdet_name + '/' + signal_name
+
+ # Add data to a temporary DF
+ current_signal_df = pd.DataFrame()
+ current_signal_df['tick'] = vcd_get_ticks_vector(signal)
+ current_signal_df[full_name] = vcd_get_data_vector(signal)
+
+ if df_pdet.empty:
+ df_pdet = current_signal_df
+ else:
+ df_pdet = df_pdet.merge(current_signal_df, how='outer')
+
+ df_pdet = df_pdet.sort_values('tick')
+ df_pdet = df_pdet.reset_index(drop=True)
+ if False:
+ # Add a point next to first value from photodetector
+ # to ensure 'step' appearance (as its more correct)
+ print(df_pdet.head())
+ df_pdet.loc[-1] = df_pdet.loc[0]
+ df_pdet.loc[0, 'tick'] = df_pdet.loc[1]['tick']
+ df_pdet = df_pdet.sort_values('tick')
+ df_pdet = df_pdet.reset_index(drop=True)
+ print(df_pdet.head())
+
+ # making sure that time goes first
+ df_pdet.insert(0, 'time', df_pdet['tick'] * vcd_get_multiplier(vcd))
+
+ # we don't need ticks anymore
+ df_pdet = df_pdet.drop(columns=['tick'])
+
+ # getting rid of the NaNs: for time domain we repeat the last valid value,
+ df_pdet = df_pdet.fillna(method='ffill')
+
+
+ print('--- Contents of simulation ---')
+ print ('In df_pdet')
+ for col in list(df_pdet.columns):
+ print('\t',col)
+ print ('In df_probes')
+ for col in list(df_probes.columns):
+ print('\t',col)
+ if is_wl_sweep:
+ print('Type of simulation: FD')
+ else:
+ print('Type of simulation: TD')
+
+ print('------------------------------')
+
+ return df_probes, df_pdet
+
+def verify_wl_sweep(simulation_data):
+ """Checks the type of simulation based on the processed data.
+
+ Args:
+ simulation_data (DataFrame):
+ Data recovered using parse_vcd_xxxx methods.
+
+ Returns:
+ is_wl_sweep(bool):
+ True if it is a wavelength sweep, False otherwise.
+ """
+ is_wl_sweep = False
+
+ if isinstance(simulation_data,pd.DataFrame):
+ is_wl_sweep = list(simulation_data.columns)[0] == 'wavelength'
+ else:
+ print('Error: Invalid simulation data')
+
+ return is_wl_sweep
+
+def find_by_time(simulation_data,colname,time):
+ """Finds specific time values from the processed data
+ Useful when you want to a datapoint of a specific moment
+ but there was no transition there so it is not necessarily
+ contained in the data frame.
+
+ As it is an event-based simulator, it means that if
+ the exact time that you want is not there, the closest
+ previous value is the correct one, and this function gets it
+ automatically for you.
+
+ Args:
+ simulation_data (DataFrame):
+ Data recovered using parse_vcd_xxxx methods.
+ colname (string):
+ Name of the column that you want to extract data from.
+ time (float):
+ Time that you want to get your sample.
+
+ Returns:
+ value(float):
+ Sample value at the specified time..
+ """
+ # first check if exact match is present
+ # else, gets most recent value smaller than
+ # the specified time
+
+ value = None
+
+ exact_match_df = simulation_data.loc[simulation_data['time'] == time]
+ if exact_match_df.empty:
+ previous_match_df = simulation_data.loc[simulation_data['time'] <= time]
+ previous_match_df = previous_match_df.tail(1)
+ value = previous_match_df[colname].values[0]
+ else:
+ value = exact_match_df[colname].values[0]
+
+ return value
+
+######### The next functions are intended for internal use #########
+
+def is_scope(data):
+ return data['type']['name'] == 'struct'
+
+def is_pdet(data):
+ if not is_scope(data):
+ return False
+
+ allowed_attrs = ["readout", "readout_no_interference"]
+ for child in data['children']:
+ if child['name'] not in allowed_attrs:
+ return False
+ return True
+
+def is_probe(data):
+ if not is_scope(data):
+ return False
+
+ allowed_attrs = ["wavelength", "power", "abs", "phase", "real", "imag"]
+ for child in data['children']:
+ if is_scope(child):
+ # Probe contains only values
+ return False
+ base_name = child['name']
+ # For MLprobe ignore what's after the "@"
+ # If there is no "@", it remains the full name
+ i_last_at = base_name.rfind('@')
+ if i_last_at > 0:
+ base_name = base_name[0:i_last_at]
+ if base_name not in allowed_attrs:
+ return False
+ return True
+
+def check_wl_sweep(probes):
+ for probe in probes:
+ # see if wavelength is part of probe attributes
+ attrs = ["wavelength", "power", "abs", "phase"]
+ lengths = {a:-1 for a in attrs}
+ for signal in probe['children']:
+ for a in attrs:
+ if signal['name'] == a:
+ lengths[a] = len( signal['data'] )
+
+ match = False
+ if lengths[attrs[1]] != -1:
+ match = lengths[attrs[0]] == lengths[attrs[1]]
+ if lengths[attrs[2]] != -1:
+ match = lengths[attrs[0]] == lengths[attrs[2]]
+
+ if match and lengths[attrs[0]] != 1:
+ return True
+ return False
+
+def vcd_get_data_vector(signal):
+ data = np.array([float(tup[1][1::]) for tup in signal['data']])
+ return data
+
+def vcd_get_ticks_vector(signal):
+ ticks = np.array([tup[0] for tup in signal['data']])
+ return ticks
+
+def vcd_get_multiplier(vcd):
+ timescale_list = vcd.timescale.split()
+ base_scale = float(timescale_list[0])
+ unit = timescale_list[1][0]
+
+ multipliers = {
+ 's': 1.0,
+ 'm': 1e-3,
+ 'u': 1e-6,
+ 'n': 1e-9,
+ 'p': 1e-12,
+ 'f': 1e-15,
+ }
+
+ # returns value in seconds
+ return base_scale * multipliers[unit]