def j_update(self, models: Optional[Union[str, List, OrderedDict]] = None): models = self._get_models(models) self._call_models_method('j_update', models) self.dae.restore_sparse() # collect sparse values into sparse structures for j_name in self.dae.jac_name: j_size = self.dae.get_size(j_name) for mdl in models.values(): for row, col, val in mdl.zip_ijv(j_name): # TODO: use `spmatrix.ipadd` if available # TODO: fix `ipadd` to get rid of type checking if isinstance(val, np.float64): # Workaround for CVXOPT's handling of np.float64 val = float(val) if isinstance(val, (int, float)) or len(val) > 0: try: self.dae.__dict__[j_name] += spmatrix( val, row, col, j_size, 'd') except TypeError as e: logger.error( f'{mdl.class_name}: j_name {j_name}, row={row}, col={col}, val={val}, ' f'j_size={j_size}') raise e
def j_update(self, models: OrderedDict): """ Call the Jacobian update method for models in sequence. The procedure is - Restore the sparsity pattern with :py:func:`andes.variables.dae.DAE.restore_sparse` - For each sparse matrix in (fx, fy, gx, gy), evaluate the Jacobian function calls and add values. Notes ----- Updated Jacobians are immediately reflected in the DAE sparse matrices (fx, fy, gx, gy). """ self.call_models('j_update', models) self.dae.restore_sparse() # collect sparse values into sparse structures for j_name in jac_names: j_size = self.dae.get_size(j_name) for mdl in models.values(): for rows, cols, vals in mdl.triplets.zip_ijv(j_name): try: if self.config.ipadd and IP_ADD: self.dae.__dict__[j_name].ipadd(vals, rows, cols) else: self.dae.__dict__[j_name] += spmatrix( vals, rows, cols, j_size, 'd') except TypeError as e: logger.error( "Error adding Jacobian triplets to existing sparsity pattern." ) logger.error( f'{mdl.class_name}: j_name {j_name}, row={rows}, col={cols}, val={vals}, ' f'j_size={j_size}') raise e
def j_update(self, models: Optional[Union[str, List, OrderedDict]] = None): """ Call the Jacobian update method for each model. Notes ----- Updated Jacobians are reflected in the numerical DAE. """ models = self._get_models(models) self._call_models_method('j_update', models) self.dae.restore_sparse() # collect sparse values into sparse structures for j_name in jac_names: j_size = self.dae.get_size(j_name) for mdl in models.values(): for rows, cols, vals in mdl.triplets.zip_ijv(j_name): try: if self.config.ipadd and IP_ADD: self.dae.__dict__[j_name].ipadd(vals, rows, cols) else: self.dae.__dict__[j_name] += spmatrix(vals, rows, cols, j_size, 'd') except TypeError as e: logger.error("Error adding Jacobian triplets to existing sparsity pattern.") logger.error(f'{mdl.class_name}: j_name {j_name}, row={rows}, col={cols}, val={vals}, ' f'j_size={j_size}') raise e
def build_pattern(self, name): """ Build sparse matrices with stored patterns. Call to `store_row_col_idx` should be made before this function. Parameters ---------- name : name jac name """ self.__dict__[name] = spmatrix(self.val_of(name), self.row_of(name), self.col_of(name), self.get_size(name), 'd')
def _compare_pattern(self, name): """ Compare the sparsity pattern for the given Jacobian name. This function is for debugging the symbolic factorization error / sparsity pattern change. To use, add the following line in `System.j_update` for each `j_name` at the end: self.dae._compare_pattern(j_name) """ self.__dict__[f'{name}_tpl'] = spmatrix(self.triplets.vjac[name], self.triplets.ijac[name], self.triplets.jjac[name], self.get_size(name), 'd') m_before = self.__dict__[f'{name}_tpl'] m_after = self.__dict__[name] for i in range(len(m_after)): if m_after.I[i] != m_before.I[i] or m_after.J[i] != m_before.J[i]: raise KeyError
def restore_sparse(self, names=None): """ Restore all sparse matrices to the sparsity pattern filled with zeros (for variable Jacobian elements) and non-zero constants. Parameters ---------- names : None or list List of Jacobian names to restore sparsity pattern """ if names is None: names = jac_names elif isinstance(names, str): names = [names] for name in names: self.__dict__[name] = spmatrix(self.tpl[name].V, self.tpl[name].I, self.tpl[name].J, self.tpl[name].size, 'd')
def build_pattern(self, name): """ Build sparse matrices with stored patterns. Call to `store_row_col_idx` should be made before this function. Parameters ---------- name : name jac name """ try: self.__dict__[name] = spmatrix(self.triplets.vjac[name], self.triplets.ijac[name], self.triplets.jjac[name], self.get_size(name), 'd') except TypeError as e: logger.error( "Your new model might have accessed an Algeb using ExtState, or vice versa." ) raise e
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
def calc_part_factor(self, As=None): """ Compute participation factor of states in eigenvalues. Returns ------- """ if As is None: As = self.As mu, N = np.linalg.eig(As) N = matrix(N) n = len(mu) idx = range(n) mu_complex = np.zeros_like(mu, dtype=complex) W = matrix(spmatrix(1.0, idx, idx, As.size, N.typecode)) gesv(N, W) partfact = mul(abs(W.T), abs(N)) b = matrix(1.0, (1, n)) WN = b * partfact partfact = partfact.T for item in idx: mu_real = float(mu[item].real) mu_imag = float(mu[item].imag) mu_complex[item] = complex(round(mu_real, 5), round(mu_imag, 5)) partfact[item, :] /= WN[item] # participation factor self.mu = matrix(mu_complex) self.part_fact = matrix(partfact) return self.mu, self.part_fact
def calc_part_factor(self): """ Compute participation factor of states in eigenvalues Returns ------- """ mu, N = np.linalg.eig(self.As) # TODO: use scipy.sparse.linalg.eigs(self.As) N = matrix(N) n = len(mu) idx = range(n) mu_complex = np.array([0] * n, dtype=complex) W = matrix(spmatrix(1.0, idx, idx, (n, n), N.typecode)) gesv(N, W) partfact = mul(abs(W.T), abs(N)) b = matrix(1.0, (1, n)) WN = b * partfact partfact = partfact.T for item in idx: mu_real = float(mu[item].real) mu_imag = float(mu[item].imag) mu_complex[item] = complex(round(mu_real, 4), round(mu_imag, 4)) partfact[item, :] /= WN[item] # participation factor self.mu = matrix(mu_complex) self.part_fact = matrix(partfact) return self.mu, self.part_fact