Beispiel #1
0
    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,
            )
Beispiel #2
0
    def find_flatBand_voltage(self, file):
        RR2 = 0
        fitR2 = 0
        df = self.data[file]["fit"]["dataframe"]
        for idx in range(5, len(df) - 5):
            # Right
            slope_right, intercept_right, r_right, p_value, std_err_right = linregress(
                df["x"][idx:], df["y"][idx:])
            r2_right = r_right * r_right

            # See if the r2 value has increased and store it
            if r2_right >= RR2:
                RR2 = r2_right
                RightEndPoints = ((df["x"][idx], slope_right * df["x"][idx] +
                                   intercept_right),
                                  (df["x"][len(df["x"]) - 1],
                                   slope_right * df["x"][len(df["x"]) - 1] +
                                   intercept_right))
                Right_stats = [
                    RightEndPoints, slope_right, intercept_right, r_right,
                    p_value, std_err_right
                ]

        startIndex = df['y'].idxmin()
        endIndex = len(df) - 1

        # Fit central region
        for idx in range(startIndex + 5, endIndex - 1):
            # Do central fit
            slope_fit, intercept_fit, r_fit, p_valuefit, std_err_fit = linregress(
                df["x"][startIndex:idx], df["y"][startIndex:idx])
            r2_fit = r_fit * r_fit

            # See if the r2 value has increased and store it
            if r2_fit >= fitR2:
                fitR2 = r2_fit
                fitEndPoints = ((df["x"][startIndex],
                                 slope_fit * df["x"][startIndex] +
                                 intercept_fit),
                                (df["x"][idx + 1],
                                 slope_fit * df["x"][idx + 1] + intercept_fit))
                fit_stats = [
                    fitEndPoints, slope_fit, intercept_fit, r_fit, p_valuefit,
                    std_err_fit
                ]

        # Add central slope, -3 on x value so the line doesnt end too soon, fit_line = [[start_x,start_x],[end_x,end_y]]
        xmax = df["x"][endIndex]
        fit_line = np.array([[
            df["x"][startIndex - 3],
            fit_stats[1] * df["x"][startIndex - 3] + fit_stats[2]
        ], [xmax + 0.2, fit_stats[1] * (xmax + 0.2) + fit_stats[2]]])
        fit_line = hv.Curve(fit_line).opts(color="red", line_width=1.5)
        self.data[file]["fit"]["lines"] = [fit_line, None]

        # Add right slope
        xmax = df["x"][len(df["y"]) - 1]
        right_line = np.array([[
            df["x"][startIndex - 3],
            Right_stats[1] * df["x"][startIndex - 3] + Right_stats[2]
        ], [xmax, Right_stats[1] * xmax + Right_stats[2]]])
        right_line = hv.Curve(right_line).opts(color="blue", line_width=1.0)
        self.data[file]["fit"]["lines"][1] = right_line

        # intersect lines and store only the voltage
        flatband_voltage = line_intersection(fit_stats[0], Right_stats[0])
        self.data[file]["fit"]["flatband"] = round(flatband_voltage[0], 4)
Beispiel #3
0
    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]
Beispiel #4
0
    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