diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_dw2ps_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_dw2ps_cv.nl_out.xlsx new file mode 100644 index 00000000..5582df37 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_dw2ps_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_dw2ps_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_dw2ps_iv.nl_out.xlsx new file mode 100644 index 00000000..d9d62db3 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_dw2ps_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_03v3_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_03v3_cv.nl_out.xlsx new file mode 100644 index 00000000..cbf4db35 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_03v3_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_03v3_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_03v3_iv.nl_out.xlsx new file mode 100644 index 00000000..f4527e09 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_03v3_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_06v0_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_06v0_cv.nl_out.xlsx new file mode 100644 index 00000000..7023e41c Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_06v0_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_06v0_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_06v0_iv.nl_out.xlsx new file mode 100644 index 00000000..08807f63 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nd2ps_06v0_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_03v3_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_03v3_cv.nl_out.xlsx new file mode 100644 index 00000000..1b2af926 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_03v3_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_03v3_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_03v3_iv.nl_out.xlsx new file mode 100644 index 00000000..0c964d83 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_03v3_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_06v0_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_06v0_cv.nl_out.xlsx new file mode 100644 index 00000000..5789d79c Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_06v0_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_06v0_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_06v0_iv.nl_out.xlsx new file mode 100644 index 00000000..9321fb63 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_nw2ps_06v0_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_03v3_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_03v3_cv.nl_out.xlsx new file mode 100644 index 00000000..bed97275 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_03v3_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_03v3_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_03v3_iv.nl_out.xlsx new file mode 100644 index 00000000..c45be6c1 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_03v3_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_06v0_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_06v0_cv.nl_out.xlsx new file mode 100644 index 00000000..2fc2a7c5 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_06v0_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_06v0_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_06v0_iv.nl_out.xlsx new file mode 100644 index 00000000..9cbce9b0 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_pd2nw_06v0_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_pw2dw_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_pw2dw_cv.nl_out.xlsx new file mode 100644 index 00000000..ccd29089 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_pw2dw_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/diode_pw2dw_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/diode_pw2dw_iv.nl_out.xlsx new file mode 100644 index 00000000..25d7edfc Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/diode_pw2dw_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/sc_diode_cv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/sc_diode_cv.nl_out.xlsx new file mode 100644 index 00000000..a6767fc7 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/sc_diode_cv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/0_measured_data/sc_diode_iv.nl_out.xlsx b/models/xyce/testing/regression/diode/0_measured_data/sc_diode_iv.nl_out.xlsx new file mode 100644 index 00000000..1d60cf54 Binary files /dev/null and b/models/xyce/testing/regression/diode/0_measured_data/sc_diode_iv.nl_out.xlsx differ diff --git a/models/xyce/testing/regression/diode/device_netlists/cv.spice b/models/xyce/testing/regression/diode/device_netlists/cv.spice index 4c5616d9..2637cad6 100644 --- a/models/xyce/testing/regression/diode/device_netlists/cv.spice +++ b/models/xyce/testing/regression/diode/device_netlists/cv.spice @@ -20,8 +20,8 @@ dn1 out 0 diode_nd2ps_03v3 AREA={10u*10u} .ac dec 10 1 10G .step volt -12.13 1.13 0.13 -.include "../../design.xyce" -.lib "../../sm141064.xyce" diode_typical +.include "../../../design.xyce" +.lib "../../../sm141064.xyce" diode_typical .end diff --git a/models/xyce/testing/regression/diode/device_netlists/iv.spice b/models/xyce/testing/regression/diode/device_netlists/iv.spice index 2ed78b2d..5833303b 100644 --- a/models/xyce/testing/regression/diode/device_netlists/iv.spice +++ b/models/xyce/testing/regression/diode/device_netlists/iv.spice @@ -17,12 +17,14 @@ dn1 n 0 {{device}} AREA={{area}}p ***************** .DC Vn -12.13 1.13 0.13 .STEP TEMP {{temp}} -60 200 -.print DC FORMAT=CSV file={{device}}_{{Id_sim}}_{{corner}}/simulated_{{Id_sim}}/{{i}}_simulated_A{{area}}_P{{pj}}.csv {abs(I(dn1))} +.print DC FORMAT=CSV file=diode_regr/{{device}}/simulated_iv/simulated_A{{area}}_P{{pj}}_t{{temp}}_{{corner}}.csv {abs(I(dn1))} + * .print DC FORMAT=CSV file=result.csv {N(dn1:is)} -.include "../../../../../design.xyce" -.lib "../../../../../sm141064.xyce" diode_{{corner}} -.end \ No newline at end of file +.include "../../../design.xyce" +.lib "../../../sm141064.xyce" diode_{{corner}} + +.end diff --git a/models/xyce/testing/regression/diode/models_regression.py b/models/xyce/testing/regression/diode/models_regression.py index cf9b3503..27ef7d4b 100644 --- a/models/xyce/testing/regression/diode/models_regression.py +++ b/models/xyce/testing/regression/diode/models_regression.py @@ -1,13 +1,25 @@ +# Copyright 2022 GlobalFoundries PDK Authors +# +# 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. """ Usage: - models_regression.py [--num_cores=] + model_reg.py [--num_cores=] -h, --help Show help text. -v, --version Show version. --num_cores= Number of cores to be used by simulator """ -from re import T from docopt import docopt import pandas as pd import numpy as np @@ -15,226 +27,286 @@ from jinja2 import Template import concurrent.futures import shutil +import multiprocessing as mp +import logging + +import glob + import warnings warnings.simplefilter(action="ignore", category=FutureWarning) +DEFAULT_TEMP = 25.0 +PASS_THRESH = 2.0 +MAX_VOLTAGE = 3.3 + + +def find_diode(filepath): + """ + Find diode in csv files + """ + return os.path.exists(filepath) + def call_simulator(file_name): """Call simulation commands to perform simulation. Args: file_name (str): Netlist file name. """ - os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log") + os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log 2> /dev/null") -def ext_measured(device, vn, d_in, Id_sim, corner): +def ext_iv_measured( + dev_data_path: str, device: str, corners: str, dev_path: str +) -> pd.DataFrame: + """ext_measured function get measured data + Args: + dev_data_path (str): measured data path + device (str): device name + corners (str): corner name + dev_path (str): device [path] - # Get dimensions used for each device - dirpath = f"{device}_{Id_sim}_{corner}" - dimensions = pd.read_csv(f"{dirpath}/{device}.csv", usecols=["W (um)", "L (um)"]) - if device == "sc_diode": - loops = 7 - else: - loops = dimensions["L (um)"].count() + Returns: + df: output dataframe + """ + # Read Data + df = pd.read_excel(dev_data_path) - # Extracting measured values for each W & L - for i in range(0, loops): - if device == "sc_diode": - width = i - length = i - else: - width = dimensions["W (um)"].iloc[i] - length = dimensions["L (um)"].iloc[i] - - # Special case for 1st measured values - if i == 0: - # measured Id - col_list = [f"{vn}", f"{d_in}_{corner}"] - df_measured = pd.read_csv(f"{dirpath}/{device}.csv", usecols=col_list) - df_measured.columns = [f"{vn}", f"{d_in}_{corner}"] - df_measured.to_csv( - f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv", - index=False, - ) - else: - # measured Id - col_list = [f"{vn}", f"{d_in}_{corner}.{i}"] - df_measured = pd.read_csv(f"{dirpath}/{device}.csv", usecols=col_list) - df_measured.columns = [f"{vn}", f"{d_in}_{corner}"] - df_measured.to_csv( - f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv", - index=False, - ) + dim_df = df[["L (um)", "W (um)"]].copy() + dim_df.rename( + columns={"L (um)": "length", "W (um)": "width"}, + inplace=True, + ) + all_dfs = [] + + loops = dim_df["length"].count() + for corner in corners: + + for i in range(0, loops): + width = dim_df["width"].iloc[int(i)] + length = dim_df["length"].iloc[int(i)] + + if i % 4 == 0: + temp = -40 + elif i % 4 == 1: + temp = 25 + elif i % 4 == 2: + temp = 125 + else: + temp = 175 + + leng = [] + wid = [] + tempr = [] + cor = [] + meas = [] + + if i == 0: + idf = df[["Vn1 (V)", f" |In1(A)| diode_{corner}"]].copy() + idf.rename( + columns={ + f" |In1(A)| diode_{corner}": "diode_measured", + "Vn1 (V)": "measured_volt", + }, + inplace=True, + ) -def ext_simulated(device, vn, d_in, vn_sweeps, Id_sim, corner): - - # Get dimensions used for each device - dirpath = f"{device}_{Id_sim}_{corner}" - dimensions = pd.read_csv(f"{dirpath}/{device}.csv", usecols=["W (um)", "L (um)"]) - if device == "sc_diode": - loops = 7 - else: - loops = dimensions["L (um)"].count() - netlist_tmp = f"./device_netlists/{Id_sim}.spice" - for i in range(0, loops): - if device == "sc_diode": - width = i - length = i - else: - width = dimensions["W (um)"].iloc[i] - length = dimensions["L (um)"].iloc[i] - - if i % 4 == 0: - temp = -40 - elif i % 4 == 1: - temp = 25 - elif i % 4 == 2: - temp = 125 - else: - temp = 175 - with open(netlist_tmp) as f: - tmpl = Template(f.read()) - os.makedirs(f"{dirpath}/{device}_netlists_{Id_sim}", exist_ok=True) - with open( - f"{dirpath}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_A{width}_P{length}.spice", - "w", - ) as netlist: - netlist.write( - tmpl.render( - device=device, - area=width, - pj=length, - i=i, - temp=temp, - Id_sim=Id_sim, - corner=corner, - ) + else: + + idf = df[["Vn1 (V)", f" |In1(A)| diode_{corner}.{i}"]].copy() + idf.rename( + columns={ + f" |In1(A)| diode_{corner}.{i}": "diode_measured", + "Vn1 (V)": "measured_volt", + }, + inplace=True, + ) + meas_csv = f"measured_A{width}_P{length}_t{temp}_{corner}.csv" + + os.makedirs(f"{dev_path}/measured_iv", exist_ok=True) + idf.to_csv(f"{dev_path}/measured_iv/{meas_csv}") + + leng.append(length) + wid.append(width) + tempr.append(temp) + cor.append(corner) + meas.append(f"{dev_path}/measured_iv/{meas_csv}") + + sdf = { + "length": leng, + "width": wid, + "temp": tempr, + "corner": cor, + "diode_measured": meas, + } + sdf = pd.DataFrame(sdf) + all_dfs.append(sdf) + + df = pd.concat(all_dfs) + df.dropna(axis=0, inplace=True) + df["device"] = device + df = df[["device", "length", "width", "temp", "corner", "diode_measured"]] + + return df + + +def run_sim( + char: str, + dirpath: str, + device: str, + length: float, + width: float, + corner: str, + temp: float, +) -> dict: + """Run simulation at specific information and corner + Args: + char (str): spice file name + dirpath (str): device path + device (str): device name + length (float): length of device + width (float): width of device + corner (str): simulation corner + temp (float): tempreture of device + + Returns: + info(dict): results are stored in, + and passed to the run_sims function to extract data""" + netlist_tmp = f"./device_netlists/{char}.spice" + + info = {} + info["device"] = device + info["corner"] = corner + info["temp"] = temp + info["width"] = width + info["length"] = length + + width_str = "{:.1f}".format(width) + length_str = "{:.1f}".format(length) + temp_str = "{:.1f}".format(temp) + + net_sp = f"netlist_A{width_str}_P{length_str}_t{temp_str}_{corner}.spice" + res_csv = f"simulated_A{width_str}_P{length_str}_t{temp_str}_{corner}.csv" + netlist_path = f"{dirpath}/{device}_netlists_{char}/{net_sp}" + result_path = f"{dirpath}/simulated_{char}/{res_csv}" + + with open(netlist_tmp) as f: + tmpl = Template(f.read()) + os.makedirs(f"{dirpath}/{device}_netlists_{char}", exist_ok=True) + os.makedirs(f"{dirpath}/simulated_{char}", exist_ok=True) + + with open(netlist_path, "w") as netlist: + netlist.write( + tmpl.render( + device=device, + area=width_str, + pj=length_str, + Id_sim=char, + corner=corner, + temp=temp_str, ) - netlist_path = f"{dirpath}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_A{width}_P{length}.spice" - # Running ngspice for each netlist - with concurrent.futures.ProcessPoolExecutor( - max_workers=workers_count - ) as executor: - executor.submit(call_simulator, netlist_path) - - # Writing simulated data - df_simulated = pd.read_csv( - f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv", - header=0, ) - # df_simulated.to_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",index= False) - # zero array to append in it shaped (vn_sweeps, number of trials + 1) - new_array = np.zeros((vn_sweeps, 2)) - new_array[: len(df_simulated.index), 0] = df_simulated.iloc[:, 0] + # Running ngspice for each netlist + try: + call_simulator(netlist_path) + # Find diode in csv + if find_diode(result_path): + diode_simu = result_path + else: + diode_simu = "None" + except Exception: + diode_simu = "None" - new_array[: len(df_simulated.index), 1] = df_simulated.iloc[:, 0] + info["diode_sim_unscaled"] = diode_simu - # Writing final simulated data - df_simulated = pd.DataFrame(new_array) - df_simulated.to_csv( - f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv", - index=False, - ) - df_simulated.columns = [f"{vn}", f"{d_in}_{corner}"] - df_simulated.to_csv( - f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv", - index=False, - ) + return info -def error_cal(device, vn, d_in, Id_sim, corner): - - # Get dimensions used for each device - dirpath = f"{device}_{Id_sim}_{corner}" - dimensions = pd.read_csv(f"{dirpath}/{device}.csv", usecols=["W (um)", "L (um)"]) - if device == "sc_diode": - loops = 7 - else: - loops = dimensions["L (um)"].count() - df_final = pd.DataFrame() - for i in range(0, loops): - if device == "sc_diode": - width = i - length = i - else: - width = dimensions["W (um)"].iloc[i] - length = dimensions["L (um)"].iloc[i] - if i % 4 == 0: - temp = -40 - elif i % 4 == 1: - temp = 25 - elif i % 4 == 2: - temp = 125 - else: - temp = 175 +def run_sims( + char: str, df: pd.DataFrame, dirpath: str, num_workers=mp.cpu_count() +) -> pd.DataFrame: + """Run simulation at specific information and corner + Args: + char (str): sim file name + df (pd.DataFrame): df has simulation info l,w,t + dirpath (str): device path + num_workers (_type_, optional): num of cores. Defaults to mp.cpu_count(). - measured = pd.read_csv( - f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv" + Returns: + pd.DataFrame: simulation df output + """ + results = [] + with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor: + futures_list = [] + for j, row in df.iterrows(): + futures_list.append( + executor.submit( + run_sim, + char, + dirpath, + row["device"], + row["length"], + row["width"], + row["corner"], + row["temp"], + ) + ) + + for future in concurrent.futures.as_completed(futures_list): + try: + data = future.result() + results.append(data) + except Exception as exc: + logging.info("Test case generated an exception: %s" % (exc)) + + sf = glob.glob(f"{dirpath}/simulated_{char}/*.csv") # stored simulated data files + for i in range(len(sf)): + sdf = pd.read_csv( + sf[i], + header=None, + delimiter=r"\s+", ) - simulated = pd.read_csv( - f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv" + sdf.drop(index=0, inplace=True) + sdf.rename( + columns={0: "diode_simulated"}, + inplace=True, ) + sdf.to_csv(sf[i], index=False) + sdf = pd.read_csv(sf[i]) + sdf.to_csv(sf[i], index=True) - error_1 = round( - 100 - * abs( - (abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1])) - / abs(measured.iloc[:, 1]) - ), - 6, - ) + df = pd.DataFrame(results) - df_error = pd.DataFrame(data=[measured.iloc[:, 0], error_1]).transpose() - df_error.to_csv( - f"{dirpath}/error_{Id_sim}/{i}_{device}_error_A{width}_P{length}.csv", - index=False, - ) + df = df[["device", "length", "width", "temp", "corner", "diode_sim_unscaled"]] + df["diode_sim"] = df["diode_sim_unscaled"] - # Mean error - mean_error = (df_error[f"{d_in}_{corner}"].mean()) / 6 - # Max error - max_error = df_error[f"{d_in}_{corner}"].max() - # Max error location - max_index = max((df_error == max_error).idxmax()) - max_location_vgs = (df_error == max_error).idxmax(axis=1)[max_index] - max_location_vds = df_error[f"{vn}"][max_index] - - df_final_ = { - "Run no.": f"{i}", - "Temp": f"{temp}", - "Device name": f"{dirpath}", - "Area": f"{width}", - "Perimeter": f"{length}", - "Simulated_Val": f"{Id_sim}", - "Mean error%": f'{"{:.2f}".format(mean_error)}', - "Max error%": f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & vn (V) = {max_location_vds}', - } - df_final = df_final.append(df_final_, ignore_index=True) - # Max mean error - print(df_final) - df_final.to_csv(f"{dirpath}/Final_report_{Id_sim}.csv", index=False) - out_report = pd.read_csv(f"{dirpath}/Final_report_{Id_sim}.csv") - print("\n", f"Max. mean error = {out_report['Mean error%'].max()}%") - print( - "=====================================================================================================================================================" - ) + return df def main(): - + """Main function applies all regression steps""" + # ======= Checking Xyce ======= + Xyce_v_ = os.popen("Xyce -v 2> /dev/null").read() + if Xyce_v_ == "": + logging.error("Xyce is not found. Please make sure Xyce is installed.") + exit(1) # pandas setup pd.set_option("display.max_columns", None) pd.set_option("display.max_rows", None) pd.set_option("max_colwidth", None) pd.set_option("display.width", 1000) + main_regr_dir = "diode_regr" + + # diode var. + corners = ["typical", "ff", "ss"] + devices = [ - "diode_nd2ps_03v3", - "diode_pw2dw", "diode_dw2ps", + "diode_pw2dw", + "diode_nd2ps_03v3", "diode_nd2ps_06v0", "diode_nw2ps_03v3", "diode_nw2ps_06v0", @@ -242,55 +314,148 @@ def main(): "diode_pd2nw_06v0", "sc_diode", ] - corners = ["typical", "ff", "ss"] - measures = [["iv", "Vn1 (V)", " |In1(A)| diode", 103]] # , - # ["cv","Vj", "diode", 17]] - - for device in devices: - for measure in measures: - for corner in corners: - # Folder structure of measured values - Id_sim, diode_vn, diode_in, no_of_vn_sweeps = ( - measure[0], - measure[1], - measure[2], - measure[3], - ) - dirpath = f"{device}_{Id_sim}_{corner}" - if os.path.exists(dirpath) and os.path.isdir(dirpath): - shutil.rmtree(dirpath) - os.makedirs(f"{dirpath}/measured_{Id_sim}", exist_ok=False) - - # From xlsx to csv - read_file = pd.read_excel( - f"../../180MCU_SPICE_DATA/Diode/{device}_{Id_sim}.nl_out.xlsx" - ) - read_file.to_csv(f"{dirpath}/{device}.csv", index=False, header=True) - # Folder structure of simulated values - os.makedirs(f"{dirpath}/simulated_{Id_sim}", exist_ok=False) - os.makedirs(f"{dirpath}/error_{Id_sim}", exist_ok=False) + char = ["iv"] # ,"cv" + + for i, dev in enumerate(devices): + dev_path = f"{main_regr_dir}/{dev}" + + if os.path.exists(dev_path) and os.path.isdir(dev_path): + shutil.rmtree(dev_path) + + os.makedirs(f"{dev_path}", exist_ok=False) + + logging.info("######" * 10) + logging.info(f"# Checking Device {dev}") + + for c in char: + + # cv section + + diode_data_files = glob.glob(f"./0_measured_data/{dev}_{c}.nl_out.xlsx") + if len(diode_data_files) < 1: + logging.error(f"# Can't find diode file for device: {dev}") + diode_file = "" + else: + diode_file = diode_data_files[0] + logging.info(f"# diode_{c} data points file : {diode_file}") + + avail_mess = f"# No {c}_datapoints available for" + if diode_file == "": + logging.info(f"{avail_mess} validation for device {dev}") + continue + f = ext_iv_measured - ext_measured(device, diode_vn, diode_in, Id_sim, corner) + if diode_file != "": + meas_df = f(diode_file, dev, corners, dev_path) + else: + meas_df = [] - ext_simulated( - device, diode_vn, diode_in, no_of_vn_sweeps, Id_sim, corner + meas_len = len(pd.read_csv(glob.glob(f"{dev_path}/measured_{c}/*.csv")[1])) + + logging.info( + f"# Device {dev} number of {c}_measured_datapoints : {len(meas_df) * meas_len}" + ) + + sim_df = run_sims(c, meas_df, dev_path, 3) + sim_len = len(pd.read_csv(glob.glob(f"{dev_path}/simulated_{c}/*.csv")[1])) + logging.info( + f"# Device {dev} number of {c}_simulated datapoints : {len(sim_df) * sim_len}" + ) + + # compare section + + merged_df = meas_df.merge( + sim_df, on=["device", "corner", "length", "width", "temp"], how="left" + ) + merged_dfs = [] + for i in range(len(merged_df)): + measured_data = pd.read_csv(merged_df["diode_measured"][i]) + simulated_data = pd.read_csv(merged_df["diode_sim"][i]) + simulated_data + result_data = simulated_data.merge(measured_data, how="left") + result_data["corner"] = ( + merged_df["diode_measured"][i] + .split("/")[-1] + .split("_")[-1] + .split(".")[0] + ) + result_data["device"] = merged_df["diode_measured"][i].split("/")[1] + result_data["length"] = ( + merged_df["diode_measured"][i] + .split("/")[-1] + .split("_")[1] + .split("A")[1] + ) + result_data["width"] = ( + merged_df["diode_measured"][i] + .split("/")[-1] + .split("_")[2] + .split("P")[1] + ) + result_data["temp"] = ( + merged_df["diode_measured"][i] + .split("/")[-1] + .split("_")[3] + .split("t")[1] ) - for device in devices: - for measure in measures: - for corner in corners: - # Folder structure of measured values - Id_sim, diode_vn, diode_in, no_of_vn_sweeps = ( - measure[0], - measure[1], - measure[2], - measure[3], + result_data["error"] = ( + np.abs( + result_data["diode_simulated"] - result_data["diode_measured"] + ) + * 100.0 + / result_data["diode_measured"] ) - error_cal(device, diode_vn, diode_in, Id_sim, corner) + result_data = result_data[ + [ + "device", + "length", + "width", + "temp", + "corner", + "measured_volt", + "diode_measured", + "diode_simulated", + "error", + ] + ] + + merged_dfs.append(result_data) + + merged_out = pd.concat(merged_dfs) + + merged_out.to_csv(f"{dev_path}/error_analysis_{c}.csv", index=False) + + if merged_out["error"].min() > 100: + min_error = 100 + else: + min_error = merged_out["error"].min() + + if merged_out["error"].max() > 100: + max_error = 100 + else: + max_error = merged_out["error"].max() + + if merged_out["error"].mean() > 100: + mean_error = 100 + else: + mean_error = merged_out["error"].mean() + + logging.info( + f"# Device {dev} min error: {min_error:.2f}, max error: {max_error:.2f}, mean error {mean_error:.2f}" + ) -# ================================================================ + if merged_out["error"].max() < PASS_THRESH: + logging.info(f"# Device {dev} has passed regression.") + else: + logging.error( + f"# Device {dev} has failed regression. Needs more analysis." + ) + + +# # ================================================================ # -------------------------- MAIN -------------------------------- # ================================================================ @@ -300,9 +465,17 @@ def main(): arguments = docopt(__doc__, version="comparator: 0.1") workers_count = ( os.cpu_count() * 2 - if arguments["--num_cores"] == None + if arguments["--num_cores"] is None else int(arguments["--num_cores"]) ) + logging.basicConfig( + level=logging.DEBUG, + handlers=[ + logging.StreamHandler(), + ], + format="%(asctime)s | %(levelname)-7s | %(message)s", + datefmt="%d-%b-%Y %H:%M:%S", + ) # Calling main function main()