示例#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
示例#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
示例#3
0
    def _debug_ac(self, xy_idx):
        """
        Debug Ac matrix by printing out equations and derivatives associated with the max. mismatch variable.

        Parameters
        ----------
        xy_idx
            Index of the maximum mismatch into the `xy` array.
        """

        xy_idx = xy_idx.tolist()
        assoc_eqns = self.Ac[:, xy_idx]
        assoc_vars = self.Ac[xy_idx, :]

        eqns_idx = np.where(np.ravel(matrix(assoc_eqns)))[0]
        vars_idx = np.where(np.ravel(matrix(assoc_vars)))[0]

        logger.debug('Max. correction is for variable %s [%d]', self.system.dae.xy_name[xy_idx], xy_idx)
        logger.debug('Associated equation rhs is %20g', self.system.dae.fg[xy_idx])
        logger.debug('')

        logger.debug(f'{"xy_index":<10} {"Equation (row)":<20} {"Derivative":<20} {"Eq. Mismatch":<20}')
        for eq in eqns_idx:
            eq = eq.tolist()
            logger.debug(f'{eq:<10} {self.system.dae.xy_name[eq]:<20} {assoc_eqns[eq]:<20g} '
                         f'{self.system.dae.fg[eq]:<20g}')

        logger.debug('')
        logger.debug(f'{"xy_index":<10} {"Variable (col)":<20} {"Derivative":<20} {"Eq. Mismatch":<20}')
        for v in vars_idx:
            v = v.tolist()
            logger.debug(f'{v:<10} {self.system.dae.xy_name[v]:<20} {assoc_vars[v]:<20g} '
                         f'{self.system.dae.fg[v]:<20g}')
示例#4
0
    def linsolve(self, A, b):
        """
        Solve linear equation set ``Ax = b`` and returns the solutions in a 1-D array.

        This function performs both symbolic and numeric factorizations every time, and can be slower than
        ``Solver.solve``.

        Parameters
        ----------
        A
            Sparse matrix

        b
            RHS of the equation

        Returns
        -------
        The solution in a 1-D np array.
        """
        if self.sparselib == 'umfpack':
            try:
                umfpack.linsolve(A, b)
            except ArithmeticError:
                logger.error('Singular matrix. Case is not solvable')
            return np.ravel(b)

        elif self.sparselib == 'klu':
            try:
                klu.linsolve(A, b)
            except ArithmeticError:
                logger.error('Singular matrix. Case is not solvable')
            return np.ravel(b)

        elif self.sparselib in ('spsolve', 'cupy'):
            ccs = A.CCS
            size = A.size
            data = np.array(ccs[2]).reshape((-1,))
            indices = np.array(ccs[1]).reshape((-1,))
            indptr = np.array(ccs[0]).reshape((-1,))

            A = csc_matrix((data, indices, indptr), shape=size)

            if self.sparselib == 'spsolve':
                x = spsolve(A, b)
                return np.ravel(x)

            elif self.sparselib == 'cupy':
                # delayed import for startup speed
                import cupy as cp  # NOQA
                from cupyx.scipy.sparse import csc_matrix as csc_cu  # NOQA
                from cupyx.scipy.sparse.linalg.solve import lsqr as cu_lsqr  # NOQA

                cu_A = csc_cu(A)
                cu_b = cp.array(np.array(b).reshape((-1,)))
                x = cu_lsqr(cu_A, cu_b)

                return np.ravel(cp.asnumpy(x[0]))
