def SimplifiedBarChart(dfs, measurement, configs, analysisType, xaxis, bins=50, **addConfigs): """Generates a simplified bar chart with a simplified x axis, can be handy if you have lots of points """ newConfigs = addConfigs log.info("Generating BarChart for measurement {}...".format(measurement)) finalplots = None try: for key in dfs["keys"]: log.info("Generating histograms for measurement {} for file {}...". format(measurement, key)) # Sanatize data data = dfs[key]["data"][[measurement, xaxis]].dropna() # Drop all nan invertedaxis = data.reset_index().set_index(measurement) data = np.histogram(data[measurement], bins=data[xaxis]) plt = hv.Histogram(data, label="BarChart: {}".format(measurement), group="{}".format(key)) try: xlabel = "{} [{}]".format( measurement, dfs[dfs["keys"][0]]["units"][dfs[ dfs["keys"][0]]["measurements"].index(measurement)], ) except Exception as err: log.error( "Label could not be generated for Histogram {}. Error: {}". format(measurement, err)) xlabel = "X-Axis" plt.opts(xlabel=xlabel) # Update the plot specific options if need be generalOptions = configs[analysisType].get("General", {}) newConfigs.update(generalOptions.copy()) data_options = (configs[analysisType].get(measurement, {}).get( "Single Histogram", {}).get("PlotOptions", {})) newConfigs.update(configs[analysisType].get( "{}Options".format("Histogram"), {})) newConfigs.update(data_options) plots = customize_plot(plt, "", configs[analysisType], **newConfigs) if finalplots: finalplots += plots else: finalplots = plots except Exception as err: log.error( "Unexpected error happened during Hist plot generation {}. Error: {}" .format(measurement, err)) return None return finalplots
def BoxWhisker(dfs, measurement, configs, analysisType, **addConfigs): """Plots a measurement from all df as boxwisker""" newConfigs = addConfigs log.info("Generating BoxWhisker Plot for {}".format(measurement)) try: plot = hv.BoxWhisker( dfs["All"], kdims="Name", vdims=measurement, label="BoxWhisker: {}".format(measurement), group="BoxWhisker: {}".format(measurement), ) # plot = relabelPlot(plot, label="{}".format(measurement)) # get labels from the configs # ylabel = "{} [{}]".format(measurement, dfs[dfs["keys"][0]]["units"][dfs[dfs["keys"][0]]["measurements"].index(measurement)]) try: ylabel = "{} [{}]".format( measurement, dfs[dfs["keys"][0]]["units"][dfs[ dfs["keys"][0]]["measurements"].index(measurement)], ) except Exception as err: log.error( "Label could not be genereated for concatonated Histogram {}. Error: {}" .format(measurement, err)) ylabel = "X-Axis" plot.opts( box_alpha=0.3, xrotation=80, box_color="blue", height=500, show_legend=False, width=600, whisker_color="blue", ylabel=ylabel, ) # Update the plot specific options if need be generalOptions = configs[analysisType].get("General", {}) newConfigs.update(generalOptions.copy()) data_options = (configs[analysisType].get(measurement, {}).get( "BoxWhisker", {}).get("PlotOptions", {})) newConfigs.update(configs[analysisType].get( "{}Options".format("BoxWhisker"), {})) newConfigs.update(data_options) plot = customize_plot(plot, "", configs[analysisType], **newConfigs) except Exception as err: log.error( "Unexpected error happened during BoxWhisker plot generation {}. Error: {}" .format(measurement, err)) return None return plot
def do_CCE(self): """Runs the CCE calculations and does the plot for the CCE measurement for ION and LASER sources""" if not "IonPad1" in self.measurements: # Todo: find a better check return from scipy import integrate IntPad1 = self.data["All"].groupby(self.data["All"].Name).apply(lambda g: integrate.trapz(g.IonPad1_1, x=g.IonPad1)) IntPad2 = self.data["All"].groupby(self.data["All"].Name).apply(lambda g: integrate.trapz(g.IonPad2_1, x=g.IonPad2)) TotalElectrons = ((IntPad1+IntPad2)/1.602e-19) Electronsperum = ((IntPad1+IntPad2)/1.602e-19)/self.config["TCAD"].get("CCE",{}).get("BulkThickness", 1) picoCoulombs = ((IntPad1 + IntPad2) * 1e12) units = {"Total electrons": TotalElectrons, "Electrons per um": Electronsperum, "Pico Coulombs": picoCoulombs} self.log.info("TotalElectrons: {} \n" "Electrons per um: {}\n" "picoCoulombs: {} \n".format(TotalElectrons, Electronsperum, picoCoulombs)) CCEOptions = self.config["TCAD"].get("CCE",{}).get("General", {}) CCEOptions.update(self.config["TCAD"].get("CCE",{}).get("PlotOptions", {})) for pltType in self.config["TCAD"].get("CCE",{}).get("PlotStyles", ["Bars"]): plot = plainPlot(pltType, self.config["TCAD"].get("CCE", {})["FileXPositions"], units[self.config["TCAD"].get("CCE",{}).get("yAxisUnits", "Pico Coulombs")], self.config["TCAD"].get("CCE",{}).get("PlotLabel", "NOName"), "CCE", self.config["TCAD"] ) if self.PlotDict["All"]: self.PlotDict["All"] += plot else: self.PlotDict["All"] = plot # Generate all Plots from the individual Files for pltType in self.config["TCAD"].get("IonSource",{}).get("PlotStyles", ["Curve"]): for file in self.data["keys"]: PlotPad1 = plainPlot(pltType, self.data[file]["data"].IonPad1,self.data[file]["data"].IonPad1_1, label="Pad1", plotName="IonSource", configs=self.config["TCAD"]) PlotPad2 = plainPlot(pltType, self.data[file]["data"].IonPad2,self.data[file]["data"].IonPad2_1, label="Pad2", plotName="IonSource", configs=self.config["TCAD"]) PLOT = PlotPad1*PlotPad2 PLOT = customize_plot(PLOT, "IonSource", self.config["TCAD"]) self.PlotDict["All"] += PLOT
def Histogram(dfs, measurement, configs, analysisType, bins=50, iqr=None, **addConfigs): """Generates a Points Plot with a corresponding Histogram""" newConfigs = addConfigs log.info("Generating histograms for measurement {}...".format(measurement)) finalplots = None try: for key in dfs["keys"]: log.info("Generating histograms for measurement {} for file {}...". format(measurement, key)) # Sanatize data data = dfs[key]["data"][measurement].dropna() # Drop all nan if iqr: log.info("Outliers correction with iqr: {}".format(iqr)) data = reject_outliers(data, iqr) mean = np.round(np.mean(data), 2) rms = np.round(np.sqrt(np.mean(data**2)), 2) std = np.round(np.std(data), 2) median = np.round(np.median(data), 2) data = np.histogram(data, bins=bins) plt = hv.Histogram(data, label="Histogram: {}".format(measurement), group="Histogram: {}: {}".format( measurement, key)) try: xlabel = "{} [{}]".format( measurement, dfs[dfs["keys"][0]]["units"][dfs[ dfs["keys"][0]]["measurements"].index(measurement)]) except Exception as err: log.error( "Label could not be generated for Histogram {}. Error: {}". format(measurement, err)) xlabel = "X-Axis" plt.opts(xlabel=xlabel) # Update the plot specific options if need be generalOptions = configs[analysisType].get("General", {}) newConfigs.update(generalOptions.copy()) data_options = configs[analysisType].get(measurement, {}).get( "Single Histogram", {}).get("PlotOptions", {}) newConfigs.update(configs[analysisType].get( "{}Options".format("Histogram"), {})) newConfigs.update(data_options) plots = customize_plot(plt, "", configs[analysisType], **newConfigs) # Add text text = '\nMean: {mean} \n' \ 'Median: {median} \n' \ 'RMS: {rms}\n' \ 'std: {std}'.format(mean=mean, median=median, rms=rms, std=std) log.info(text) y = data[0].max() x = data[1][int(len(data[1]) * 0.9)] text = hv.Text(x, y, text).opts(fontsize=30) #text = text_box(text, x, y, boxsize= (100, 150)) plots = plots * text if finalplots: finalplots += plots else: finalplots = plots except Exception as err: log.error( "Unexpected error happened during Hist plot generation {}. Error: {}" .format(measurement, err)) return None return finalplots
def concatHistogram(dfs, measurement, configs, analysisType, bins=50, iqr=None, **addConfigs): """Concatenates dataframes and generates a Histogram for all passed columns""" newConfigs = addConfigs log.info("Generating concat histograms for measurements {}...".format( measurement)) try: df = dfs["All"] # Sanatize data data = df[measurement].dropna() # Drop all nan if iqr: log.info("Outliers correction with iqr: {}".format(iqr)) data = reject_outliers(data, iqr) mean = np.round(np.mean(data), 2) rms = np.round(np.sqrt(np.mean(data**2)), 2) std = np.round(np.std(data), 2) median = np.round(np.median(data), 2) data = np.histogram(data, bins=bins) plt = hv.Histogram( data, label="Concatenated Histogram: {}".format(measurement), group="Concatenated Histogram: {}".format(measurement)) #plt = hv.Histogram(data, vdims=to_plot, group="Concatenated Histogram: {}".format(to_plot)) try: xlabel = "{} [{}]".format( measurement, dfs[dfs["keys"][0]]["units"][dfs[ dfs["keys"][0]]["measurements"].index(measurement)]) except Exception as err: log.error( "Label could not be genereated for concatonated Histogram {}. Error: {}" .format(measurement, err)) xlabel = "X-Axis" plt.opts(xlabel=xlabel) # Update the plot specific options if need be generalOptions = configs[analysisType].get("General", {}) newConfigs.update(generalOptions.copy()) data_options = configs[analysisType].get(measurement, {}).get( "Concatenated Histogram", {}).get("PlotOptions", {}) newConfigs.update(configs[analysisType].get( "{}Options".format("Histogram"), {})) newConfigs.update(data_options) #addConfigs.update({"xlabel": measurement}) plots = customize_plot(plt, "", configs[analysisType], **newConfigs) # Add text text = '\nMean: {mean} \n' \ 'Median: {median} \n' \ 'RMS: {rms}\n' \ 'std: {std}'.format(mean=mean, median=median, rms=rms, std=std) log.info(text) y = data[0].max() x = data[1][int(len(data[1]) * 0.9)] text = hv.Text(x, y, text).opts(fontsize=30) plots = plots * text except Exception as err: log.error( "Unexpected error happened during concatHist plot generation {}. Error: {}" .format(measurement, err)) return None return plots
def find_flatBand_voltage( self, plot, data, configs, indexMax, indexMin, df, cv_files, voltage_value_of_max_firstder, **addConfigs ): # cv is the list containing all the cvmos files """ Finds the full depletion voltage of all data samples and adds a vertical line for the full depletion in the plot. Vertical line is the mean of all measurements. Furthermore, adds a text with the statistics. :param plot: The plot object :param data: The data files :param configs: the configs :param **addConfigs: the configs special for the 1/C2 plot, it is recommended to pass the same options here again, like in the original plot! :return: The updated plot """ # global Right_stats, fit_stats self.log.info("Searching for flat band voltage in all files...") sample = deepcopy(data[df]) # Create a new data frame with just two columns try: df1 = pd.DataFrame( { "xaxis": sample["data"]["voltage"], "yaxis": sample["data"]["CapacityCopy"], } ) except Exception: df1 = pd.DataFrame( { "xaxis": sample["data"]["Voltage"], "yaxis": sample["data"]["CapacityCopy"], } ) df1 = df1.dropna() # Loop one time from the right side, to get the slope of the accumulation region, and then loop on the fit region to get the fit slope RR2 = 0 fitR2 = 0 for idx in range(5, len(df1) - 5): # Right slope_right, intercept_right, r_right, p_value, std_err_right = linregress( df1["xaxis"][idx:], df1["yaxis"][idx:] ) r2_right = r_right * r_right self.log.debug( "Right side fit: Slope {}, intercept: {}, r^2: {}, std: {}".format( slope_right, intercept_right, r2_right, std_err_right ) ) # See if the r2 value has increased and store it if r2_right >= RR2: RR2 = r2_right RightEndPoints = ( ( df1["xaxis"][idx], slope_right * df1["xaxis"][idx] + intercept_right, ), ( df1["xaxis"][len(df1["xaxis"]) - 1], slope_right * df1["xaxis"][len(df1["xaxis"]) - 1] + intercept_right, ), ) Right_stats = [ RightEndPoints, slope_right, intercept_right, r_right, p_value, std_err_right, ] # Fit central region for idx in range(indexMax, indexMin - 1): # Do central fit slope_fit, intercept_fit, r_fit, p_valuefit, std_err_fit = linregress( df1["xaxis"][idx : indexMin - 1], df1["yaxis"][idx : indexMin - 1] ) r2_fit = r_fit * r_fit self.log.debug( "central fit: Slope {}, intercept: {}, r^2: {}, std: {}".format( slope_fit, intercept_fit, r2_fit, std_err_fit ) ) # See if the r2 value has increased and store it if r2_fit >= fitR2: fitR2 = r2_fit fitEndPoints = ( ( df1["xaxis"][indexMax], slope_fit * df1["xaxis"][indexMax] + intercept_fit, ), ( df1["xaxis"][idx + 1], slope_fit * df1["xaxis"][idx + 1] + intercept_fit, ), # use idx +1 to avoid having the same end points ) fit_stats = [ fitEndPoints, slope_fit, intercept_fit, r_fit, p_valuefit, std_err_fit, ] # Add central slope xmax = df1["xaxis"][indexMin] fit_line = np.array( [ [ df1["xaxis"][indexMax - 3], fit_stats[1] * df1["xaxis"][indexMax - 3] + fit_stats[2], ], [xmax + 0.2, fit_stats[1] * (xmax + 0.2) + fit_stats[2]], ] ) # Add right slope xmax = df1["xaxis"][len(df1["yaxis"]) - 1] right_line = np.array( [ [ df1["xaxis"][indexMax - 3], Right_stats[1] * df1["xaxis"][indexMax - 3] + Right_stats[2], ], [xmax, Right_stats[1] * xmax + Right_stats[2]], ] ) # Compute the flatband voltage flatband_voltage = line_intersection(fit_stats[0], Right_stats[0]) self.log.info( "Flatband voltage to data file {} is {}".format(df, flatband_voltage[0]) ) # Find oxide thickness Tox in nm Accum_capacitance = np.max(df1["yaxis"]) * ( float(self.data[df]["header"][0].split(":")[1]) * (1e-8) ) # float(..) is the area. Accum_capacitance_table = "{:.2e}".format(Accum_capacitance) Accum_capacitance_normalized = np.max(df1["yaxis"]) # F/cm^2 Accum_capacitance_normalized_table = "{:.2e}".format( Accum_capacitance_normalized ) Tox = ( self.config["IV_PQC_parameter"]["epsilonNull"] * self.config["IV_PQC_parameter"]["epsilonSiliconOxide"] * 1e5 / Accum_capacitance_normalized ) Tox_table = "{:.2e}".format(Tox) # Find Fixed oxide charge Nox in cm^-2 phi_s = ( self.config["IV_PQC_parameter"]["electronAffinity"] + self.config["IV_PQC_parameter"]["bandGapEnergy"] / 2 + ( self.config["IV_PQC_parameter"]["boltzmannConstant"] * self.config["IV_PQC_parameter"]["Temperature"] * np.log( self.config["IV_PQC_parameter"]["SiliconDoping"] / self.config["IV_PQC_parameter"]["intrinsicDopingConcentration"] ) ) / self.config["IV_PQC_parameter"]["q"] ) phi_ms = self.config["IV_PQC_parameter"]["phi_m"] - phi_s Nox = (Accum_capacitance_normalized * (phi_ms + flatband_voltage[0])) / ( self.config["IV_PQC_parameter"]["q"] ) Nox_table = "{:.2e}".format( Nox ) # Value to insert later on in the results table # Append the values resulting from the analysis to the corresponding lists. fbvoltage.append(flatband_voltage[0]) Accum_capacitance_list.append(Accum_capacitance_table) Accum_capacitance_normalized_list.append(Accum_capacitance_normalized_table) Tox_list.append(Tox_table) Nox_list.append(Nox_table) # Add text text = hv.Text( 10, 0.00000000065, "Flat band voltage_fit_2nd derivative: {} V \n" "Flat band voltage first derivative: {} V \n" "C accumulation: {} F \n" "C accumulation/A: {} F/cm\N{SUPERSCRIPT TWO} \n" "Tox: {} nm \n" "Nox: {} cm\N{SUPERSCRIPT MINUS}\N{SUPERSCRIPT TWO}".format( np.round(np.median(flatband_voltage[0]), 2), np.round(voltage_value_of_max_firstder, 2), np.round(Accum_capacitance, 10), np.round(Accum_capacitance_normalized, 10), np.round(Tox, 2), np.format_float_scientific(Nox, 2), ), ).opts(style=dict(text_font_size="25pt")) # If more than one file do not do the derivates plots if not len(cv_files) == 1: returnPlot = plot returnPlot = customize_plot( returnPlot, "1C2", configs["IV_PQC"], **addConfigs ) return ( returnPlot, flatband_voltage[0], Accum_capacitance_table, Accum_capacitance_normalized_table, Tox_table, Nox_table, ) elif len(cv_files) == 1: # Plot a vertical line in the value of the fb voltage vline = hv.VLine(flatband_voltage[0]).opts(color="black", line_width=1.0) # Plots of the derivatives secondDerivativePlot = self.basePlots5.Curve.secondderivative # Plots of the fits right_line = hv.Curve(right_line).opts(color="blue", line_width=1.0) fit_line = hv.Curve(fit_line).opts(color="red", line_width=1.5) returnPlot = plot * right_line * fit_line * secondDerivativePlot * vline returnPlot = customize_plot( returnPlot, "1C2", configs["IV_PQC"], **addConfigs ) returnplot2 = plot * fit_line * right_line * vline * text return ( returnPlot, flatband_voltage[0], Accum_capacitance_table, Accum_capacitance_normalized_table, Tox_table, Nox_table, returnplot2, )
def add_single_plots(self, xaxis, yaxis, name): points_plot = (xaxis, yaxis) interp_plot = hv.Curve(points_plot) interp_plot = customize_plot(interp_plot, name, self.config["IV_PQC"]) return interp_plot
def find_full_depletion_c2(self, plot, data, configs, diode_files, **addConfigs): """ Finds the full depletion voltage of all data samples and adds a vertical line for the full depletion in the plot. Vertical line is the mean of all measurements. Furthermore, adds a text with the statistics. :param plot: The plot object :param data: The data files :param configs: the configs :param **addConfigs: the configs special for the 1/C2 plot, it is recomended to pass the same options here again, like in the original plot! :return: The updated plot """ # global LeftEndPoints, df Left_stats = np.zeros((len(diode_files), 6), dtype=np.object) self.log.info("Searching for full depletion voltage in all files...") for samplekey in diode_files: if "1C2" not in data[samplekey]["data"]: self.log.warning( "Full depletion calculation could not be done for data set: {}".format( samplekey ) ) else: self.log.debug("Data: {}".format(samplekey)) sample = deepcopy(data[samplekey]) df = pd.DataFrame( {"xaxis": sample["data"]["Voltage"], "yaxis": sample["data"]["1C2"]} ) df = df.dropna() # Loop one time from the from the left side, to get the slope LR2 = 0 for idx in range(5, len(df) - 20): # Left ( slope_left, intercept_left, r_left, p_value, std_err_left, ) = linregress(df["xaxis"][:-idx], df["yaxis"][:-idx]) r2_left = r_left * r_left self.log.debug( "Left side fit: Slope {}, intercept: {}, r^2: {}, std: {}".format( slope_left, intercept_left, r2_left, std_err_left ) ) # See if the r2 value has increased and store end points if r2_left >= LR2: LR2 = r2_left LeftEndPoints = ( (df["xaxis"][0], intercept_left), ( df["xaxis"][idx], slope_left * df["xaxis"][idx] + intercept_left, ), ) # Find the right fit by averaging on the final 20 points average_right = np.mean(list(df["yaxis"][-20:])) RightEndPoints = [ (df["xaxis"][len(df["xaxis"]) - 20], average_right), (df["xaxis"][len(df["xaxis"]) - 1], average_right), ] # Find the line intersection full_depletion_voltages = line_intersection(LeftEndPoints, RightEndPoints) fdepvoltage.append(full_depletion_voltages[0]) self.log.info( "Full depletion voltage: {} V".format( np.round(full_depletion_voltages[0], 2) ) ) # Add vertical line for full depletion vline = hv.VLine(full_depletion_voltages[0]).opts(color="black", line_width=5.0) # Add slopes left_line = np.array( [ [0, np.median(Left_stats[:, 2])], [full_depletion_voltages[0], full_depletion_voltages[1]], ] ) left_line = hv.Curve(left_line).opts(color="grey") right_line = hv.HLine(average_right).opts(color="grey") # Add text text = hv.Text( 230, 5e21, "Depletion voltage: {} V".format(np.round(full_depletion_voltages[0], 2)), ).opts(style=dict(text_font_size="20pt")) # Update the plot specific options if need be returnPlot = plot * vline * right_line * left_line * text returnPlot = customize_plot(returnPlot, "1C2", configs["IV_PQC"], **addConfigs) return returnPlot, full_depletion_voltages[0]
def run(self): """Runs the script""" # Add the measurement to the list # Plot all IV measurements # (data, config, xaxis_measurement, analysis_name, do_not_plot=(), plot_only=(), keys=None, **kwargs) IVplot = None IVSubplot = None self.PlotDict["All"] = None for file in self.data["keys"]: if "IV" in file: for meas in self.measurements: self.config[self.analysisName][meas + "IV"] = self.config[ self.analysisName]["current"] tempplot = plot( self.data, self.config, self.xaxis, self.analysisName, plot_only=(meas, ), keys=(file, ), do_not_plot=self.donts, PlotLabel=meas, ) tempplot = customize_plot(tempplot, "current", self.config[self.analysisName]) if IVplot: IVplot *= tempplot else: IVplot = tempplot if IVSubplot: IVSubplot += tempplot else: IVSubplot = tempplot IVplot = customize_plot(IVplot, "current", self.config[self.analysisName]) # Plot all CV measurements # (data, config, xaxis_measurement, analysis_name, do_not_plot=(), plot_only=(), keys=None, **kwargs) CVplot = None for file in self.data["keys"]: if "CV" in file: for meas in self.measurements: self.config[self.analysisName][meas + "CV"] = self.config[ self.analysisName]["capacitance"] tempplot = plot( self.data, self.config, self.xaxis, self.analysisName, plot_only=(meas, ), keys=(file, ), do_not_plot=self.donts, PlotLabel=meas + "CV", ) if CVplot: CVplot *= tempplot else: CVplot = tempplot CVplot = customize_plot(CVplot, "capacitance", self.config[self.analysisName]) # Add full depletion point to 1/c^2 curve c2plot = None for file in self.data["keys"]: if "1C2" in file: for meas in self.measurements: self.config[self.analysisName][meas + "1c2"] = self.config[ self.analysisName]["1C2"] tempplot = plot( self.data, self.config, self.xaxis, self.analysisName, plot_only=(meas, ), keys=(file, ), do_not_plot=self.donts, PlotLabel=meas + "1c2", ) if c2plot: c2plot *= tempplot else: c2plot = tempplot c2plot = customize_plot(c2plot, "1C2", self.config[self.analysisName]) if IVplot: self.PlotDict["All"] = IVplot self.PlotDict["All"] += IVSubplot if CVplot and self.PlotDict["All"]: self.PlotDict["All"] += CVplot + c2plot elif CVplot and not self.PlotDict["All"]: self.PlotDict["All"] = CVplot + c2plot # Reconfig the plots to be sure self.PlotDict["All"] = config_layout( self.PlotDict["All"], **self.config[self.analysisName].get("Layout", {})) return self.PlotDict
def find_full_depletion(self, plot, data, configs, **addConfigs): """ Finds the full depletion voltage of all data samples and adds a vertical line for the full depletion in the plot. Vertical line is the mean of all measurements. Furthermore, adds a text with the statistics. :param plot: The plot object :param data: The data files :param configs: the configs :param **addConfigs: the configs special for the 1/C2 plot, it is recomended to pass the same options here again, like in the original plot! :return: The updated plot """ full_depletion_voltages = np.zeros((len(data["keys"]), 2)) Left_stats = np.zeros((len(data["keys"]), 6), dtype=np.object) Right_stats = np.zeros((len(data["keys"]), 6), dtype=np.object) self.log.info("Searching for full depletion voltage in all files...") for i, samplekey in enumerate(data["keys"]): if "1C2" not in data[samplekey]["data"]: self.log.warning( "Full depletion calculation could not be done for data set: {}" .format(samplekey)) else: self.log.debug("Data: {}".format(samplekey)) sample = deepcopy(data[samplekey]) try: df = sample["data"][["voltage", "1C2"]].rename(columns={ "voltage": "xaxis", "1C2": "yaxis" }) except: df = sample["data"][["Voltage", "1C2"]].rename(columns={ "Voltage": "xaxis", "1C2": "yaxis" }) df = df.dropna() # Loop one time from the right side and from the left, to get both slopes LR2, Left_stats[i], RR2, Right_stats[ i] = self.calculate_slopes(df, minSize=5, startidx=5) # Make the line intersection full_depletion_voltages[i] = line_intersection( Left_stats[i][0], Right_stats[i][0]) self.log.info( "Full depletion voltage to data file {} is {}, with LR^2={} and RR^2={}" .format(samplekey, full_depletion_voltages[i], LR2, RR2)) # Add vertical line for full depletion # Calculate the mean of all full depeltion voltages and draw a line there valid_indz = np.nonzero(full_depletion_voltages[:, 0]) vline = hv.VLine( np.mean(full_depletion_voltages[valid_indz], axis=0)[0]).opts(color="black", line_width=5.0) # Add slopes xmax = df["xaxis"][len(df["yaxis"]) - 1] left_line = np.array([ [0, np.median(Left_stats[:, 2])], [ xmax, np.median(Left_stats[:, 1]) * xmax + np.median(Left_stats[:, 2]), ], ]) left_line = hv.Curve(left_line).opts(color="grey") right_line = np.array([ [0, np.median(Right_stats[:, 2])], [ xmax, np.median(Right_stats[:, 1]) * xmax + np.median(Right_stats[:, 2]), ], ]) right_line = hv.Curve(right_line).opts(color="grey") # Add text self.log.info( "Full depletion voltage: {} V, " "Error: {} V".format( np.round(np.median(full_depletion_voltages[valid_indz, 0]), 2), np.round(np.std(full_depletion_voltages[valid_indz, 0]), 2), )) # text = hv.Text(np.mean(full_depletion_voltages[valid_indz], axis=0)[0]*2, np.mean(full_depletion_voltages[valid_indz], axis=0)[1]*1.2, 'Depletion voltage: {} V \n' # 'Error: {} V'.format(np.round(np.mean(full_depletion_voltages[:, 0]), 2), # np.round(np.std(full_depletion_voltages[valid_indz, 0]), 2)) # ).opts(fontsize=30) bounds = np.mean(full_depletion_voltages[valid_indz], axis=0) text = text_box( "Depletion voltage: {} V \n" "Error: {} V".format( np.round(np.mean(full_depletion_voltages[:, 0]), 2), np.round(np.std(full_depletion_voltages[valid_indz, 0]), 2), ), np.mean(full_depletion_voltages[valid_indz], axis=0)[0] * 2, np.mean(full_depletion_voltages[valid_indz], axis=0)[1] * 1.3, boxsize=(200, bounds[1] * 0.3), ) # Update the plot specific options if need be returnPlot = vline * right_line * left_line * text * plot # returnPlot = relabelPlot(returnPlot, "CV CURVES - Full depletion calculation") returnPlot = customize_plot(returnPlot, "1C2", configs[self.analysisName], **addConfigs) return returnPlot
def run(self): """Runs the script""" # Add the measurement to the list # Plot all IV measurements # (data, config, xaxis_measurement, analysis_name, do_not_plot=(), plot_only=(), keys=None, **kwargs) IVplot = None self.PlotDict["All"] = None for file in self.data["keys"]: if "IV" in file: for meas in self.measurements: self.config[self.analysisName][meas + "IV"] = self.config[ self.analysisName]["current"] tempplot = plot( self.data, self.config, self.xaxis, self.analysisName, plot_only=(meas, ), keys=(file, ), do_not_plot=self.donts, PlotLabel=meas + "IV", ) if IVplot: IVplot *= tempplot else: IVplot = tempplot IVplot = customize_plot(IVplot, "current", self.config[self.analysisName]) # Plot all CV measurements # (data, config, xaxis_measurement, analysis_name, do_not_plot=(), plot_only=(), keys=None, **kwargs) CVplot = None for file in self.data["keys"]: if "CV" in file: for meas in self.measurements: self.config[self.analysisName][meas + "CV"] = self.config[ self.analysisName]["capacitance"] tempplot = plot( self.data, self.config, self.xaxis, self.analysisName, plot_only=(meas, ), keys=(file, ), do_not_plot=self.donts, PlotLabel=meas + "CV", ) if CVplot: CVplot *= tempplot else: CVplot = tempplot CVplot = customize_plot(CVplot, "capacitance", self.config[self.analysisName]) # Add full depletion point to 1/c^2 curve c2plot = None for file in self.data["keys"]: if "1C2" in file: for meas in self.measurements: self.config[self.analysisName][meas + "1c2"] = self.config[ self.analysisName]["1C2"] tempplot = plot( self.data, self.config, self.xaxis, self.analysisName, plot_only=(meas, ), keys=(file, ), do_not_plot=self.donts, PlotLabel=meas + "1c2", ) if c2plot: c2plot *= tempplot else: c2plot = tempplot c2plot = customize_plot(c2plot, "1C2", self.config[self.analysisName]) # if self.config[self.analysisName].get("1C2", {}).get("DoFullDepletionCalculation", False): # try: # if self.basePlots.Overlay.CV_CURVES_hyphen_minus_Full_depletion.children: # c2plot = self.basePlots.Overlay.CV_CURVES_hyphen_minus_Full_depletion.opts(clone = True) ## else: c2plot = self.basePlots.Curve.CV_CURVES_hyphen_minus_Full_depletion.opts(clone = True) # fdestimation = self.find_full_depletion(c2plot, self.data, self.config, PlotLabel="Full depletion estimation") # self.PlotDict["All"] += fdestimation # self.PlotDict["BasePlots"] += fdestimation # except Exception as err: # self.log.warning("No full depletion calculation possible... Error: {}".format(err)) # Reconfig the plots to be sure # self.PlotDict["All"] = config_layout(self.PlotDict["All"], **self.config[self.analysisName].get("Layout", {})) self.PlotDict["All"] = IVplot + CVplot + c2plot # Reconfig the plots to be sure self.PlotDict["All"] = config_layout( self.PlotDict["All"], **self.config[self.analysisName].get("Layout", {})) return self.PlotDict