diff --git a/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG b/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG index 29d2c8a4..df23f58b 100644 Binary files a/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG and b/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG differ diff --git a/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG b/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG index 461ee488..0a9f3c9a 100644 Binary files a/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG and b/docs/_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG differ diff --git a/docs/_static/img/examples/04_spice_cosimulation/linear_phase_power_mapping.png b/docs/_static/img/examples/04_spice_cosimulation/linear_phase_power_mapping.png index 216086f4..71122a89 100644 Binary files a/docs/_static/img/examples/04_spice_cosimulation/linear_phase_power_mapping.png and b/docs/_static/img/examples/04_spice_cosimulation/linear_phase_power_mapping.png differ diff --git a/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG b/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG index f677b762..2286f0a9 100644 Binary files a/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG and b/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG differ diff --git a/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG b/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG index 57a3f8e0..8778d67d 100644 Binary files a/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG and b/docs/_static/img/examples/04_spice_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG differ diff --git a/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot.PNG b/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot.PNG index 77984d51..81927b41 100644 Binary files a/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot.PNG and b/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot.PNG differ diff --git a/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_full.PNG b/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_full.PNG index 394b4a08..2398af93 100644 Binary files a/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_full.PNG and b/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_full.PNG differ diff --git a/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_power_resistance.PNG b/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_power_resistance.PNG index 2d902cce..212442e1 100644 Binary files a/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_power_resistance.PNG and b/docs/_static/img/examples/04_spice_cosimulation/simple_transient_plot_power_resistance.PNG differ diff --git a/docs/_static/resources/fsic_2024_presentation.odp b/docs/_static/resources/fsic_2024_presentation.odp index 976bebb6..6e1e6d0f 100644 Binary files a/docs/_static/resources/fsic_2024_presentation.odp and b/docs/_static/resources/fsic_2024_presentation.odp differ diff --git a/docs/examples/03a_sax_cocotb_cosimulation.py b/docs/examples/03a_sax_cocotb_cosimulation.py index 17351f47..0c6169b2 100644 --- a/docs/examples/03a_sax_cocotb_cosimulation.py +++ b/docs/examples/03a_sax_cocotb_cosimulation.py @@ -339,7 +339,8 @@ "output_amplitude_array_0_abs", "output_amplitude_array_0_phase_deg", ], - y_axis_title_list=["e1 Phase", "o3 Amplitude", "o3 Phase"], + y_label=[r"$|e1|$ (abs)", r"$|o3|$ (abs)", r"$deg(o3)$"], + x_label="time (ns)", ) simple_ideal_o3_mzi_2x2_plots.savefig( "../_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG" @@ -355,7 +356,7 @@ "output_amplitude_array_1_abs", "output_amplitude_array_1_phase_deg", ], - y_axis_title_list=["e1 Phase", "o4 Amplitude", "o4 Phase"], + y_label=[r"|e1| (abs)", r"$|o4|$ (abs)", r"$deg(o4)$"], ) simple_ideal_o4_mzi_2x2_plots.savefig( "../_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG" @@ -787,7 +788,7 @@ def switch_lattice_phase_array_to_state( "out_o_" + str(port_i) + "_abs", "out_o_" + str(port_i) + "_phase_deg", ], - y_axis_title_list=[ + y_label=[ "e1,5 Phase", "o" + str(port_i) + "Amplitude", "o" + str(port_i) + "Phase", diff --git a/docs/examples/04_spice_cosimulation/04_spice_cosimulation.py b/docs/examples/04_spice_cosimulation/04_spice_cosimulation.py index 595d5742..f7e727ef 100644 --- a/docs/examples/04_spice_cosimulation/04_spice_cosimulation.py +++ b/docs/examples/04_spice_cosimulation/04_spice_cosimulation.py @@ -105,6 +105,9 @@ # So this is very cool, we have our device model giving us electrical data when connected to the geometrical design parameters. What effect does half that resistance have on the driver though? We need to first create a SPICE model of our circuit. One of the main complexities now is that we need to create a mapping between our component models and `hdl21` which is dependent on our device model extraction. Another functionality we might desire is to validate physical electrical connectivity by simulating the circuit accordingly. +from gdsfactory.export import to_svg +to_svg(our_short_resistive_mzi_2x2_2x2_phase_shifter) + # ## Extracting the SPICE circuit and assigning model parameters # We will exemplify how `piel` microservices enable the extraction and configuration of the SPICE circuit. This is done by implementing a SPICE netlist construction backend to the circuit composition functions in `sax`, and is composed in a way that is then integrated into `hdl21` or any SPICE-based solver through the `VLSIR` `Netlist`. @@ -320,6 +323,12 @@ # So this seems equivalent to the gdsfactory component representation. We can now continue to implement our SPICE simulation. +# ### Flow Automation + +piel.flows.extract_component_spice_from_netlist( + component=straight_heater_metal_simple(), +) + # ## `SPICE` Integration # We have seen in the previous example how to integrate digital-driven data with photonic circuit steady-state simulations. However, this is making a big assumption: whenever digital codes are applied to photonic components, the photonic component responds immediately. We need to account for the electrical load physics in order to perform more accurate simulation models of our systems. @@ -388,7 +397,7 @@ class OperatingPointTb: # We can now run the simulation using `ngpsice`. Make sure you have it installed, although this will be automatic in the *IIC-OSIC-TOOLS* environment: -results = piel.run_simulation(simulation=simple_operating_point_simulation) +results = piel.run_simulation(sistraight_heater_metal_simple()mulation=simple_operating_point_simulation) results # ```python @@ -478,15 +487,12 @@ class TransientTb: # We can plot our simulation data accordingly: -simple_transient_plot = piel.visual.plot_simple_multi_row( - data=transient_simulation_results, - x_axis_column_name="time", - row_list=[ - "v(xtop.vpulse_p)", - "i(v.xtop.vvpulse)", - ], - y_axis_title_list=["v(v.xtop.vvpulse)", "i(v.xtop.vvpulse)", "o4 Phase"], -) +simple_transient_plot = piel.visual.plot_simple_multi_row(data=transient_simulation_results, x_axis_column_name="time", + row_list=[ + "v(xtop.vpulse_p)", + "i(v.xtop.vvpulse)", + ], y_label=["v(v.xtop.vvpulse)", "i(v.xtop.vvpulse)", + "o4 Phase"]) simple_transient_plot.savefig( "../../_static/img/examples/04_spice_cosimulation/simple_transient_plot.PNG" ) @@ -537,15 +543,11 @@ class TransientTb: # | 39 | 39 | 0.00295 | -0.61 | 0.00061 | -0.0003721 | 1000 | # -simple_transient_plot_power_resistance = piel.visual.plot_simple_multi_row( - data=transient_simulation_results, - x_axis_column_name="time", - row_list=[ +simple_transient_plot_power_resistance = piel.visual.plot_simple_multi_row(data=transient_simulation_results, + x_axis_column_name="time", row_list=[ "resistance(xtop.vpulse)", "power(xtop.vpulse)", - ], - y_axis_title_list=[r"resistance ($\Omega$)", r"power ($W$)"], -) + ], y_label=[r"resistance ($\Omega$)", r"power ($W$)"]) simple_transient_plot_power_resistance.savefig( "../../_static/img/examples/04_spice_cosimulation/simple_transient_plot_power_resistance.PNG" ) @@ -571,18 +573,14 @@ class TransientTb: # A full visualisation of the signal is including the cumulative energy use: -simple_transient_plot_full = piel.visual.plot_simple_multi_row( - data=transient_simulation_results, - x_axis_column_name="time", - row_list=[ +simple_transient_plot_full = piel.visual.plot_simple_multi_row(data=transient_simulation_results, + x_axis_column_name="time", row_list=[ "v(xtop.vpulse_p)", "i(v.xtop.vvpulse)", "resistance(xtop.vpulse)", "power(xtop.vpulse)", "energy_consumed(xtop.vpulse)", - ], - y_axis_title_list=[r"$V$", r"$A$", r"$\Omega$", r"$W$", r"$J$"], -) + ], y_label=[r"$V$", r"$A$", r"$\Omega$", r"$W$", r"$J$"]) simple_transient_plot_full.savefig( "../../_static/img/examples/04_spice_cosimulation/simple_transient_plot_full.PNG" ) @@ -703,13 +701,13 @@ def linear_phase_mapping(power_w: float) -> float: simple_ideal_o3_mzi_2x2_plots = piel.visual.plot_simple_multi_row( data=transient_simulation_results, - x_axis_column_name="time", - row_list=[ + x_axis_column_name="time", row_list=[ "power(xtop.vpulse)", "output_amplitude_array_0_abs", "output_amplitude_array_0_phase_deg", ], - y_axis_title_list=["e1 Power", "o3 Amplitude", "o3 Phase"], + y_label=[r"$|e1|$ (W)", r"$|o3|$ (abs)", r"$deg(o3)$"], + x_label="time (s)" ) simple_ideal_o3_mzi_2x2_plots.savefig( "../../_static/img/examples/04_spice_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG" @@ -717,16 +715,12 @@ def linear_phase_mapping(power_w: float) -> float: # ![simple_ideal_o3_mzi_2x2_plots](../../_static/img/examples/04_spice_cosimulation/simple_ideal_o3_mzi_2x2_plots.PNG) -simple_ideal_o4_mzi_2x2_plots = piel.visual.plot_simple_multi_row( - data=transient_simulation_results, - x_axis_column_name="time", - row_list=[ +simple_ideal_o4_mzi_2x2_plots = piel.visual.plot_simple_multi_row(data=transient_simulation_results, + x_axis_column_name="time", row_list=[ "power(xtop.vpulse)", "output_amplitude_array_1_abs", "output_amplitude_array_1_phase_deg", - ], - y_axis_title_list=["e1 Phase", "o4 Amplitude", "o4 Phase"], -) + ], y_label=["e1 Phase", "o4 Amplitude", "o4 Phase"]) simple_ideal_o4_mzi_2x2_plots.savefig( "../../_static/img/examples/04_spice_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG" ) diff --git a/docs/examples/04_spice_cosimulation/netlist.raw b/docs/examples/04_spice_cosimulation/netlist.raw index 9c3436d3..1f5046d1 100644 Binary files a/docs/examples/04_spice_cosimulation/netlist.raw and b/docs/examples/04_spice_cosimulation/netlist.raw differ diff --git a/docs/examples/07_full_flow_demo_electronic_photonic/07_full_flow_demo_electronic_photonic.py b/docs/examples/07_full_flow_demo_electronic_photonic/07_full_flow_demo_electronic_photonic.py index 2840c31c..48465e78 100644 --- a/docs/examples/07_full_flow_demo_electronic_photonic/07_full_flow_demo_electronic_photonic.py +++ b/docs/examples/07_full_flow_demo_electronic_photonic/07_full_flow_demo_electronic_photonic.py @@ -104,6 +104,10 @@ def create_switch_fabric(): chain_3_mode_lattice_circuit = create_switch_fabric() chain_3_mode_lattice_circuit +from gdsfactory.export import to_svg + +to_svg(chain_3_mode_lattice_circuit) + # ## 2. Extracting our optical-to-electronic control logic truth table @@ -334,7 +338,7 @@ def create_switch_fabric(): "output_amplitude_array_1_abs", "output_amplitude_array_1_phase_deg", ], - y_axis_title_list=["e1 Phase", "o4 Amplitude", "o4 Phase"], + y_label=["e1 Phase", "o4 Amplitude", "o4 Phase"], ) simple_ideal_o4_mzi_2x2_plots.savefig( "../_static/img/examples/03a_sax_active_cosimulation/simple_ideal_o4_mzi_2x2_plots.PNG" diff --git a/docs/examples/designs/inverter1/config.json b/docs/examples/designs/inverter1/config.json deleted file mode 100644 index 14baa629..00000000 --- a/docs/examples/designs/inverter1/config.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "meta": { - "version": 2, - "flow": [ - "Yosys.Synthesis", - "OpenROAD.CheckSDCFiles", - "OpenROAD.Floorplan", - "OpenROAD.TapEndcapInsertion", - "OpenROAD.GeneratePDN", - "OpenROAD.IOPlacement", - "OpenROAD.GlobalPlacement", - "OpenROAD.RepairDesign", - "OpenROAD.DetailedPlacement", - "OpenROAD.GlobalRouting", - "OpenROAD.DetailedRouting", - "OpenROAD.FillInsertion", - "Magic.StreamOut", - "Magic.DRC", - "Checker.MagicDRC", - "Magic.SpiceExtraction", - "Netgen.LVS", - "Checker.LVS" - ] - }, - "DESIGN_NAME": "inverter", - "VERILOG_FILES": "dir::src/*.v", - "CLOCK_PORT": null, - "FP_SIZING": "absolute", - "DIE_AREA": [ - 0, - 0, - 50, - 50 - ], - "PL_TARGET_DENSITY": 0.75, - "FP_PDN_AUTO_ADJUST": false, - "FP_PDN_VPITCH": 25, - "FP_PDN_HPITCH": 25, - "FP_PDN_VOFFSET": 5, - "FP_PDN_HOFFSET": 5, - "//": "With those two defined, the repair design step should do nothing:", - "RSZ_DONT_TOUCH_RX": "^in$", - "RSZ_DONT_TOUCH_LIST": [ - "out" - ] -} \ No newline at end of file diff --git a/docs/examples/designs/inverter1/src/inverter.v b/docs/examples/designs/inverter1/src/inverter.v deleted file mode 100644 index 6441b4c5..00000000 --- a/docs/examples/designs/inverter1/src/inverter.v +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 Matt Venn -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -`default_nettype none -module inverter ( - input wire in, - output out ); - - assign out = !in; - -endmodule diff --git a/docs/sections/environment/index.rst b/docs/sections/environment/index.rst index 5903ee3a..b7a47286 100644 --- a/docs/sections/environment/index.rst +++ b/docs/sections/environment/index.rst @@ -20,6 +20,15 @@ If you want to enter the corresponding `nix-shell` environment, you can run the $ piel environment activate +It will print: + +.. code-block:: + + # Please run this in your shell: + nix shell github:efabless/nix-eda#{ngspice,xschem,verilator,yosys} github:efabless/openlane2 nixpkgs#verilog nixpkgs#gtkwave + +This is because, I believe, for security reasons it is very difficult to automatically enter a nix shell directly from python or a subprocess. + `OpenLane 2 via nix `__ have recently released another way to package their `python`-driven ``Openlane 2`` digital chip layout flow. We have previously had issues reproducibly building the `docker` configuration, and because most users are likely to use these tools for developing their chips rather than distributing software, `nix `__ might be well suited for these applications. .. include:: nix/development_installation.rst diff --git a/docs/sections/environment/nix/development_installation.rst b/docs/sections/environment/nix/development_installation.rst index b3b394fd..96e026ba 100644 --- a/docs/sections/environment/nix/development_installation.rst +++ b/docs/sections/environment/nix/development_installation.rst @@ -15,13 +15,13 @@ Assuming you already have ``piel`` installed in a local environment, you can sim piel environment install-nix # To install nix piel environment install-openlane # To install openlane -To enter the nix environment, run: +To enter the nix environment that uses all the tools used within ``piel``, run: .. code:: bash - piel environment activate-piel-nix - # piel environment activate-openlane-nix # if you want to enter the openlane one instead + piel environment activate +An important thing to note is that, for `openlane` to work properly, its `nix` configured binaries need to be untouched. This means we need to make sure that the virtualenviron System requirements ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/piel/flows/__init__.py b/piel/flows/__init__.py index 3b0be0f0..163bc1ba 100644 --- a/piel/flows/__init__.py +++ b/piel/flows/__init__.py @@ -1,8 +1,9 @@ +from .analog_photonic import extract_component_spice_from_netlist from .digital_logic import ( generate_verilog_and_verification_from_truth_table, read_simulation_data_to_truth_table, run_verification_simulation_for_design, - layout_truth_table + layout_truth_table, ) from .digital_electro_optic import ( add_truth_table_phase_to_bit_data, diff --git a/piel/flows/analog_photonic.py b/piel/flows/analog_photonic.py new file mode 100644 index 00000000..a398ee51 --- /dev/null +++ b/piel/flows/analog_photonic.py @@ -0,0 +1,45 @@ +""" +This file contains the design flow from going from a photonic component into an analogue. +""" +import sys +import hdl21 as h +from piel.types import CircuitComponent, PathTypes +from piel.integration import ( + gdsfactory_netlist_with_hdl21_generators, + construct_hdl21_module, +) + + +def extract_component_spice_from_netlist( + component: CircuitComponent, output_path: PathTypes = sys.stdout, fmt: str = "spice" +): + """ + This function extracts the SPICE netlist from a component definition and writes it to a file. The function uses + the HDL21 library to generate the SPICE netlist from the component's netlist. The netlist is then written to a + file in the specified format. + + Args: + component (CircuitComponent): The component for which to extract the SPICE netlist. + output_path (str): The path to the output file where the SPICE netlist will be written. + fmt (str, optional): The format in which the netlist will be written. Defaults to "spice". + + Returns: + None + """ + # Get the netlist of the component + component_netlist = component.get_netlist( + allow_multiple=True, exclude_port_types="optical" + ) + + # + spice_component_netlist = gdsfactory_netlist_with_hdl21_generators( + component_netlist + ) + + hdl21_module = construct_hdl21_module(spice_netlist=spice_component_netlist) + + h.netlist( + hdl21_module, + output_path, + fmt="spice", + ) diff --git a/piel/types/__init__.py b/piel/types/__init__.py index 2fbbe987..9ac4ff07 100644 --- a/piel/types/__init__.py +++ b/piel/types/__init__.py @@ -40,6 +40,7 @@ SParameterCollection, ) from .electronic import HVAMetricsType, LNAMetricsType, ElectronicCircuitComponent +from .integration import CircuitComponent from .materials import ( MaterialReferenceType, MaterialReferencesTypes, diff --git a/piel/types/integration.py b/piel/types/integration.py new file mode 100644 index 00000000..28708214 --- /dev/null +++ b/piel/types/integration.py @@ -0,0 +1,4 @@ +from .electronic import ElectronicCircuitComponent +from .photonic import PhotonicCircuitComponent + +CircuitComponent = ElectronicCircuitComponent | PhotonicCircuitComponent diff --git a/piel/visual/auto_plot_multiple.py b/piel/visual/auto_plot_multiple.py index fdf1323a..646948de 100644 --- a/piel/visual/auto_plot_multiple.py +++ b/piel/visual/auto_plot_multiple.py @@ -1,9 +1,7 @@ -import matplotlib import matplotlib.pyplot as plt import numpy as np import pandas as pd -import pathlib - +from typing import List, Union, Tuple, Optional __all__ = [ "plot_simple", @@ -12,34 +10,37 @@ def plot_simple( - x_data: np.array, - y_data: np.array, - label: str | None = None, - ylabel: str | None = None, - xlabel: str | None = None, - fig: plt.Figure | None = None, - ax: plt.Axes | None = None, + x_data: np.ndarray, + y_data: np.ndarray, + label: Optional[str] = None, + ylabel: Optional[str] = None, + xlabel: Optional[str] = None, + fig: Optional[plt.Figure] = None, + ax: Optional[plt.Axes] = None, + title: Optional[str] = None, *args, **kwargs -): +) -> Tuple[plt.Figure, plt.Axes]: """ - Plot a simple line graph. The desire of this function is just to abstract the most basic data representation whilst - keeping the flexibility of the matplotlib library. The goal would be as well that more complex data plots can be - constructed from a set of these methods. + Plot a simple line graph. This function abstracts the basic data representation while + keeping the flexibility of the matplotlib library. Args: - x_data (np.array): X axis data. - y_data (np.array): Y axis data. - label (str, optional): Label for the plot. Defaults to None. - ylabel (str, optional): Y axis label. Defaults to None. - xlabel (str, optional): X axis label. Defaults to None. - fig (plt.Figure, optional): Matplotlib figure. Defaults to None. - ax (plt.Axes, optional): Matplotlib axes. Defaults to None. + x_data (np.ndarray): X axis data. + y_data (np.ndarray): Y axis data. + label (Optional[str], optional): Label for the plot. Defaults to None. + ylabel (Optional[str], optional): Y axis label. Defaults to None. + xlabel (Optional[str], optional): X axis label. Defaults to None. + fig (Optional[plt.Figure], optional): Matplotlib figure. Defaults to None. + ax (Optional[plt.Axes], optional): Matplotlib axes. Defaults to None. + title (Optional[str], optional): Title of the plot. Defaults to None. + *args: Additional arguments passed to plt.plot(). + **kwargs: Additional keyword arguments passed to plt.plot(). Returns: - plt: Matplotlib plot. + Tuple[plt.Figure, plt.Axes]: The figure and axes of the plot. """ - if (ax is None) and (fig is None): + if fig is None and ax is None: fig, ax = plt.subplots() ax.plot(x_data, y_data, label=label, *args, **kwargs) @@ -50,52 +51,81 @@ def plot_simple( if xlabel is not None: ax.set_xlabel(xlabel) + if title is not None: + ax.set_title(title) + if label is not None: - # This function appends to the existing plt legend ax.legend() + # Rotate x-axis labels for better fit + for label in ax.get_xticklabels(): + label.set_rotation(45) + label.set_ha("right") + + fig.tight_layout() + return fig, ax def plot_simple_multi_row( data: pd.DataFrame, x_axis_column_name: str = "t", - row_list: list | None = None, - y_axis_title_list: list | None = None, - x_axis_title: str | None = None, -): + row_list: Optional[List[str]] = None, + y_label: Optional[List[str]] = None, + x_label: Optional[str] = None, + titles: Optional[List[str]] = None, + subplot_spacing: float = 0.15, +) -> plt.Figure: """ - Plot multiple rows of data on the same plot. Each row is a different line. Each row is a different y axis. The x - axis is the same for all rows. The y axis title is the same for all rows. + Plot multiple rows of data on separate subplots, sharing the same x-axis. Args: data (pd.DataFrame): Data to plot. - x_axis_column_name (str, optional): Column name of the x axis. Defaults to "t". - row_list (list, optional): List of column names to plot. Defaults to None. - y_axis_title_list (list, optional): List of y axis titles. Defaults to None. - x_axis_title (str, optional): Title of the x axis. Defaults to None. + x_axis_column_name (str, optional): Column name of the x-axis. Defaults to "t". + row_list (Optional[List[str]], optional): List of column names to plot. Defaults to None. + y_label (Optional[List[str]], optional): List of Y-axis titles for each subplot. Defaults to None. + x_label (Optional[str], optional): Title of the x-axis. Defaults to None. + titles (Optional[List[str]], optional): Titles for each subplot. Defaults to None. + subplot_spacing (float, optional): Spacing between subplots. Defaults to 0.3. Returns: - plt: Matplotlib plot. + plt.Figure: The matplotlib figure containing the subplots. """ - x = data[x_axis_column_name] - y_array = [] + if row_list is None: + raise ValueError("row_list must be provided") - if y_axis_title_list is None: - y_axis_title_list = row_list + x_data = data[x_axis_column_name] + y_data_list = [data[row] for row in row_list] + + if y_label is None: + y_label = row_list + + if titles is None: + titles = [""] * len(row_list) row_amount = len(row_list) - for row_name in row_list: - y_array.append(data[row_name]) + fig, axes = plt.subplots(row_amount, 1, sharex=True, figsize=(8, row_amount * 2)) + + if row_amount == 1: + axes = [axes] + + for i, (ax, y_data, y_label, title) in enumerate( + zip(axes, y_data_list, y_label, titles) + ): + ax.plot(x_data, y_data) + ax.grid(True) + ax.set_ylabel(y_label) + ax.set_title(title) - fig, axes = plt.subplots(row_amount, 1, sharex=True) + if x_label is not None: + axes[-1].set_xlabel(x_label) - for i in range(len(row_list)): - axes[i].plot(x, y_array[i]) - axes[i].grid(True) - axes[i].set(ylabel=y_axis_title_list[i]) + # Rotate x-axis labels for better fit + for label in axes[-1].get_xticklabels(): + label.set_rotation(45) + label.set_ha("right") - # TODO Xaxis title - # TODO align all ytitles + fig.tight_layout() + plt.subplots_adjust(hspace=subplot_spacing) # Add space between subplots - return plt + return fig diff --git a/pyproject.toml b/pyproject.toml index 2cc1d09e..e0180bab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,9 +33,11 @@ repository = "https://github.com/daquintero/piel" version = "0.0.56" [tool.poetry.dependencies] +# CORE PACKAGES # Click = ">=7.0" amaranth = "^0.4.0" amaranth-yosys = "^0.40.0.0.post94" +# DEVELOPMENT ONLY amaranth_driven_flow = {path = "docs/examples/designs/amaranth_driven_flow", develop = true, optional = true} black = {version = "24.3.0", optional = true} bokeh = {version = "*", optional = true} @@ -60,7 +62,7 @@ jupyter-bokeh = {version = "*", optional = true} jupyter-core = {version = "*", optional = true} jupyter-packaging = {version = ">=0.7.9", optional = true} jupyterlab = {version = "^4.0.0", optional = true} -jupytext = "1.15.0" +jupytext = {version = "1.15.0", optional = true} kfactory = {version = "*", extras = ["git", "ipy"], optional = true} kweb = {version = "^2.0.1", optional = true} matplotlib = {version = "*"} @@ -68,7 +70,11 @@ myst-parser = {version = "*", optional = true} nbsphinx = {version = "*", optional = true} networkx = "^3.1" numpy = "^1.24.4" -openlane = "2.0.9" +# NIX-INSTALLED PACKAGES +# These packages link directly within the nix-environment +# Only used for development +# Do not install unless you know what you're doing. +openlane = {version = "^2.0.0", optional = true} pandas = "^1.5.3" pandoc = {version = "*", optional = true} poetry = {version = "1.8.2", optional = true} # I am not happy with this being here @@ -140,6 +146,9 @@ dev = [ "tox", "watchdog" ] +nonix = [ + "openlane" +] [tool.poetry.scripts] piel = "piel.cli:main"