示例#5
0
    def test_init(self):
        """
        Test if the TDS initialization is successful.

        This function update ``dae.f`` and ``dae.g`` and checks if the residuals
        are zeros.
        """

        system = self.system
        # fg_update is called in TDS.init()
        system.j_update(models=system.exist.pflow_tds)

        # reset diff. RHS where `check_init == False`
        system.dae.f[system.no_check_init] = 0.0

        # warn if variables are initialized at limits
        if system.config.warn_limits:
            for model in system.exist.pflow_tds.values():
                for item in model.discrete.values():
                    item.warn_init_limit()

        if np.max(np.abs(system.dae.fg)) < self.config.tol:
            logger.debug('Initialization tests passed.')
            return True

        # otherwise, show suspect initialization error
        fail_idx = np.ravel(np.where(abs(system.dae.fg) >= self.config.tol))
        nan_idx = np.ravel(np.where(np.isnan(system.dae.fg)))
        bad_idx = np.hstack([fail_idx, nan_idx])

        fail_names = [system.dae.xy_name[int(i)] for i in fail_idx]
        nan_names = [system.dae.xy_name[int(i)] for i in nan_idx]
        bad_names = fail_names + nan_names

        title = 'Suspect initialization issue! Simulation may crash!'
        err_data = {
            'Name': bad_names,
            'Var. Value': system.dae.xy[bad_idx],
            'Eqn. Mismatch': system.dae.fg[bad_idx],
        }
        tab = Tab(
            title=title,
            header=err_data.keys(),
            data=list(map(list, zip(*err_data.values()))),
        )

        logger.error(tab.draw())

        if system.options.get('verbose') == 1:
            breakpoint()
        system.exit_code += 1

        return False
示例#6
0
    def solve(self, A, b):
        """
        Solve linear system ``Ax = b`` using numeric factorization ``N`` and symbolic factorization ``F``.
        Store the solution in ``b``.

        This function caches the symbolic factorization in ``self.F`` and is faster in general.
        Will attempt ``Solver.linsolve`` if the cached symbolic factorization is invalid.

        Parameters
        ----------
        A
            Sparse matrix for the equation set coefficients.
        F
            The symbolic factorization of A or a matrix with the same non-zero shape as ``A``.
        N
            Numeric factorization of A.
        b
            RHS of the equation.

        Returns
        -------
        numpy.ndarray
            The solution in a 1-D ndarray
        """
        self.A = A
        self.b = b

        if self.factorize is True:
            self.F = self._symbolic(self.A)
            self.factorize = False

        try:
            self.N = self._numeric(self.A, self.F)
            self._solve(self.A, self.F, self.N, self.b)

            return np.ravel(self.b)
        except ValueError:
            logger.debug('Unexpected symbolic factorization.')
            self.F = self._symbolic(self.A)
            self.N = self._numeric(self.A, self.F)
            self._solve(self.A, self.F, self.N, self.b)

            return np.ravel(self.b)
        except ArithmeticError:
            logger.error('Jacobian matrix is singular.')
            diag = self.A[0:self.A.size[0]**2:self.A.size[0] + 1]
            idx = (np.argwhere(np.array(matrix(diag)).ravel() == 0.0)).ravel()
            logger.error('The xy indices of associated variables:')
            logger.error(idx)

            return np.ravel(matrix(np.nan, self.b.size, 'd'))
示例#7
0
文件: pflow.py 项目: whoiszyc/andes
    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
示例#8
0
    def solve(self, A, b):
        """
        Solve linear system ``Ax = b`` using numeric factorization ``N`` and symbolic factorization ``F``.
        Store the solution in ``b``.

        This function caches the symbolic factorization in ``self.F`` and is faster in general.
        Will attempt ``Solver.linsolve`` if the cached symbolic factorization is invalid.

        Parameters
        ----------
        A
            Sparse matrix for the equation set coefficients.
        F
            The symbolic factorization of A or a matrix with the same non-zero shape as ``A``.
        N
            Numeric factorization of A.
        b
            RHS of the equation.

        Returns
        -------
        numpy.ndarray
            The solution in a 1-D ndarray
        """
        self.A = A
        self.b = b

        if self.sparselib in ('umfpack', 'klu'):
            if self.factorize is True:
                self.F = self._symbolic(self.A)
                self.factorize = False

            try:
                self.N = self._numeric(self.A, self.F)
                self._solve(self.A, self.F, self.N, self.b)
                return np.ravel(self.b)
            except ValueError:
                logger.debug('Unexpected symbolic factorization.')
                self.F = self._symbolic(self.A)
                self.N = self._numeric(self.A, self.F)
                self._solve(self.A, self.F, self.N, self.b)
                return np.ravel(self.b)
            except ArithmeticError:
                logger.error('Jacobian matrix is singular.')
                return np.ravel(matrix(np.nan, self.b.size, 'd'))

        elif self.sparselib in ('spsolve', 'cupy'):
            return self.linsolve(A, b)
