Esempio n. 1
0
    def nr_step(self):
        """
        Single step using Newton-Raphson method.

        Returns
        -------
        float
            maximum absolute mismatch
        """
        system = self.system
        # evaluate discrete, differential, algebraic, and Jacobians
        system.dae.clear_fg()
        system.l_update_var(self.models, niter=self.niter, err=self.mis[-1])
        system.s_update_var(self.models)
        system.f_update(self.models)
        system.g_update(self.models)
        system.l_update_eq(self.models)
        system.fg_to_dae()

        if self.config.method == 'NR':
            system.j_update(models=self.models)
        elif self.config.method == 'dishonest':
            if self.niter < self.config.n_factorize:
                system.j_update(self.models)

        # prepare and solve linear equations
        self.inc = -matrix([matrix(system.dae.f),
                            matrix(system.dae.g)])

        self.A = sparse([[system.dae.fx, system.dae.gx],
                         [system.dae.fy, system.dae.gy]])

        if not self.config.linsolve:
            self.inc = self.solver.solve(self.A, self.inc)
        else:
            self.inc = self.solver.linsolve(self.A, self.inc)

        system.dae.x += np.ravel(np.array(self.inc[:system.dae.n]))
        system.dae.y += np.ravel(np.array(self.inc[system.dae.n:]))

        # find out variables associated with maximum mismatches
        fmax = 0
        if system.dae.n > 0:
            fmax_idx = np.argmax(np.abs(system.dae.f))
            fmax = system.dae.f[fmax_idx]
            logger.debug("Max. diff mismatch %.10g on %s", fmax, system.dae.x_name[fmax_idx])

        gmax_idx = np.argmax(np.abs(system.dae.g))
        gmax = system.dae.g[gmax_idx]
        logger.debug("Max. algeb mismatch %.10g on %s", gmax, system.dae.y_name[gmax_idx])

        mis = max(abs(fmax), abs(gmax))
        if self.niter == 0:
            self.mis[0] = mis
        else:
            self.mis.append(mis)

        system.vars_to_models()

        return mis
Esempio n. 2
0
    def nr_step(self):
        """
        Single stepping for Newton Raphson method
        Returns
        -------

        """
        system = self.system
        # evaluate discrete, differential, algebraic, and jacobians
        system.e_clear()
        system.l_update_var()
        system.f_update()
        system.g_update()
        system.l_check_eq()
        system.l_set_eq()
        system.fg_to_dae()
        system.j_update()

        # prepare and solve linear equations
        self.inc = -matrix([matrix(system.dae.f), matrix(system.dae.g)])

        self.A = sparse([[system.dae.fx, system.dae.gx],
                         [system.dae.fy, system.dae.gy]])

        self.inc = self.solver.solve(self.A, self.inc)

        system.dae.x += np.ravel(np.array(self.inc[:system.dae.n]))
        system.dae.y += np.ravel(np.array(self.inc[system.dae.n:]))

        mis = np.max(np.abs(system.dae.fg))
        self.mis.append(mis)

        system.vars_to_models()

        return mis
Esempio n. 3
0
    def calc_jac(tds, gxs, gys):
        """
        Build full Jacobian matrix ``Ac`` for Trapezoid method.
        """

        dae = tds.system.dae

        return sparse([[tds.Teye - tds.h * 0.5 * dae.fx, gxs],
                       [-tds.h * 0.5 * dae.fy, gys]], 'd')
Esempio n. 4
0
    def _calc_state_matrix(self, fx, fy, gx, gy, Tf, dense=True):
        """
        Kernel function for calculating state matrix.
        """
        gyx = matrix(gx)
        self.solver.linsolve(gy, gyx)

        Tfnz = Tf + np.ones_like(Tf) * np.equal(Tf, 0.0)
        iTf = spdiag((1 / Tfnz).tolist())

        if dense:
            return iTf * (fx - fy * gyx)
        else:
            return sparse(iTf * (fx - fy * gyx))
