diff --git a/docs/examples/09a_model_lna/09a_model_lna.py b/docs/examples/09a_model_lna/09a_model_lna.py index 468394ef..439d2171 100644 --- a/docs/examples/09a_model_lna/09a_model_lna.py +++ b/docs/examples/09a_model_lna/09a_model_lna.py @@ -16,21 +16,30 @@ # + -import pandas as pd - -data = pd.read_csv("data/example_dc_response.csv") - -data[data["driver_b_v_set"] == 1.6] -data["driver_b_v_set"].unique() +# import pandas as pd + +# data = pd.read_csv("data/example_dc_response.csv") + +# data[data["driver_b_v_set"] == 1.6] +# data["driver_b_v_set"].unique() +# opp = data[["driver_b_v_set"]].drop_duplicates() +# for index, operating_point in opp.iterrows(): +# print(index) +# print(operating_point) +# a = data[(data[["driver_b_v_set"]] == operating_point).all(axis=1)] +# print(a) # - -dc_sweep = pe.extract_signal_data_from_csv( +dc_sweep = pe.extract_dc_sweeps_from_operating_point_csv( file_path="data/example_dc_response.csv", sourcemeter_voltage_current_signal_name_pairs=[ ("driver_a_v", "driver_a_i"), ("driver_b_v", "driver_b_i"), ], multimeter_signals=["measurement_a_v"], + unique_operating_point_columns=["driver_b_v_set"], ) -pe.visual.plot_dc_sweep(dc_sweep=dc_sweep) +pe.visual.plot_dc_sweep(dc_sweep=dc_sweep[0]) + +pe.visual.plot_dc_sweeps(dc_sweep_collection=dc_sweep) diff --git a/piel/experimental/__init__.py b/piel/experimental/__init__.py index b76a6bba..a6311a30 100644 --- a/piel/experimental/__init__.py +++ b/piel/experimental/__init__.py @@ -14,6 +14,8 @@ construct_multimeter_sweep_signal_from_dataframe, construct_sourcemeter_sweep_signal_from_dataframe, extract_signal_data_from_csv, + extract_signal_data_from_dataframe, + extract_dc_sweeps_from_operating_point_csv, ) from .measurements.data.propagation import ( extract_propagation_delay_from_measurement, diff --git a/piel/experimental/measurements/data/dc.py b/piel/experimental/measurements/data/dc.py index c147ab66..a1e0685c 100644 --- a/piel/experimental/measurements/data/dc.py +++ b/piel/experimental/measurements/data/dc.py @@ -9,6 +9,7 @@ from ...types import ( SourcemeterSweepMeasurementData, DCSweepMeasurementData, + DCSweepMeasurementDataCollection, MultimeterSweepVoltageMeasurementData, SourcemeterVoltageCurrentSignalNamePair, ) @@ -61,6 +62,26 @@ def construct_multimeter_sweep_signal_from_csv( signal_type: QuantityTypesDC = "voltage", **kwargs, ) -> MultimeterSweepVoltageMeasurementData: + """ + Construct a multimeter sweep signal from a CSV file. + + Parameters + ---------- + + file_path : PathTypes + The path to the CSV file. + signal_name : str + The name of the signal. + signal_type : QuantityTypesDC + The type of signal. + **kwargs + + Returns + ------- + + MultimeterSweepVoltageMeasurementData + The multimeter sweep signal + """ file = return_path(file_path) dataframe = pd.read_csv(file) @@ -84,6 +105,26 @@ def construct_multimeter_sweep_signal_from_dataframe( signal_kwargs: dict = None, **kwargs, ) -> MultimeterSweepVoltageMeasurementData: + """ + Construct a multimeter sweep signal from a dataframe. + + Parameters + ---------- + + dataframe : pd.DataFrame + The dataframe containing the multimeter sweep signal data. + signal_name : str + The name of the signal. + signal_kwargs : dict + Additional keyword arguments. + **kwargs + + Returns + ------- + + MultimeterSweepVoltageMeasurementData + The multimeter sweep signal + """ if signal_kwargs is None: signal_kwargs = {} @@ -94,17 +135,35 @@ def construct_multimeter_sweep_signal_from_dataframe( return MultimeterSweepVoltageMeasurementData(signal=signal, **kwargs) -def extract_signal_data_from_csv( - file_path: PathTypes, +def extract_signal_data_from_dataframe( + dataframe: pd.DataFrame, sourcemeter_voltage_current_signal_name_pairs: list[ SourcemeterVoltageCurrentSignalNamePair ], multimeter_signals: list[str], **kwargs, ) -> DCSweepMeasurementData: - file = return_path(file_path) - dataframe = pd.read_csv(file) + """ + Extract DC sweep data from a dataframe. + + Parameters + ---------- + dataframe : pd.DataFrame + The dataframe containing the DC sweep data. + sourcemeter_voltage_current_signal_name_pairs : list[SourcemeterVoltageCurrentSignalNamePair] + The pairs of sourcemeter voltage and current signal names. + multimeter_signals : list[str] + The multimeter signals. + **kwargs + Additional keyword arguments. + + Returns + ------- + + DCSweepMeasurementData + The DC sweep data. + """ # Iterate through the sourcemeter signals and create the sourcemeter sweep signals sourcemeter_sweep_signals = [] @@ -134,3 +193,103 @@ def extract_signal_data_from_csv( outputs=multimeter_sweep_signals, **kwargs, ) + + +def extract_signal_data_from_csv( + file_path: PathTypes, + sourcemeter_voltage_current_signal_name_pairs: list[ + SourcemeterVoltageCurrentSignalNamePair + ], + multimeter_signals: list[str], + **kwargs, +) -> DCSweepMeasurementData: + """ + Extract DC sweep data from a CSV file. + + Parameters + ---------- + file_path : PathTypes + The path to the CSV file. + sourcemeter_voltage_current_signal_name_pairs : list[SourcemeterVoltageCurrentSignalNamePair] + The pairs of sourcemeter voltage and current signal names. + multimeter_signals : list[str] + The multimeter signals. + **kwargs + Additional keyword arguments. + + Returns + ------- + + DCSweepMeasurementData + The DC sweep data. + """ + file = return_path(file_path) + dataframe = pd.read_csv(file) + return extract_signal_data_from_dataframe( + dataframe=dataframe, + sourcemeter_voltage_current_signal_name_pairs=sourcemeter_voltage_current_signal_name_pairs, + multimeter_signals=multimeter_signals, + **kwargs, + ) + + +def extract_dc_sweeps_from_operating_point_csv( + file_path: PathTypes, + sourcemeter_voltage_current_signal_name_pairs: list[ + SourcemeterVoltageCurrentSignalNamePair + ], + multimeter_signals: list[str], + unique_operating_point_columns: list[str], + **kwargs, +) -> DCSweepMeasurementDataCollection: + """ + Extract DC sweep data from a full operating point CSV file. The operating point CSV file contains the DC sweep data + for multiple operating points. The unique operating point columns are used to extract the unique operating points + from the CSV file. The DC sweep data is then extracted for each unique operating point. The DC sweep data is + returned as a DCMeasurementDataCollection. The DCMeasurementDataCollection is a list of DCMeasurementDataTypes. + + Parameters + ---------- + + file_path : PathTypes + The path to the operating point CSV file. + sourcemeter_voltage_current_signal_name_pairs : list[SourcemeterVoltageCurrentSignalNamePair] + The pairs of sourcemeter voltage and current signal names. + multimeter_signals : list[str] + The multimeter signals. + unique_operating_point_columns : list[str] + The unique operating point columns. + **kwargs + Additional keyword arguments. + + Returns + ------- + DCMeasurementDataCollection + The DC sweep data collection. + """ + file = return_path(file_path) + dataframe = pd.read_csv(file) + + # Extract the unique operating points + unique_operating_points = dataframe[ + unique_operating_point_columns + ].drop_duplicates() + + # Iterate through the unique operating points and extract the DC sweep data + dc_sweep_data = [] + + for _, operating_point in unique_operating_points.iterrows(): + operating_point_data = dataframe[ + (dataframe[unique_operating_point_columns] == operating_point).all(axis=1) + ] + + dc_sweep = extract_signal_data_from_dataframe( + dataframe=operating_point_data, + sourcemeter_voltage_current_signal_name_pairs=sourcemeter_voltage_current_signal_name_pairs, + multimeter_signals=multimeter_signals, + **kwargs, + ) + + dc_sweep_data.append(dc_sweep) + + return dc_sweep_data diff --git a/piel/experimental/types/__init__.py b/piel/experimental/types/__init__.py index 9ed4b537..568f2e62 100644 --- a/piel/experimental/types/__init__.py +++ b/piel/experimental/types/__init__.py @@ -28,6 +28,8 @@ from .measurements.data.dc import ( DCSweepMeasurementData, + DCSweepMeasurementDataCollection, + DCMeasurementDataCollection, MultimeterSweepVoltageMeasurementData, SourcemeterSweepMeasurementData, ) diff --git a/piel/experimental/types/measurements/data/dc.py b/piel/experimental/types/measurements/data/dc.py index 0d30973a..65d2f0ea 100644 --- a/piel/experimental/types/measurements/data/dc.py +++ b/piel/experimental/types/measurements/data/dc.py @@ -22,9 +22,14 @@ class DCSweepMeasurementData(MeasurementData): """ +DCSweepMeasurementDataCollection = list[DCSweepMeasurementData] + DCMeasurementDataTypes = ( DCSweepMeasurementData | MultimeterSweepVoltageMeasurementData | SourcemeterSweepMeasurementData ) -DCMeasurementDataCollection = list[DCMeasurementDataTypes] + +DCMeasurementDataCollection = ( + list[DCMeasurementDataTypes] | DCSweepMeasurementDataCollection +) diff --git a/piel/experimental/visual/__init__.py b/piel/experimental/visual/__init__.py index 91d33ec5..e72ab3c6 100644 --- a/piel/experimental/visual/__init__.py +++ b/piel/experimental/visual/__init__.py @@ -1,4 +1,4 @@ -from .dc import plot_dc_sweep +from .dc import plot_dc_sweep, plot_dc_sweeps from .propagation import ( plot_signal_propagation_measurements, plot_signal_propagation_signals, diff --git a/piel/experimental/visual/dc.py b/piel/experimental/visual/dc.py index 096cad34..8a0e454a 100644 --- a/piel/experimental/visual/dc.py +++ b/piel/experimental/visual/dc.py @@ -1,8 +1,8 @@ -from ..types import DCSweepMeasurementData -import matplotlib.pyplot as plt +from ..types import DCSweepMeasurementData, DCSweepMeasurementDataCollection +from ...visual import create_plot_containers, save -def plot_dc_sweep(dc_sweep: DCSweepMeasurementData): +def plot_dc_sweep(dc_sweep: DCSweepMeasurementData, **kwargs) -> tuple: """ Plot a DC sweep measurement. @@ -11,7 +11,33 @@ def plot_dc_sweep(dc_sweep: DCSweepMeasurementData): dc_sweep : DCMeasurementDataTypes The DC sweep measurement data to plot. """ - plt.plot( + fig, axs = create_plot_containers(container_list=[dc_sweep]) + + axs[0].plot( # dc_sweep.inputs[0].signal.signal_instances[0].values dc_sweep.outputs[0].signal.signal_instances[0].values, ) + + if kwargs["path"]: + save(fig, **kwargs) + + return fig, axs + + +def plot_dc_sweeps( + dc_sweep_collection: DCSweepMeasurementDataCollection, **kwargs +) -> tuple: + fig, axs = create_plot_containers( + container_list=dc_sweep_collection, axes_structure="overlay" + ) + + for dc_sweep_i in dc_sweep_collection: + axs[0].plot( + dc_sweep_i.inputs[0].signal.signal_instances[0].values, + dc_sweep_i.outputs[0].signal.signal_instances[0].values, + ) + + if kwargs["path"]: + save(fig, **kwargs) + + return fig, axs diff --git a/piel/visual/plot/position.py b/piel/visual/plot/position.py index 2cc6bbde..3b4b6028 100644 --- a/piel/visual/plot/position.py +++ b/piel/visual/plot/position.py @@ -23,7 +23,7 @@ def create_axes_per_figure(rows: int = 1, columns: int = 1, **kwargs) -> tuple: """ fig, axs = plt.subplots(rows, columns, **kwargs) - if rows == 1 and columns == 1: + if (rows == 1) and (columns == 1): # We always want this to be an array so we can compose easily with the rest of the code. axs = [axs] @@ -57,6 +57,7 @@ def list_to_separate_plots( def list_to_overlayed_plots(container_list: list, **kwargs) -> tuple: fig, axs = create_axes_per_figure(rows=1, columns=1, **kwargs) + return fig, axs def create_plot_containers(