示例#9
0
    def _debug_g(self, y_idx):
        """
        Print out the associated variables with the given algebraic equation index.

        Parameters
        ----------
        y_idx
            Index of the equation into the `g` array. Diff. eqns. are not counted in.
        """
        y_idx = y_idx.tolist()
        logger.debug(
            f'Max. algebraic mismatch associated with {self.system.dae.y_name[y_idx]} [y_idx={y_idx}]'
        )
        assoc_vars = self.system.dae.gy[y_idx, :]
        vars_idx = np.where(np.ravel(matrix(assoc_vars)))[0]

        logger.debug('')
        logger.debug(f'{"y_index":<10} {"Variable":<20} {"Derivative":<20}')
        for v in vars_idx:
            v = v.tolist()
            logger.debug(
                f'{v:<10} {self.system.dae.y_name[v]:<20} {assoc_vars[v]:<20g}'
            )

        pass
示例#10
0
文件: tds.py 项目: JiweiTian/andes
    def test_initialization(self):
        """
        Update f and g to see if initialization is successful
        """

        system = self.system
        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()
        system.j_update(models=self.pflow_tds_models)

        if np.max(np.abs(system.dae.fg)) < self.config.tol:
            logger.debug('Initialization tests passed.')
            return True
        else:
            logger.warning('Suspect initialization issue!')
            fail_idx = np.where(abs(system.dae.fg) >= self.config.tol)
            fail_names = [
                system.dae.xy_name[int(i)] for i in np.ravel(fail_idx)
            ]
            logger.warning(f"Check variables {', '.join(fail_names)}")
            return False
示例#11
0
    def _debug_g(self, y_idx):
        """
        Print out the associated variables with the given algebraic equation index.

        Parameters
        ----------
        y_idx
            Index of the equation into the `g` array. Diff. eqns. are not counted in.
        """
        y_idx = y_idx.tolist()
        logger.debug('--> Iteration Number: niter = %d', self.niter)
        logger.debug('Max. algebraic equation mismatch:')
        logger.debug('  <%s> [y_idx=%d]', self.system.dae.y_name[y_idx], y_idx)
        logger.debug('  Variable value = %.4f', self.system.dae.y[y_idx])
        logger.debug('  Mismatch value = %.4f', self.system.dae.g[y_idx])

        assoc_vars = self.system.dae.gy[y_idx, :]
        vars_idx = np.where(np.ravel(matrix(assoc_vars)))[0]

        logger.debug('Related variable values:')
        logger.debug(f'{"y_index":<10} {"Variable":<20} {"Derivative":<20}')
        for v in vars_idx:
            v = v.tolist()
            logger.debug('%10d %20s %20g', v, self.system.dae.y_name[v],
                         assoc_vars[v])
示例#12
0
    def solve(self, A, b):

        # delayed import for startup speed
        from cupyx.scipy.sparse import csc_matrix as csc_cu  # NOQA
        from cupyx.scipy.sparse.linalg import lsqr as cu_lsqr  # NOQA
        A_csc = self.to_csc(A)

        cu_A = csc_cu(A_csc)
        cu_b = cupy.array(np.array(b).ravel())
        x = cu_lsqr(cu_A, cu_b)

        return np.ravel(cupy.asnumpy(x[0]))
示例#13
0
    def store_switch_times(self, models=None):
        models = self._get_models(models)
        out = []
        for instance in models.values():
            out.extend(instance.get_times())

        out = np.ravel(np.array(out))
        out = np.unique(out)
        out = out[np.where(out >= 0)]
        out = np.sort(out)

        self.switch_times = out
        return self.switch_times