Esempio n. 5
0
    def nr_step(self):
        """
        Single step using Newton-Raphson method.

        Returns
        -------
        float
            maximum absolute mismatch
        """
        system = self.system
        # evaluate discrete, differential, algebraic, and Jacobians
        system.dae.clear_fg()
        system.l_update_var(self.models, niter=self.niter, err=self.mis[-1])
        system.s_update_var(self.models)
        system.f_update(self.models)
        system.g_update(self.models)
        system.l_update_eq(self.models)
        system.fg_to_dae()

        if self.config.method == 'NR':
            system.j_update(models=self.models)
        elif self.config.method == 'dishonest':
            if self.niter < self.config.n_factorize:
                system.j_update(self.models)

        # prepare and solve linear equations
        self.inc = -matrix([matrix(system.dae.f), matrix(system.dae.g)])

        self.A = sparse([[system.dae.fx, system.dae.gx],
                         [system.dae.fy, system.dae.gy]])

        if not self.config.linsolve:
            self.inc = self.solver.solve(self.A, self.inc)
        else:
            self.inc = self.solver.linsolve(self.A, self.inc)

        system.dae.x += np.ravel(np.array(self.inc[:system.dae.n]))
        system.dae.y += np.ravel(np.array(self.inc[system.dae.n:]))

        mis = np.max(np.abs(system.dae.fg))

        if self.niter == 0:
            self.mis[0] = mis
        else:
            self.mis.append(mis)

        system.vars_to_models()

        return mis
Esempio n. 6
0
    def _reduce(self, fx, fy, gx, gy, Tf, dense=True):
        """
        Reduce algebraic equations (or states associated with zero time constants).

        Returns
        -------
        spmatrix
            The reduced state matrix
        """
        gyx = matrix(gx)
        self.solver.linsolve(gy, gyx)

        Tfnz = Tf + np.ones_like(Tf) * np.equal(Tf, 0.0)
        iTf = spdiag((1 / Tfnz).tolist())

        if dense:
            return iTf * (fx - fy * gyx)
        else:
            return sparse(iTf * (fx - fy * gyx))
Esempio n. 7
0
    def reorder_As(self):
        """
        reorder As by moving rows and cols associated with zero time constants to the end.

        Returns `fx`, `fy`, `gx`, `gy`, `Tf`.
        """
        system = self.system
        rows = np.arange(system.dae.n, dtype=int)
        cols = np.arange(system.dae.n, dtype=int)
        vals = np.ones(system.dae.n, dtype=float)

        swaps = []
        bidx = self.non_zeros
        for ii in range(system.dae.n - self.non_zeros):
            if ii in self.singular_idx:
                while (bidx in self.singular_idx):
                    bidx += 1
                cols[ii] = bidx
                rows[bidx] = ii
                swaps.append((ii, bidx))

        # swap the variable names
        for fr, bk in swaps:
            bk_name = self.x_name[bk]
            self.x_name[fr] = bk_name
        self.x_name = self.x_name[:self.non_zeros]

        # compute the permutation matrix for `As` containing non-states
        perm = spmatrix(matrix(vals), matrix(rows), matrix(cols))
        As_perm = perm * sparse(self.As) * perm
        self.As_perm = As_perm

        nfx = As_perm[:self.non_zeros, :self.non_zeros]
        nfy = As_perm[:self.non_zeros, self.non_zeros:]
        ngx = As_perm[self.non_zeros:, :self.non_zeros]
        ngy = As_perm[self.non_zeros:, self.non_zeros:]
        nTf = np.delete(system.dae.Tf, self.singular_idx)

        return nfx, nfy, ngx, ngy, nTf
