示例#1
0
    def do_model_cli(self, range, ya, model, method_nr=0, do_norm_denorm=True):
        # Clear status and params label boxes

        # Import the model script, or reload it if already imported
        self.model = model
        self.model = reload(self.model)

        # Append data to form full set for modeling
        mag = np.array(ya.inputData[M])
        phase = np.array(np.radians(ya.inputData[P]))
        freq = np.array(range.xa["Hz"])
        load = np.array(range.load_array)

        # Radian frequency
        w = 2 * np.pi * freq

        # Multiple data segments.
        # Create null weighting array, same size as m but full of 1's
        weight = mag.copy()
        weight.fill(1.0)

        # Complex impedance target
        z = mag * np.exp(1j * phase)

        # Instantiate clean class for lmfit fitter
        params = Parameters()
        params.clear()

        # Init list of name/value tuples
        values = []

        # Get selected fitting method
        method = METHODS[method_nr][1]

        # Do actual modeling.
        # Make working copy of PARAMS list from model
        param_list = list(self.model.PARAMS)

        # Adjust min and max if necessary
        for phase in param_list:
            phase["min"] = self._min_max_set(phase["min"], method,
                                             phase["init"] / 1e2)
            phase["max"] = self._min_max_set(phase["max"], method,
                                             phase["init"] * 1e2)

        if do_norm_denorm:
            # Normalize component, frequency, and impedance values
            # Determine frequency and Z scaling factors from initial values
            fsf, zsf = self._find_sf(param_list)
            # Normalize each component value, min, and max
            for phase in param_list:
                type = phase["name"][0].upper()
                norm = self._normalize(phase["init"], phase["min"],
                                       phase["max"], type, fsf, zsf)
                phase["init"] = norm["init"]
                phase["min"] = norm["min"]
                phase["max"] = norm["max"]
            # Normalize frequency, target Z, and load
            w = w / fsf
            z = z / zsf
            # TODO: check what does gui in case of None
            #load = load / zsf
        else:
            fsf, zsf = 1.0, 1.0

        # Add modified params to lmfit Parameter class
        # .add converts min/max of None to -/+inf
        for phase in param_list:
            params.add(phase["name"],
                       value=phase["init"],
                       vary=phase["vary"],
                       min=phase["min"],
                       max=phase["max"])

        # Perform weighted model optimization.
        # Errors will be caught and displayed by zfit_excepthook() in main window.
        kw_args = {"load": load, "fsf": fsf, "zsf": zsf}
        result = minimize(self._fcn2min,
                          params,
                          args=(w, z, weight),
                          kws=kw_args,
                          method=method)

        # Don't use params class after minimize -- some values are scrambled or changed.

        # Populate values[] with modeling results, denormalized if necessary
        for phase in param_list:
            name = phase["name"]
            val = result.params[name].value
            if do_norm_denorm:
                comp_type = name[0]
                val = self._denormalize(val, comp_type, fsf, zsf)
            v = (name, val)
            values.append(v)

        if do_norm_denorm:
            # Denormalize frequency, target Z, and load
            w = w * fsf
            z = z * zsf
            # TODO: check what does gui in case of None
            #load = load * zsf

        # Write denormalized modeling results to file
        with open(PARAM_FILE, mode='w', encoding='utf-8') as f:
            print('name, value', file=f)
            for param in values:
                print('{}, {}'.format(param[0], param[1]), file=f)
                print('{}, {}'.format(param[0], param[1]))

        # Convert list of tuples to a single dict for the model, to be compatible
        # with the way minimize() uses the model
        values_d = {param[0]: param[1] for param in values}

        # Get complex impedance of model using modeled or locked parameters
        # Use denormalized values
        kw_args = {"load": load, "fsf": 1.0, "zsf": 1.0}
        zfit = self.model.model(w, values_d, **kw_args)

        # Break into magnitude and degree phase
        magfit = np.abs(zfit)
        phasefit = np.angle(zfit, deg=True)

        # TODO: understand why a, b, i are 0!!!
        # # Split into segments as required, add to data list
        # a, b, i = 0, 0, 0
        # ya.modeledData[M] = magfit[a:b]
        # ya.modeledData[P] = phasefit[a:b]

        ya.modeledData[M] = magfit
        ya.modeledData[P] = phasefit

        self.ya = ya

        status = "Number of function calls: " + str(result.nfev)
        if result.aborted:
            status = "Process aborted"

        print(status)
        report_fit(result)
