def reduce(self, r, projection='bfsr'): """Reduce using SOBT. Parameters ---------- r Order of the reduced model. projection Projection method used: - `'sr'`: square root method - `'bfsr'`: balancing-free square root method (default, since it avoids scaling by singular values and orthogonalizes the projection matrices, which might make it more accurate than the square root method) - `'biorth'`: like the balancing-free square root method, except it biorthogonalizes the projection matrices Returns ------- rom Reduced-order |SecondOrderModel|. """ assert 0 < r < self.fom.order assert projection in ('sr', 'bfsr', 'biorth') # compute all necessary Gramian factors pcf = self.fom.gramian('pc_lrcf', mu=self.mu) pof = self.fom.gramian('po_lrcf', mu=self.mu) vcf = self.fom.gramian('vc_lrcf', mu=self.mu) vof = self.fom.gramian('vo_lrcf', mu=self.mu) if r > min(len(pcf), len(pof), len(vcf), len(vof)): raise ValueError( 'r needs to be smaller than the sizes of Gramian factors.') # find necessary SVDs Up, sp, Vp = spla.svd(pof.inner(pcf), lapack_driver='gesvd') Up = Up.T Uv, sv, Vv = spla.svd(vof.inner(vcf, product=self.fom.M), lapack_driver='gesvd') Uv = Uv.T # compute projection matrices and find the reduced model self.V1 = pcf.lincomb(Vp[:r]) self.W1 = pof.lincomb(Up[:r]) self.V2 = vcf.lincomb(Vv[:r]) self.W2 = vof.lincomb(Uv[:r]) if projection == 'sr': alpha1 = 1 / np.sqrt(sp[:r]) self.V1.scal(alpha1) self.W1.scal(alpha1) alpha2 = 1 / np.sqrt(sv[:r]) self.V2.scal(alpha2) self.W2.scal(alpha2) W1TV1invW1TV2 = self.W1.inner(self.V2) projected_ops = {'M': IdentityOperator(NumpyVectorSpace(r))} elif projection == 'bfsr': gram_schmidt(self.V1, atol=0, rtol=0, copy=False) gram_schmidt(self.W1, atol=0, rtol=0, copy=False) gram_schmidt(self.V2, atol=0, rtol=0, copy=False) gram_schmidt(self.W2, atol=0, rtol=0, copy=False) W1TV1invW1TV2 = spla.solve(self.W1.inner(self.V1), self.W1.inner(self.V2)) projected_ops = { 'M': project(self.fom.M, range_basis=self.W2, source_basis=self.V2) } elif projection == 'biorth': gram_schmidt_biorth(self.V1, self.W1, copy=False) gram_schmidt_biorth(self.V2, self.W2, product=self.fom.M, copy=False) W1TV1invW1TV2 = self.W1.inner(self.V2) projected_ops = {'M': IdentityOperator(NumpyVectorSpace(r))} projected_ops.update({ 'E': project(self.fom.E.assemble(mu=self.mu), range_basis=self.W2, source_basis=self.V2), 'K': project(self.fom.K.assemble(mu=self.mu), range_basis=self.W2, source_basis=self.V1.lincomb(W1TV1invW1TV2.T)), 'B': project(self.fom.B.assemble(mu=self.mu), range_basis=self.W2, source_basis=None), 'Cp': project(self.fom.Cp.assemble(mu=self.mu), range_basis=None, source_basis=self.V1.lincomb(W1TV1invW1TV2.T)), 'Cv': project(self.fom.Cv.assemble(mu=self.mu), range_basis=None, source_basis=self.V2), 'D': self.fom.D.assemble(mu=self.mu), }) rom = SecondOrderModel(name=self.fom.name + '_reduced', **projected_ops) rom.disable_logging() return rom
def build_rom(self, projected_operators, error_estimator): return SecondOrderModel(error_estimator=error_estimator, **projected_operators)
def main( n: int = Argument( 101, help='Order of the full second-order model (odd number).'), r: int = Argument(5, help='Order of the ROMs.'), ): """Parametric string example.""" set_log_levels({'pymor.algorithms.gram_schmidt.gram_schmidt': 'WARNING'}) # Assemble M, D, K, B, C_p assert n % 2 == 1, 'The order has to be an odd integer.' n2 = (n + 1) // 2 k = 0.01 # stiffness M = sps.eye(n, format='csc') E = sps.eye(n, format='csc') K = sps.diags( [n * [2 * k * n**2], (n - 1) * [-k * n**2], (n - 1) * [-k * n**2]], [0, -1, 1], format='csc') B = np.zeros((n, 1)) B[n2 - 1, 0] = n Cp = np.zeros((1, n)) Cp[0, n2 - 1] = 1 # Second-order system Mop = NumpyMatrixOperator(M) Eop = NumpyMatrixOperator(E) * ProjectionParameterFunctional('damping') Kop = NumpyMatrixOperator(K) Bop = NumpyMatrixOperator(B) Cpop = NumpyMatrixOperator(Cp) so_sys = SecondOrderModel(Mop, Eop, Kop, Bop, Cpop) print(f'order of the model = {so_sys.order}') print(f'number of inputs = {so_sys.dim_input}') print(f'number of outputs = {so_sys.dim_output}') mu_list = [1, 5, 10] w = np.logspace(-3, 2, 200) # System poles fig, ax = plt.subplots() for mu in mu_list: poles = so_sys.poles(mu=mu) ax.plot(poles.real, poles.imag, '.', label=fr'$\mu = {mu}$') ax.set_title('System poles') ax.legend() plt.show() # Magnitude plots fig, ax = plt.subplots() for mu in mu_list: so_sys.mag_plot(w, ax=ax, mu=mu, label=fr'$\mu = {mu}$') ax.set_title('Magnitude plot of the full model') ax.legend() plt.show() # "Hankel" singular values fig, ax = plt.subplots(2, 2, figsize=(12, 8), sharey=True, constrained_layout=True) for mu in mu_list: psv = so_sys.psv(mu=mu) vsv = so_sys.vsv(mu=mu) pvsv = so_sys.pvsv(mu=mu) vpsv = so_sys.vpsv(mu=mu) ax[0, 0].semilogy(range(1, len(psv) + 1), psv, '.-', label=fr'$\mu = {mu}$') ax[0, 1].semilogy(range(1, len(vsv) + 1), vsv, '.-') ax[1, 0].semilogy(range(1, len(pvsv) + 1), pvsv, '.-') ax[1, 1].semilogy(range(1, len(vpsv) + 1), vpsv, '.-') ax[0, 0].set_title('Position singular values') ax[0, 1].set_title('Velocity singular values') ax[1, 0].set_title('Position-velocity singular values') ax[1, 1].set_title('Velocity-position singular values') fig.legend(loc='upper center', ncol=len(mu_list)) plt.show() # System norms for mu in mu_list: print(f'mu = {mu}:') print(f' H_2-norm of the full model: {so_sys.h2_norm(mu=mu):e}') if config.HAVE_SLYCOT: print( f' H_inf-norm of the full model: {so_sys.hinf_norm(mu=mu):e}' ) print( f' Hankel-norm of the full model: {so_sys.hankel_norm(mu=mu):e}' ) # Model order reduction run_mor_method_param(so_sys, r, w, mu_list, SOBTpReductor, 'SOBTp') run_mor_method_param(so_sys, r, w, mu_list, SOBTvReductor, 'SOBTv') run_mor_method_param(so_sys, r, w, mu_list, SOBTpvReductor, 'SOBTpv') run_mor_method_param(so_sys, r, w, mu_list, SOBTvpReductor, 'SOBTvp') run_mor_method_param(so_sys, r, w, mu_list, SOBTfvReductor, 'SOBTfv') run_mor_method_param(so_sys, r, w, mu_list, SOBTReductor, 'SOBT') run_mor_method_param(so_sys, r, w, mu_list, SORIRKAReductor, 'SOR-IRKA') run_mor_method_param(so_sys.to_lti(), r, w, mu_list, BTReductor, 'BT') run_mor_method_param(so_sys.to_lti(), r, w, mu_list, IRKAReductor, 'IRKA')