Esempio n. 8
0
    def _implicit_step(self):
        """
        Integrate for a single given step.

        This function has an internal Newton-Raphson loop for algebraized semi-explicit DAE.
        The function returns the convergence status when done but does NOT progress simulation time.

        Returns
        -------
        bool
            Convergence status in ``self.converged``.

        """
        system = self.system
        dae = self.system.dae

        self.mis = []
        self.niter = 0
        self.converged = False

        self.x0 = np.array(dae.x)
        self.y0 = np.array(dae.y)
        self.f0 = np.array(dae.f)

        while True:
            system.e_clear(models=self.pflow_tds_models)

            system.l_update_var(models=self.pflow_tds_models)
            system.f_update(models=self.pflow_tds_models)
            system.g_update(models=self.pflow_tds_models)
            system.l_check_eq(models=self.pflow_tds_models)
            system.l_set_eq(models=self.pflow_tds_models)
            system.fg_to_dae()

            # lazy jacobian update
            if dae.t == 0 or self.niter > 3 or (dae.t - self._last_switch_t < 0.2):
                system.j_update(models=self.pflow_tds_models)
                self.solver.factorize = True

            # solve trapezoidal rule integration
            In = spdiag([1] * dae.n)
            self.Ac = sparse([[In - self.h * 0.5 * dae.fx, dae.gx],
                              [-self.h * 0.5 * dae.fy, dae.gy]], 'd')
            # reset q as well
            q = dae.x - self.x0 - self.h * 0.5 * (dae.f + self.f0)
            for item in system.antiwindups:
                if len(item.x_set) > 0:
                    for key, val in item.x_set:
                        np.put(q, key[np.where(item.zi == 0)], 0)

            qg = np.hstack((q, dae.g))

            inc = self.solver.solve(self.Ac, -matrix(qg))

            # check for np.nan first
            if np.isnan(inc).any():
                logger.error(f'NaN found in solution. Convergence not likely')
                self.niter = self.config.max_iter + 1
                self.busted = True
                break

            # reset really small values to avoid anti-windup limiter flag jumps
            inc[np.where(np.abs(inc) < 1e-12)] = 0
            # set new values
            dae.x += np.ravel(np.array(inc[:dae.n]))
            dae.y += np.ravel(np.array(inc[dae.n: dae.n + dae.m]))
            system.vars_to_models()

            # calculate correction
            mis = np.max(np.abs(inc))
            self.mis.append(mis)
            self.niter += 1

            # converged
            if mis <= self.config.tol:
                self.converged = True
                break
            # non-convergence cases
            if self.niter > self.config.max_iter:
                logger.debug(f'Max. iter. {self.config.max_iter} reached for t={dae.t:.6f}, '
                             f'h={self.h:.6f}, mis={mis:.4g} '
                             f'({system.dae.xy_name[np.argmax(inc)]})')
                break
            if mis > 1000 and (mis > 1e8 * self.mis[0]):
                logger.error(f'Error increased too quickly. Convergence not likely.')
                self.busted = True
                break

        if not self.converged:
            dae.x = np.array(self.x0)
            dae.y = np.array(self.y0)
            dae.f = np.array(self.f0)
            system.vars_to_models()

        return self.converged
Esempio n. 9
0
    def _itm_step(self):
        """
        Integrate with Implicit Trapezoidal Method (ITM) to the current time.

        This function has an internal Newton-Raphson loop for algebraized semi-explicit DAE.
        The function returns the convergence status when done but does NOT progress simulation time.

        Returns
        -------
        bool
            Convergence status in ``self.converged``.

        """
        system = self.system
        dae = self.system.dae

        self.mis = 1
        self.niter = 0
        self.converged = False

        self.x0 = np.array(dae.x)
        self.y0 = np.array(dae.y)
        self.f0 = np.array(dae.f)

        while True:
            self._fg_update(models=system.exist.pflow_tds)

            # lazy Jacobian update

            if dae.t == 0 or \
                    self.config.honest or \
                    self.custom_event or \
                    not self.last_converged or \
                    self.niter > 4 or \
                    (dae.t - self._last_switch_t < 0.1):

                system.j_update(models=system.exist.pflow_tds)
                # set flag in `solver.worker.factorize`, not `solver.factorize`.
                self.solver.worker.factorize = True

            # `Tf` should remain constant throughout the simulation, even if the corresponding diff. var.
            # is pegged by the anti-windup limiters.

            # solve implicit trapezoidal method (ITM) integration
            self.Ac = sparse([[self.Teye - self.h * 0.5 * dae.fx, dae.gx],
                              [-self.h * 0.5 * dae.fy, dae.gy]], 'd')

            # equation `self.qg[:dae.n] = 0` is the implicit form of differential equations using ITM
            self.qg[:dae.n] = dae.Tf * (dae.x - self.x0) - self.h * 0.5 * (dae.f + self.f0)

            # reset the corresponding q elements for pegged anti-windup limiter
            for item in system.antiwindups:
                for key, _, eqval in item.x_set:
                    np.put(self.qg, key, eqval)

            self.qg[dae.n:] = dae.g

            if not self.config.linsolve:
                inc = self.solver.solve(self.Ac, matrix(self.qg))
            else:
                inc = self.solver.linsolve(self.Ac, matrix(self.qg))

            # check for np.nan first
            if np.isnan(inc).any():
                self.err_msg = 'NaN found in solution. Convergence is not likely'
                self.niter = self.config.max_iter + 1
                self.busted = True
                break

            # reset small values to reduce chattering
            inc[np.where(np.abs(inc) < self.tol_zero)] = 0

            # set new values
            dae.x -= inc[:dae.n].ravel()
            dae.y -= inc[dae.n: dae.n + dae.m].ravel()

            # store `inc` to self for debugging
            self.inc = inc

            system.vars_to_models()

            # calculate correction
            mis = np.max(np.abs(inc))
            # store initial maximum mismatch
            if self.niter == 0:
                self.mis = mis

            self.niter += 1

            # converged
            if mis <= self.config.tol:
                self.converged = True
                break
            # non-convergence cases
            if self.niter > self.config.max_iter:
                tqdm.write(f'* Max. iter. {self.config.max_iter} reached for t={dae.t:.6f}, '
                           f'h={self.h:.6f}, mis={mis:.4g} ')

                # debug helpers
                g_max = np.argmax(abs(dae.g))
                inc_max = np.argmax(abs(inc))
                self._debug_g(g_max)
                self._debug_ac(inc_max)

                break

            if mis > 1e6 and (mis > 1e6 * self.mis):
                self.err_msg = 'Error increased too quickly. Convergence not likely.'
                self.busted = True
                break

        if not self.converged:
            dae.x[:] = np.array(self.x0)
            dae.y[:] = np.array(self.y0)
            dae.f[:] = np.array(self.f0)
            system.vars_to_models()

        self.last_converged = self.converged

        return self.converged