示例#14
0
文件: tds.py 项目: willjschmitt/andes
    def test_init(self):
        """
        Update f and g to see if initialization is successful.
        """
        system = self.system
        self.fg_update(system.exist.pflow_tds)
        system.j_update(models=system.exist.pflow_tds)

        # reset diff. RHS where `check_init == False`
        system.dae.f[system.no_check_init] = 0.0

        # warn if variables are initialized at limits
        if system.config.warn_limits:
            for model in system.exist.pflow_tds.values():
                for item in model.discrete.values():
                    item.warn_init_limit()

        if np.max(np.abs(system.dae.fg)) < self.config.tol:
            logger.debug('Initialization tests passed.')
            return True

        # otherwise, show suspect initialization error
        fail_idx = np.where(abs(system.dae.fg) >= self.config.tol)
        fail_names = [system.dae.xy_name[int(i)] for i in np.ravel(fail_idx)]

        title = 'Suspect initialization issue! Simulation may crash!'
        err_data = {
            'Name': fail_names,
            'Var. Value': system.dae.xy[fail_idx],
            'Eqn. Mismatch': system.dae.fg[fail_idx],
        }
        tab = Tab(
            title=title,
            header=err_data.keys(),
            data=list(map(list, zip(*err_data.values()))),
        )

        logger.error(tab.draw())

        if system.options.get('verbose') == 1:
            breakpoint()
        system.exit_code += 1

        return False
示例#15
0
    def store_switch_times(self, models=None):
        """
        Store event switching time in a sorted Numpy array at ``System.switch_times``.

        Returns
        -------
        array-like
            self.switch_times
        """
        models = self._get_models(models)
        out = []
        for instance in models.values():
            out.extend(instance.get_times())

        out = np.ravel(np.array(out))
        out = np.unique(out)
        out = out[np.where(out >= 0)]
        out = np.sort(out)

        self.switch_times = out
        return self.switch_times
示例#16
0
    def store_switch_times(self, models):
        """
        Store event switching time in a sorted Numpy array at ``System.switch_times``.

        Returns
        -------
        array-like
            self.switch_times
        """
        out = []
        for instance in models.values():
            out.extend(instance.get_times())

        out = np.ravel(np.array(out))
        out = np.append(out, out + 1e-4)
        out = np.unique(out)
        out = out[np.where(out >= 0)]
        out = np.sort(out)

        self.switch_times = out
        self.n_switches = len(self.switch_times)
        return self.switch_times
示例#17
0
    def _solve_g(self, verbose):
        system = self.system
        dae = system.dae
        self.converged = False
        self.niter = 0
        self.mis = []

        # check if the next step is critical time
        if self.is_switch_time():
            self._last_switch_t = system.switch_times[self._switch_idx]
            system.switch_action(self.pflow_tds_models)

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

            inc = -matrix(system.dae.g)
            system.j_update(models=self.pflow_tds_models)
            inc = self.solver.solve(dae.gy, inc)
            dae.y += np.ravel(np.array(inc))
            system.vars_to_models()

            mis = np.max(np.abs(inc))
            self.mis.append(mis)
            if verbose:
                print(f't={dae.t:<.4g}, iter={self.niter:<g}, mis={mis:<.4g}')
            if mis < self.config.tol:
                self.converged = True
                break
            elif self.niter > self.config.max_iter:
                raise NoConvergence(f'Convergence not reached after {self.config.max_iter} iterations')
            elif mis >= 1000 and (mis > 1e4 * self.mis[0]):
                raise NoConvergence('Mismatch increased too fast. Convergence not likely.')

            self.niter += 1
示例#18
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
示例#19
0
 def solve(self, A, b):
     A_csc = self.to_csc(A)
     x = spsolve(A_csc, b)
     return np.ravel(x)
示例#20
0
 def linsolve(self, A, b):
     try:
         klu.linsolve(A, b)
     except ArithmeticError:
         logger.error('Singular matrix. Case is not solvable')
     return np.ravel(b)