def to_lti(self): """Convert model to |LTIModel|. This method interprets the given model as an |LTIModel| in the following way:: - self.operator -> A self.rhs -> B self.outputs -> C None -> D self.mass -> E """ if len(self.outputs) == 0: raise ValueError('No outputs defined.') if len(self.outputs) > 1: raise NotImplementedError('Only one output supported.') A = -self.operator B = self.rhs C = next(iter(self.outputs.values())) E = self.mass if not all(op.linear for op in [A, B, C, E]): raise ValueError('Operators not linear.') from pymor.models.iosys import LTIModel return LTIModel(A, B, C, E=E, visualizer=self.visualizer)
def create_cl_fom(Re=110, level=2, palpha=1e-3, control='bc'): """Create model which is used to evaluate the H2-Gap norm.""" setup_str = 'lvl_' + str(level) + ('_' + control if control is not None else '') \ + '_re_' + str(Re) + ('_palpha_' + str(palpha) if control == 'bc' else '') fom = load_fom(Re, level, palpha, control) Bra = fom.B.as_range_array() Cva = fom.C.as_source_array() Z = solve_ricc_lrcf(fom.A, fom.E, Bra, Cva, trans=False) K = fom.E.apply(Z).lincomb(Z.dot(Cva).T) KC = LowRankOperator(K, np.eye(len(K)), Cva) mKB = cat_arrays([-K, Bra]).to_numpy().T mKBop = NumpyMatrixOperator(mKB) mKBop_proj = LerayProjectedOperator(mKBop, fom.A.source.G, fom.A.source.E, projection_space='range') cl_fom = LTIModel(fom.A - KC, mKBop_proj, fom.C, None, fom.E) with open(setup_str + '/cl_fom', 'wb') as cl_fom_file: pickle.dump({'cl_fom': cl_fom}, cl_fom_file)
def get_gap_rom(rom): """Based on a rom, create model which is used to evaluate H2-Gap norm.""" A = to_matrix(rom.A, format='dense') B = to_matrix(rom.B, format='dense') C = to_matrix(rom.C, format='dense') if isinstance(rom.E, IdentityOperator): P = spla.solve_continuous_are(A.T, C.T, B.dot(B.T), np.eye(len(C)), balanced=False) F = P @ C.T else: E = to_matrix(rom.E, format='dense') P = spla.solve_continuous_are(A.T, C.T, B.dot(B.T), np.eye(len(C)), e=E.T, balanced=False) F = E @ P @ C.T AF = A - F @ C mFB = np.concatenate((-F, B), axis=1) return LTIModel.from_matrices( AF, mFB, C, E=None if isinstance(rom.E, IdentityOperator) else E)
def to_lti(self): """Convert model to |LTIModel|. This method interprets the given model as an |LTIModel| in the following way:: - self.operator -> A self.rhs -> B self.output_functional -> C None -> D self.mass -> E """ if self.output_functional is None: raise ValueError('No output defined.') A = -self.operator B = self.rhs C = self.output_functional E = self.mass if not all(op.linear for op in [A, B, C, E]): raise ValueError('Operators not linear.') from pymor.models.iosys import LTIModel return LTIModel(A, B, C, E=E, parameter_space=self.parameter_space, visualizer=self.visualizer)
def _poles_b_c_to_lti(poles, b, c): r"""Create an |LTIModel| from poles and residue rank-1 factors. Returns an |LTIModel| with real matrices such that its transfer function is .. math:: \sum_{i = 1}^r \frac{c_i b_i^T}{s - \lambda_i} where :math:`\lambda_i, b_i, c_i` are the poles and residue rank-1 factors. Parameters ---------- poles Sequence of poles. b |VectorArray| of right residue rank-1 factors. c |VectorArray| of left residue rank-1 factors. Returns ------- |LTIModel|. """ A, B, C = [], [], [] for i, pole in enumerate(poles): if pole.imag == 0: A.append(pole.real) B.append(b[i].to_numpy().real) C.append(c[i].to_numpy().real.T) elif pole.imag > 0: A.append([[pole.real, pole.imag], [-pole.imag, pole.real]]) bi = b[i].to_numpy() B.append(np.vstack([2 * bi.real, -2 * bi.imag])) ci = c[i].to_numpy() C.append(np.hstack([ci.real.T, ci.imag.T])) A = spla.block_diag(*A) B = np.vstack(B) C = np.hstack(C) return LTIModel.from_matrices(A, B, C)
def _poles_b_c_to_lti(poles, b, c): r"""Create an |LTIModel| from poles and residue rank-1 factors. Returns an |LTIModel| with real matrices such that its transfer function is .. math:: \sum_{i = 1}^r \frac{c_i b_i^T}{s - \lambda_i} where :math:`\lambda_i, b_i, c_i` are the poles and residue rank-1 factors. Parameters ---------- poles Sequence of poles. b |NumPy array| of shape `(rom.order, rom.dim_input)`. c |NumPy array| of shape `(rom.order, rom.dim_output)`. Returns ------- |LTIModel|. """ A, B, C = [], [], [] for i, pole in enumerate(poles): if pole.imag == 0: A.append(pole.real) B.append(b[i].real) C.append(c[i].real[:, np.newaxis]) elif pole.imag > 0: A.append([[pole.real, pole.imag], [-pole.imag, pole.real]]) B.append(np.vstack([2 * b[i].real, -2 * b[i].imag])) C.append(np.hstack([c[i].real[:, np.newaxis], c[i].imag[:, np.newaxis]])) A = spla.block_diag(*A) B = np.vstack(B) C = np.hstack(C) return LTIModel.from_matrices(A, B, C)
def reduce(self, sigma, b, c): """Realization-independent tangential Hermite interpolation. Parameters ---------- sigma Interpolation points (closed under conjugation), list of length `r`. b Right tangential directions, |NumPy array| of shape `(fom.input_dim, r)`. c Left tangential directions, |NumPy array| of shape `(fom.output_dim, r)`. Returns ------- lti The reduced-order |LTIModel| interpolating the transfer function of `fom`. """ r = len(sigma) assert isinstance(b, np.ndarray) and b.shape == (self.fom.input_dim, r) assert isinstance(c, np.ndarray) and c.shape == (self.fom.output_dim, r) # rescale tangential directions (to avoid overflow or underflow) if b.shape[0] > 1: for i in range(r): b[:, i] /= spla.norm(b[:, i]) else: b = np.ones((1, r)) if c.shape[0] > 1: for i in range(r): c[:, i] /= spla.norm(c[:, i]) else: c = np.ones((1, r)) # matrices of the interpolatory LTI system Er = np.empty((r, r), dtype=complex) Ar = np.empty((r, r), dtype=complex) Br = np.empty((r, self.fom.input_dim), dtype=complex) Cr = np.empty((self.fom.output_dim, r), dtype=complex) Hs = [self.fom.eval_tf(s, mu=self.mu) for s in sigma] dHs = [self.fom.eval_dtf(s, mu=self.mu) for s in sigma] for i in range(r): for j in range(r): if i != j: Er[i, j] = -c[:, i].dot( (Hs[i] - Hs[j]).dot(b[:, j])) / (sigma[i] - sigma[j]) Ar[i, j] = -c[:, i].dot( (sigma[i] * Hs[i] - sigma[j] * Hs[j])).dot(b[:, j]) / ( sigma[i] - sigma[j]) else: Er[i, i] = -c[:, i].dot(dHs[i].dot(b[:, i])) Ar[i, i] = -c[:, i].dot( (Hs[i] + sigma[i] * dHs[i]).dot(b[:, i])) Br[i, :] = Hs[i].T.dot(c[:, i]) Cr[:, i] = Hs[i].dot(b[:, i]) # transform the system to have real matrices T = np.zeros((r, r), dtype=complex) for i in range(r): if sigma[i].imag == 0: T[i, i] = 1 else: indices = np.nonzero( np.isclose(sigma[i + 1:], sigma[i].conjugate()))[0] if len(indices) > 0: j = i + 1 + indices[0] T[i, i] = 1 T[i, j] = 1 T[j, i] = -1j T[j, j] = 1j Er = (T.dot(Er).dot(T.conj().T)).real Ar = (T.dot(Ar).dot(T.conj().T)).real Br = (T.dot(Br)).real Cr = (Cr.dot(T.conj().T)).real return LTIModel.from_matrices(Ar, Br, Cr, D=None, E=Er, cont_time=self.fom.cont_time)
def build_rom(self, projected_operators, error_estimator): return LTIModel(error_estimator=error_estimator, **projected_operators)
def reduce(self, sigma, b, c): """Realization-independent tangential Hermite interpolation. Parameters ---------- sigma Interpolation points (closed under conjugation), sequence of length `r`. b Right tangential directions, |NumPy array| of shape `(r, fom.dim_input)`. c Left tangential directions, |NumPy array| of shape `(r, fom.dim_output)`. Returns ------- lti The reduced-order |LTIModel| interpolating the transfer function of `fom`. """ r = len(sigma) assert b.shape == (r, self.fom.dim_input) assert c.shape == (r, self.fom.dim_output) # rescale tangential directions (to avoid overflow or underflow) b = b * (1 / np.linalg.norm(b)) if b.shape[1] > 1 else np.ones((r, 1)) c = c * (1 / np.linalg.norm(c)) if c.shape[1] > 1 else np.ones((r, 1)) # matrices of the interpolatory LTI system Er = np.empty((r, r), dtype=np.complex_) Ar = np.empty((r, r), dtype=np.complex_) Br = np.empty((r, self.fom.dim_input), dtype=np.complex_) Cr = np.empty((self.fom.dim_output, r), dtype=np.complex_) Hs = [self.fom.eval_tf(s, mu=self.mu) for s in sigma] dHs = [self.fom.eval_dtf(s, mu=self.mu) for s in sigma] for i in range(r): for j in range(r): if i != j: Er[i, j] = -c[i] @ (Hs[i] - Hs[j]) @ b[j] / (sigma[i] - sigma[j]) Ar[i, j] = ( -c[i] @ (sigma[i] * Hs[i] - sigma[j] * Hs[j]) @ b[j] / (sigma[i] - sigma[j])) else: Er[i, i] = -c[i] @ dHs[i] @ b[i] Ar[i, i] = -c[i] @ (Hs[i] + sigma[i] * dHs[i]) @ b[i] Br[i, :] = Hs[i].T @ c[i] Cr[:, i] = Hs[i] @ b[i] # transform the system to have real matrices T = np.zeros((r, r), dtype=np.complex_) for i in range(r): if sigma[i].imag == 0: T[i, i] = 1 else: j = np.argmin(np.abs(sigma - sigma[i].conjugate())) if i < j: T[i, i] = 1 T[i, j] = 1 T[j, i] = -1j T[j, j] = 1j Er = (T @ Er @ T.conj().T).real Ar = (T @ Ar @ T.conj().T).real Br = (T @ Br).real Cr = (Cr @ T.conj().T).real return LTIModel.from_matrices(Ar, Br, Cr, None, Er, cont_time=self.fom.cont_time)
def main( n: int = Argument(100, help='Order of the FOM.'), r: int = Argument(10, help='Order of the ROMs.'), ): """Synthetic parametric demo. See the `MOR Wiki page <http://modelreduction.org/index.php/Synthetic_parametric_model>`_. """ # Model # set coefficients a = -np.linspace(1e1, 1e3, n // 2) b = np.linspace(1e1, 1e3, n // 2) c = np.ones(n // 2) d = np.zeros(n // 2) # build 2x2 submatrices aa = np.empty(n) aa[::2] = a aa[1::2] = a bb = np.zeros(n) bb[::2] = b # set up system matrices Amu = sps.diags(aa, format='csc') A0 = sps.diags([bb, -bb], [1, -1], shape=(n, n), format='csc') B = np.zeros((n, 1)) B[::2, 0] = 2 C = np.empty((1, n)) C[0, ::2] = c C[0, 1::2] = d # form operators A0 = NumpyMatrixOperator(A0) Amu = NumpyMatrixOperator(Amu) B = NumpyMatrixOperator(B) C = NumpyMatrixOperator(C) A = A0 + Amu * ProjectionParameterFunctional('mu') # form a model lti = LTIModel(A, B, C) mu_list = [1 / 50, 1 / 20, 1 / 10, 1 / 5, 1 / 2, 1] w = np.logspace(0.5, 3.5, 200) # System poles fig, ax = plt.subplots() for mu in mu_list: poles = lti.poles(mu=mu) ax.plot(poles.real, poles.imag, '.', label=fr'$\mu = {mu}$') ax.set_title('System poles') ax.legend() plt.show() # Magnitude plot fig, ax = plt.subplots() for mu in mu_list: lti.mag_plot(w, ax=ax, mu=mu, label=fr'$\mu = {mu}$') ax.legend() plt.show() # Hankel singular values fig, ax = plt.subplots() for mu in mu_list: hsv = lti.hsv(mu=mu) ax.semilogy(range(1, len(hsv) + 1), hsv, '.-', label=fr'$\mu = {mu}$') ax.set_title('Hankel singular values') ax.legend() plt.show() # System norms for mu in mu_list: print(f'mu = {mu}:') print(f' H_2-norm of the full model: {lti.h2_norm(mu=mu):e}') if config.HAVE_SLYCOT: print( f' H_inf-norm of the full model: {lti.hinf_norm(mu=mu):e}') print(f' Hankel-norm of the full model: {lti.hankel_norm(mu=mu):e}') # Model order reduction run_mor_method_param(lti, r, w, mu_list, BTReductor, 'BT') run_mor_method_param(lti, r, w, mu_list, IRKAReductor, 'IRKA')
C[0, 1::2] = d # In[ ]: A0 = NumpyMatrixOperator(A0) Amu = NumpyMatrixOperator(Amu) B = NumpyMatrixOperator(B) C = NumpyMatrixOperator(C) # In[ ]: A = A0 + Amu * ProjectionParameterFunctional('mu', ()) # In[ ]: lti = LTIModel(A, B, C) # # Magnitude plot # In[ ]: mu_list_short = [1 / 50, 1 / 20, 1 / 10, 1 / 5, 1 / 2, 1] # In[ ]: w = np.logspace(0.5, 3.5, 200) fig, ax = plt.subplots() for mu in mu_list_short: lti.mag_plot(w, ax=ax, mu=mu, label=fr'$\mu = {mu}$') ax.legend()
def reduce(self, sigma, b, c): """Realization-independent tangential Hermite interpolation. Parameters ---------- sigma Interpolation points (closed under conjugation), list of length `r`. b Right tangential directions, |NumPy array| of shape `(fom.input_dim, r)`. c Left tangential directions, |NumPy array| of shape `(fom.output_dim, r)`. Returns ------- lti The reduced-order |LTIModel| interpolating the transfer function of `fom`. """ r = len(sigma) assert isinstance(b, np.ndarray) and b.shape == (self.fom.input_dim, r) assert isinstance(c, np.ndarray) and c.shape == (self.fom.output_dim, r) # rescale tangential directions (to avoid overflow or underflow) if b.shape[0] > 1: for i in range(r): b[:, i] /= spla.norm(b[:, i]) else: b = np.ones((1, r)) if c.shape[0] > 1: for i in range(r): c[:, i] /= spla.norm(c[:, i]) else: c = np.ones((1, r)) # matrices of the interpolatory LTI system Er = np.empty((r, r), dtype=complex) Ar = np.empty((r, r), dtype=complex) Br = np.empty((r, self.fom.input_dim), dtype=complex) Cr = np.empty((self.fom.output_dim, r), dtype=complex) Hs = [self.fom.eval_tf(s) for s in sigma] dHs = [self.fom.eval_dtf(s) for s in sigma] for i in range(r): for j in range(r): if i != j: Er[i, j] = -c[:, i].dot((Hs[i] - Hs[j]).dot(b[:, j])) / (sigma[i] - sigma[j]) Ar[i, j] = -c[:, i].dot((sigma[i] * Hs[i] - sigma[j] * Hs[j])).dot(b[:, j]) / (sigma[i] - sigma[j]) else: Er[i, i] = -c[:, i].dot(dHs[i].dot(b[:, i])) Ar[i, i] = -c[:, i].dot((Hs[i] + sigma[i] * dHs[i]).dot(b[:, i])) Br[i, :] = Hs[i].T.dot(c[:, i]) Cr[:, i] = Hs[i].dot(b[:, i]) # transform the system to have real matrices T = np.zeros((r, r), dtype=complex) for i in range(r): if sigma[i].imag == 0: T[i, i] = 1 else: indices = np.nonzero(np.isclose(sigma[i + 1:], sigma[i].conjugate()))[0] if len(indices) > 0: j = i + 1 + indices[0] T[i, i] = 1 T[i, j] = 1 T[j, i] = -1j T[j, j] = 1j Er = (T.dot(Er).dot(T.conj().T)).real Ar = (T.dot(Ar).dot(T.conj().T)).real Br = (T.dot(Br)).real Cr = (Cr.dot(T.conj().T)).real return LTIModel.from_matrices(Ar, Br, Cr, D=None, E=Er, cont_time=self.fom.cont_time)