Esempio n. 10
0
    def _itm_step(self):
        """
        Integrate with Implicit Trapezoidal Method (ITM) to the current time.

        This function has an internal Newton-Raphson loop for algebraized semi-explicit DAE.
        The function returns the convergence status when done but does NOT progress simulation time.

        Returns
        -------
        bool
            Convergence status in ``self.converged``.

        """
        system = self.system
        dae = self.system.dae

        self.mis = 1
        self.niter = 0
        self.converged = False

        self.x0 = np.array(dae.x)
        self.y0 = np.array(dae.y)
        self.f0 = np.array(dae.f)

        while True:
            self._fg_update(models=system.exist.pflow_tds)

            # lazy Jacobian update
            if dae.t == 0 or self.niter > 3 or (dae.t - self._last_switch_t < 0.2):
                system.j_update(models=system.exist.pflow_tds)
                self.solver.factorize = True

            # TODO: set the `Tf` corresponding to the pegged anti-windup limiters to zero.
            # Although this should not affect anything since corr. mismatches in `self.qg` are reset to zero

            # solve implicit trapezoidal method (ITM) integration
            self.Ac = sparse([[self.Teye - self.h * 0.5 * dae.fx, dae.gx],
                              [-self.h * 0.5 * dae.fy, dae.gy]], 'd')

            # equation `self.qg[:dae.n] = 0` is the implicit form of differential equations using ITM
            self.qg[:dae.n] = dae.Tf * (dae.x - self.x0) - self.h * 0.5 * (dae.f + self.f0)

            # reset the corresponding q elements for pegged anti-windup limiter
            for item in system.antiwindups:
                for key, val in item.x_set:
                    np.put(self.qg, key, 0)

            self.qg[dae.n:] = dae.g

            if not self.config.linsolve:
                inc = self.solver.solve(self.Ac, -matrix(self.qg))
            else:
                inc = self.solver.linsolve(self.Ac, -matrix(self.qg))

            # check for np.nan first
            if np.isnan(inc).any():
                self.err_msg = 'NaN found in solution. Convergence not likely'
                self.niter = self.config.max_iter + 1
                self.busted = True
                break

            # reset small values to reduce chattering
            inc[np.where(np.abs(inc) < self.tol_zero)] = 0

            # set new values
            dae.x += inc[:dae.n].ravel()
            dae.y += inc[dae.n: dae.n + dae.m].ravel()

            system.vars_to_models()

            # calculate correction
            mis = np.max(np.abs(inc))
            if self.niter == 0:
                self.mis = mis

            self.niter += 1

            # converged
            if mis <= self.config.tol:
                self.converged = True
                break
            # non-convergence cases
            if self.niter > self.config.max_iter:
                logger.debug(f'Max. iter. {self.config.max_iter} reached for t={dae.t:.6f}, '
                             f'h={self.h:.6f}, mis={mis:.4g} ')

                # debug helpers
                g_max = np.argmax(abs(dae.g))
                inc_max = np.argmax(abs(inc))
                self._debug_g(g_max)
                self._debug_ac(inc_max)

                break
            if mis > 1000 and (mis > 1e8 * self.mis):
                self.err_msg = 'Error increased too quickly. Convergence not likely.'
                self.busted = True
                break

        if not self.converged:
            dae.x = np.array(self.x0)
            dae.y = np.array(self.y0)
            dae.f = np.array(self.f0)
            system.vars_to_models()

        return self.converged