示例#2
0
    def do_model(self):
        # Clear status and params label boxes
        self.amw.labelParams.setText("")
        self.amw.labelParams.repaint()
        self.amw.labelStatus.setText("Modeling...")
        self.amw.labelStatus.repaint()

        # Import the model script, or reload it if already imported
        self.model = import_module("Models." + self.amw.lineEditModel.text())
        self.model = reload(self.model)

        # Clear any previous modeling, M and P axes
        for line in self.ya.ax[M].get_lines() + self.ya.ax[P].get_lines():
            if line.get_label() == "modeledZPlot":
                line.remove()

        # Create local concatenated arrays for freq, mag, phase, and load.
        # Overwrites data in ya class
        m = np.array([])
        p = np.array([])
        f = np.array([])
        l = np.array([])
        for y, r in zip(self.ya_list, self.range_list):
            # Copy data from lists to working objects
            self.ya.data_unbundle(y)
            self.range.data_unbundle(r)
            # Append data to form full set for modeling
            m = np.append(m, self.ya.inputData[M])
            p = np.append(p, np.radians(self.ya.inputData[P]))
            f = np.append(f, self.range.xa["Hz"])
            l = np.append(l, self.range.load_array)

        # Radian frequency
        w = 2 * np.pi * f

        # Use drawn curves if they exist
        if self.ya.drawnData[M] is not None:
            # Drawn data exists for magnitude, use it instead
            m = self.ya.drawnData[M]
        if self.ya.drawnData[P] is not None:
            # Drawn data exists for phase, use it instead
            p = np.radians(self.ya.drawnData[P])

        if len(self.ya_list) > 1:
            # Multiple data segments.
            # Create null weighting array, same size as m but full of 1's
            weight = m.copy()
            weight.fill(1.0)
        else:
            weight = self.ya.drawnData[W]

        # Complex impedance target
        z = m * np.exp(1j * p)

        # Instantiate clean class for lmfit fitter
        params = Parameters()
        params.clear()

        # Init list of name/value tuples
        values = []

        # Get selected fitting method
        method = METHODS[self.amw.comboBoxMethod.currentIndex()][1]

        if self.amw.checkBoxLocked.isChecked():
            # Read last saved or edited params data (denormalized)
            with open(PARAM_FILE, mode='r', encoding='utf-8', newline='') as f:
                reader = csv.reader(f)
                next(f)  # skip header line
                for line in reader:
                    v = (line[0], float(line[1]))
                    # Build a list of name/value tuples
                    values.append(v)
        else:
            # Do actual modeling.
            # Make working copy of PARAMS list from model
            param_list = list(self.model.PARAMS)

            # Adjust min and max if necessary
            for p in param_list:
                p["min"] = self._min_max_set(p["min"], method, p["init"] / 1e2)
                p["max"] = self._min_max_set(p["max"], method, p["init"] * 1e2)

            if self.amw.do_norm_denorm:
                # Normalize component, frequency, and impedance values
                # Determine frequency and Z scaling factors from initial values
                fsf, zsf = self._find_sf(param_list)
                # Normalize each component value, min, and max
                for p in param_list:
                    type = p["name"][0].upper()
                    norm = self._normalize(p["init"], p["min"], p["max"], type,
                                           fsf, zsf)
                    p["init"] = norm["init"]
                    p["min"] = norm["min"]
                    p["max"] = norm["max"]
                # Normalize frequency, target Z, and load
                w = w / fsf
                z = z / zsf
                l = l / zsf
            else:
                fsf, zsf = 1.0, 1.0

            # Add modified params to lmfit Parameter class
            # .add converts min/max of None to -/+inf
            for p in param_list:
                params.add(p["name"],
                           value=p["init"],
                           vary=p["vary"],
                           min=p["min"],
                           max=p["max"])

            # Perform weighted model optimization.
            # Errors will be caught and displayed by zfit_excepthook() in main window.
            kw_args = {"load": l, "fsf": fsf, "zsf": zsf}
            result = minimize(self._fcn2min,
                              params,
                              args=(w, z, weight),
                              kws=kw_args,
                              method=method,
                              iter_cb=self._prog_bar_update)

            # Don't use params class after minimize -- some values are scrambled or changed.

            # Populate values[] with modeling results, denormalized if necessary
            for p in param_list:
                name = p["name"]
                val = result.params[name].value
                if self.amw.do_norm_denorm:
                    comp_type = name[0]
                    val = self._denormalize(val, comp_type, fsf, zsf)
                v = (name, val)
                values.append(v)

            if self.amw.do_norm_denorm:
                # Denormalize frequency, target Z, and load
                w = w * fsf
                z = z * zsf
                l = l * zsf

            # Write denormalized modeling results to file
            with open(PARAM_FILE, mode='w', encoding='utf-8') as f:
                print('name, value', file=f)
                for p in values:
                    print('{}, {}'.format(p[0], p[1]), file=f)

            self.amw.progressBar.setValue(0)

        # Convert list of tuples to a single dict for the model, to be compatible
        # with the way minimize() uses the model
        values_d = {p[0]: p[1] for p in values}

        # Get complex impedance of model using modeled or locked parameters
        # Use denormalized values
        kw_args = {"load": l, "fsf": 1.0, "zsf": 1.0}
        zfit = self.model.model(w, values_d, **kw_args)

        # Break into magnitude and degree phase
        magfit = np.abs(zfit)
        phasefit = np.angle(zfit, deg=True)
        # Split into segments as required, add to data list
        a, b, i = 0, 0, 0
        for y in self.ya_list:
            self.ya.data_unbundle(y)
            seg_length = len(self.ya.inputData[M])
            b += seg_length
            self.ya.modeledData[M] = magfit[a:b]
            self.ya.modeledData[P] = phasefit[a:b]
            a += seg_length
            self.ya_list[i] = self.ya.data_bundle()
            i += 1

        # Refresh working classes with currently indexed segment data
        self.ya.data_unbundle(self.ya_list[self.range.segment_index])
        self.range.data_unbundle(self.range_list[self.range.segment_index])

        # Add to plot
        self.ya.ax[M].plot(self.range.xa["Hz"],
                           self.ya.modeledData[M],
                           self.ya.modeledLinePlot[M],
                           ls=self.ya.modeledLineStyle[M],
                           lw=1,
                           label="modeledZPlot")
        self.ya.ax[P].plot(self.range.xa["Hz"],
                           self.ya.modeledData[P],
                           self.ya.modeledLinePlot[P],
                           ls=self.ya.modeledLineStyle[P],
                           lw=1,
                           label="modeledZPlot")

        # Update results text box
        self.print_results(values)

        if self.amw.checkBoxLocked.isChecked():
            self.amw.labelStatus.setText("")
        else:
            # Append "written to" text to results box
            outstr = self.amw.labelParams.text()
            outstr += "<br>Written to<br>" + PARAM_FILE
            self.amw.labelParams.setText(outstr)
            # Print optimization info
            status = "Number of function calls: " + str(result.nfev) + "<br>"
            if result.aborted:
                status = RICH_TEXT_RED + "Process aborted:<br>"
            #status += result.lmdif_message
            self.amw.labelStatus.setText(status)

        self.draw_formatted()