def extend_basis(U, basis, product=None, method='gram_schmidt', pod_modes=1, pod_orthonormalize=True, copy_U=True): assert method in ('trivial', 'gram_schmidt', 'pod') basis_length = len(basis) if method == 'trivial': remove = set() for i in range(len(U)): if np.any(almost_equal(U[i], basis)): remove.add(i) basis.append(U[[i for i in range(len(U)) if i not in remove]], remove_from_other=(not copy_U)) elif method == 'gram_schmidt': basis.append(U, remove_from_other=(not copy_U)) gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False) elif method == 'pod': U_proj_err = U - basis.lincomb(U.inner(basis, product)) basis.append(pod(U_proj_err, modes=pod_modes, product=product, orth_tol=np.inf)[0]) if pod_orthonormalize: gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False) if len(basis) <= basis_length: raise ExtensionError
def build_basis(self): """Compute a reduced basis using proper orthogonal decomposition.""" self.training_data = [] with self.logger.block('Building reduced basis ...'): # compute snapshots for POD and training of neural networks with self.logger.block('Computing training snapshots ...'): U = self.fom.solution_space.empty() for mu in self.training_set: U.append(self.fom.solve(mu)) # compute reduced basis via POD reduced_basis, svals = pod(U, modes=self.basis_size, rtol=self.rtol / 2., atol=self.atol / 2., l2_err=self.l2_err / 2., **(self.pod_params or {})) # determine the coefficients of the full-order solutions in the reduced basis to obtain the # training data; convert everything into tensors that are compatible with PyTorch for mu, u in zip(self.training_set, U): mu_tensor = torch.DoubleTensor(mu.to_numpy()) u_tensor = torch.DoubleTensor(reduced_basis.inner(u)[:, 0]) self.training_data.append((mu_tensor, u_tensor)) # compute mean square loss mean_square_loss = (sum(U.norm2()) - sum(svals**2)) / len(U) return reduced_basis, mean_square_loss
def build_basis(self): """Compute a reduced basis using proper orthogonal decomposition.""" with self.logger.block('Building reduced basis ...'): # compute snapshots for POD and training of neural networks with self.logger.block('Computing training snapshots ...'): U = self.fom.solution_space.empty() for mu in self.training_set: u = self.fom.solve(mu) if hasattr(self, 'nt'): assert self.nt == len(u) else: self.nt = len(u) U.append(u) # compute reduced basis via POD reduced_basis, svals = pod(U, modes=self.basis_size, rtol=self.rtol / 2., atol=self.atol / 2., l2_err=self.l2_err / 2., **(self.pod_params or {})) self.training_data = [] for i, mu in enumerate(self.training_set): sample = self._compute_sample( mu, U[i * self.nt:(i + 1) * self.nt], reduced_basis) self.training_data.extend(sample) # compute mean square loss mean_square_loss = (sum(U.norm2()) - sum(svals**2)) / len(U) return reduced_basis, mean_square_loss
def reduce_pod(d, reductor, snapshots_per_block, basis_size): from pymor.algorithms.pod import pod tic = time.time() training_set = d.parameter_space.sample_uniformly(snapshots_per_block) print('Solving on training set ...') snapshots = d.operator.source.empty(reserve=len(training_set)) for mu in training_set: snapshots.append(d.solve(mu)) print('Performing POD ...') basis, singular_values = pod(snapshots, modes=basis_size, product=reductor.product) print('Reducing ...') reductor.extend_basis(basis, 'trivial') rd = reductor.reduce() elapsed_time = time.time() - tic # generate summary real_rb_size = rd.solution_space.dim training_set_size = len(training_set) summary = '''POD basis generation: size of training set: {training_set_size} prescribed basis size: {basis_size} actual basis size: {real_rb_size} elapsed time: {elapsed_time} '''.format(**locals()) return rd, summary
def extend_basis(U, basis, product=None, method='gram_schmidt', pod_modes=1, pod_orthonormalize=True, copy_U=True): assert method in ('trivial', 'gram_schmidt', 'pod') basis_length = len(basis) if method == 'trivial': remove = set() for i in range(len(U)): if np.any(almost_equal(U[i], basis)): remove.add(i) basis.append(U[[i for i in range(len(U)) if i not in remove]], remove_from_other=(not copy_U)) elif method == 'gram_schmidt': basis.append(U, remove_from_other=(not copy_U)) gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False) elif method == 'pod': U_proj_err = U - basis.lincomb(U.inner(basis, product)) basis.append(pod(U_proj_err, modes=pod_modes, product=product, orthonormalize=False)[0]) if pod_orthonormalize: gram_schmidt(basis, offset=basis_length, product=product, copy=False, check=False) if len(basis) <= basis_length: raise ExtensionError
def reduce_pod(fom, reductor, snapshots_per_block, basis_size): from pymor.algorithms.pod import pod tic = time.time() training_set = fom.parameter_space.sample_uniformly(snapshots_per_block) print('Solving on training set ...') snapshots = fom.operator.source.empty(reserve=len(training_set)) for mu in training_set: snapshots.append(fom.solve(mu)) print('Performing POD ...') basis, singular_values = pod(snapshots, modes=basis_size, product=reductor.products['RB']) print('Reducing ...') reductor.extend_basis(basis, method='trivial') rom = reductor.reduce() elapsed_time = time.time() - tic # generate summary real_rb_size = rom.solution_space.dim training_set_size = len(training_set) summary = f'''POD basis generation: size of training set: {training_set_size} prescribed basis size: {basis_size} actual basis size: {real_rb_size} elapsed time: {elapsed_time} ''' return rom, summary
def default_pod_method(U, eps, is_root_node, product): return pod(U, atol=0., rtol=0., l2_err=eps, product=product, orth_tol=None if is_root_node else np.inf)
def default_pod_method(U, eps, is_root_node, product): return pod(U, atol=0., rtol=0., l2_err=eps, product=product, orthonormalize=is_root_node, check=False)
def test_pod_with_product(operator_with_arrays_and_products, method): _, _, A, _, p, _ = operator_with_arrays_and_products B = A.copy() with log_levels({"pymor.algorithms": "ERROR"}): U, s = pod(A, product=p, method=method) assert np.all(almost_equal(A, B)) assert len(U) == len(s) assert np.allclose(U.gramian(p), np.eye(len(s)))
def test_pod_with_product(operator_with_arrays_and_products, method): _, _, A, _, p, _ = operator_with_arrays_and_products print(type(A)) print(A.dim, len(A)) B = A.copy() U, s = pod(A, product=p, method=method) assert np.all(almost_equal(A, B)) assert len(U) == len(s) assert np.allclose(U.gramian(p), np.eye(len(s)))
def test_pod(vector_array, method): A = vector_array print(type(A)) print(A.dim, len(A)) B = A.copy() U, s = pod(A, method=method) assert np.all(almost_equal(A, B)) assert len(U) == len(s) assert np.allclose(U.gramian(), np.eye(len(s)))
def test_pod(vector_array, method): A = vector_array # TODO assumption here masks a potential issue with the algorithm # where it fails in internal lapack instead of a proper error assume(len(A) > 1 or A.dim > 1) assume(not contains_zero_vector(A, rtol=1e-13, atol=1e-13)) B = A.copy() orth_tol = 1e-10 U, s = pod(A, method=method, orth_tol=orth_tol) assert np.all(almost_equal(A, B)) assert len(U) == len(s) assert np.allclose(U.gramian(), np.eye(len(s)), atol=orth_tol)
def hapod_demo(args): args['--grid'] = int(args['--grid']) args['--nt'] = int(args['--nt']) args['--omega'] = float(args['--omega']) args['--procs'] = int(args['--procs']) args['--snap'] = int(args['--snap']) args['--threads'] = int(args['--threads']) args['TOL'] = float(args['TOL']) args['DIST'] = int(args['DIST']) args['INC'] = int(args['INC']) assert args['--procs'] == 0 or args['--threads'] == 0 tol = args['TOL'] omega = args['--omega'] executor = ProcessPoolExecutor(args['--procs']) if args['--procs'] > 0 else \ ThreadPoolExecutor(args['--threads']) if args['--threads'] > 0 else \ None p = burgers_problem_2d() d, data = discretize_instationary_fv(p, grid_type=RectGrid, diameter=np.sqrt(2)/args['--grid'], nt=args['--nt']) U = d.solution_space.empty() for mu in d.parameter_space.sample_randomly(args['--snap']): U.append(d.solve(mu)) tic = time() pod_modes = pod(U, l2_err=tol * np.sqrt(len(U)), product=d.l2_product, check=False)[0] pod_time = time() - tic tic = time() dist_modes = dist_vectorarray_hapod(args['DIST'], U, tol, omega, product=d.l2_product, executor=executor)[0] dist_time = time() - tic tic = time() inc_modes = inc_vectorarray_hapod(args['INC'], U, tol, omega, product=d.l2_product)[0] inc_time = time() - tic print('Snapshot matrix: {} x {}'.format(U.dim, len(U))) print(format_table([ ['Method', 'Error', 'Modes', 'Time'], ['POD', np.linalg.norm(d.l2_norm(U-pod_modes.lincomb(d.l2_product.apply2(U, pod_modes)))/np.sqrt(len(U))), len(pod_modes), pod_time], ['DIST HAPOD', np.linalg.norm(d.l2_norm(U-dist_modes.lincomb(d.l2_product.apply2(U, dist_modes)))/np.sqrt(len(U))), len(dist_modes), dist_time], ['INC HAPOD', np.linalg.norm(d.l2_norm(U-inc_modes.lincomb(d.l2_product.apply2(U, inc_modes)))/np.sqrt(len(U))), len(inc_modes), inc_time]] ))
def reduce_pod(d, reductor, snapshots_per_block, product_name, basis_size): from pymor.algorithms.pod import pod tic = time.time() training_set = d.parameter_space.sample_uniformly(snapshots_per_block) print("Solving on training set ...") snapshots = d.operator.source.empty(reserve=len(training_set)) for mu in training_set: snapshots.append(d.solve(mu)) if product_name == "h1": pod_product = d.h1_0_semi_product elif product_name == "euclidean": pod_product = None else: assert False print("Performing POD ...") basis, singular_values = pod(snapshots, modes=basis_size, product=pod_product) print("Reducing ...") rd, rc, _ = reductor(d, basis) elapsed_time = time.time() - tic # generate summary real_rb_size = rd.solution_space.dim training_set_size = len(training_set) summary = """POD basis generation: size of training set: {training_set_size} inner product for POD: {product_name} prescribed basis size: {basis_size} actual basis size: {real_rb_size} elapsed time: {elapsed_time} """.format( **locals() ) return rd, rc, summary
def reduce_pod(d, reductor, snapshots_per_block, product_name, basis_size): from pymor.algorithms.pod import pod tic = time.time() training_set = d.parameter_space.sample_uniformly(snapshots_per_block) print('Solving on training set ...') snapshots = d.operator.source.empty(reserve=len(training_set)) for mu in training_set: snapshots.append(d.solve(mu)) if product_name == 'h1': pod_product = d.h1_0_semi_product elif product_name == 'euclidean': pod_product = None else: assert False print('Performing POD ...') basis, singular_values = pod(snapshots, modes=basis_size, product=pod_product) print('Reducing ...') rd, rc, _ = reductor(d, basis) elapsed_time = time.time() - tic # generate summary real_rb_size = rd.solution_space.dim training_set_size = len(training_set) summary = '''POD basis generation: size of training set: {training_set_size} inner product for POD: {product_name} prescribed basis size: {basis_size} actual basis size: {real_rb_size} elapsed time: {elapsed_time} '''.format(**locals()) return rd, rc, summary
def thermalblock_demo(args): args['XBLOCKS'] = int(args['XBLOCKS']) args['YBLOCKS'] = int(args['YBLOCKS']) args['--grid'] = int(args['--grid']) args['SNAPSHOTS'] = int(args['SNAPSHOTS']) args['RBSIZE'] = int(args['RBSIZE']) args['--test'] = int(args['--test']) args['--pod-norm'] = args['--pod-norm'].lower() assert args['--pod-norm'] in {'trivial', 'h1'} print('Solving on TriaGrid(({0},{0}))'.format(args['--grid'])) print('Setup Problem ...') problem = ThermalBlockProblem(num_blocks=(args['XBLOCKS'], args['YBLOCKS'])) print('Discretize ...') discretization, _ = discretize_elliptic_cg(problem, diameter=1. / args['--grid']) print('The parameter type is {}'.format(discretization.parameter_type)) if args['--plot-solutions']: print('Showing some solutions') Us = tuple() legend = tuple() for mu in discretization.parameter_space.sample_randomly(2): print('Solving for diffusion = \n{} ... '.format(mu['diffusion'])) sys.stdout.flush() Us = Us + (discretization.solve(mu),) legend = legend + (str(mu['diffusion']),) discretization.visualize(Us, legend=legend, title='Detailed Solutions for different parameters', block=True) print('RB generation ...') tic = time.time() print('Solving on training set ...') S_train = list(discretization.parameter_space.sample_uniformly(args['SNAPSHOTS'])) snapshots = discretization.operator.source.empty(reserve=len(S_train)) for mu in S_train: snapshots.append(discretization.solve(mu)) print('Performing POD ...') pod_product = discretization.h1_product if args['--pod-norm'] == 'h1' else None rb = pod(snapshots, modes=args['RBSIZE'], product=pod_product)[0] print('Reducing ...') reductor = reduce_generic_rb rb_discretization, reconstructor, _ = reductor(discretization, rb) toc = time.time() t_offline = toc - tic print('\nSearching for maximum error on random snapshots ...') tic = time.time() h1_err_max = -1 cond_max = -1 for mu in discretization.parameter_space.sample_randomly(args['--test']): print('Solving RB-Scheme for mu = {} ... '.format(mu), end='') URB = reconstructor.reconstruct(rb_discretization.solve(mu)) U = discretization.solve(mu) h1_err = discretization.h1_norm(U - URB)[0] cond = np.linalg.cond(rb_discretization.operator.assemble(mu)._matrix) if h1_err > h1_err_max: h1_err_max = h1_err mumax = mu if cond > cond_max: cond_max = cond cond_max_mu = mu print('H1-error = {}, condition = {}'.format(h1_err, cond)) toc = time.time() t_est = toc - tic real_rb_size = len(rb) print(''' *** RESULTS *** Problem: number of blocks: {args[XBLOCKS]}x{args[YBLOCKS]} h: sqrt(2)/{args[--grid]} POD basis generation: number of snapshots: {args[SNAPSHOTS]}^({args[XBLOCKS]}x{args[YBLOCKS]}) pod norm: {args[--pod-norm]} prescribed basis size: {args[RBSIZE]} actual basis size: {real_rb_size} elapsed time: {t_offline} Stochastic error estimation: number of samples: {args[--test]} maximal H1-error: {h1_err_max} (mu = {mumax}) maximal condition of system matrix: {cond_max} (mu = {cond_max_mu}) elapsed time: {t_est} '''.format(**locals())) sys.stdout.flush() if args['--plot-err']: discretization.visualize((U, URB, U - URB), legend=('Detailed Solution', 'Reduced Solution', 'Error'), title='Maximum Error Solution', separate_colorbars=True, block=True)
def reduce_pod(fom, reductor, snapshots, basis_size): basis, singular_values = pod(snapshots, modes=basis_size) reductor.extend_basis(basis, method='trivial') rom = reductor.reduce() return rom
fom = InstationaryModel(T=120., initial_data=q0_array, operator=op, rhs=None, mass=None, visualizer=vis, time_stepper=ts, num_values=20) # Constant Timesteps dt = fom.T / fom.time_stepper.nt problem.claw.solver.dt = dt U = fom.solve(mu=None) # Solve modes, svals = pod(U) print(np.shape(modes.to_numpy())) print(svals) ## Write ROM to File # writeROMtoFile(U, modes, outDir) # Plot the singular values plt.figure() plt.semilogy(svals, linewidth=2, label='Singular Values') plt.legend() plt.show() fom.visualize(U) # plot the solution num_output_times = fom.time_stepper.nt
def hapod_demo(args): args['--grid'] = int(args['--grid']) args['--nt'] = int(args['--nt']) args['--omega'] = float(args['--omega']) args['--procs'] = int(args['--procs']) args['--snap'] = int(args['--snap']) args['--threads'] = int(args['--threads']) args['TOL'] = float(args['TOL']) args['DIST'] = int(args['DIST']) args['INC'] = int(args['INC']) assert args['--procs'] == 0 or args['--threads'] == 0 tol = args['TOL'] omega = args['--omega'] executor = ProcessPoolExecutor(args['--procs']) if args['--procs'] > 0 else \ ThreadPoolExecutor(args['--threads']) if args['--threads'] > 0 else \ None p = burgers_problem_2d() m, data = discretize_instationary_fv(p, grid_type=RectGrid, diameter=np.sqrt(2) / args['--grid'], nt=args['--nt']) U = m.solution_space.empty() for mu in m.parameter_space.sample_randomly(args['--snap']): U.append(m.solve(mu)) tic = time() pod_modes = pod(U, l2_err=tol * np.sqrt(len(U)), product=m.l2_product, check=False)[0] pod_time = time() - tic tic = time() dist_modes = dist_vectorarray_hapod(args['DIST'], U, tol, omega, product=m.l2_product, executor=executor)[0] dist_time = time() - tic tic = time() inc_modes = inc_vectorarray_hapod(args['INC'], U, tol, omega, product=m.l2_product)[0] inc_time = time() - tic print(f'Snapshot matrix: {U.dim} x {len(U)}') print( format_table([ ['Method', 'Error', 'Modes', 'Time'], [ 'POD', np.linalg.norm( m.l2_norm(U - pod_modes.lincomb( m.l2_product.apply2(U, pod_modes))) / np.sqrt(len(U))), len(pod_modes), pod_time ], [ 'DIST HAPOD', np.linalg.norm( m.l2_norm(U - dist_modes.lincomb( m.l2_product.apply2(U, dist_modes))) / np.sqrt(len(U))), len(dist_modes), dist_time ], [ 'INC HAPOD', np.linalg.norm( m.l2_norm(U - inc_modes.lincomb( m.l2_product.apply2(U, inc_modes))) / np.sqrt(len(U))), len(inc_modes), inc_time ] ]))
visualizer=visualizer, name='C++-Discretization', cache_region=None) return d # discretize d = discretize(50, 10000, 4) # generate solution snapshots snapshots = d.solution_space.empty() for mu in d.parameter_space.sample_uniformly(2): snapshots.append(d.solve(mu)) # apply POD reduced_basis = pod(snapshots, 4)[0] # reduce the model rd, rc, _ = reduce_generic_rb(d, reduced_basis) # stochastic error estimation mu_max = None err_max = -1. for mu in d.parameter_space.sample_randomly(10): U_RB = (rc.reconstruct(rd.solve(mu))) U = d.solve(mu) err = np.max((U_RB - U).l2_norm()) if err > err_max: err_max = err mu_max = mu
def deim(U, modes=None, error_norm=None, product=None): """Generate data for empirical interpolation using DEIM algorithm. Given a |VectorArray| `U`, this method generates a collateral basis and interpolation DOFs for empirical interpolation of the vectors contained in `U`. The returned objects can also be used to instantiate an |EmpiricalInterpolatedOperator|. The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`. Parameters ---------- U A |VectorArray| of vectors to interpolate. modes Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`. error_norm Norm w.r.t. which to calculate the interpolation error. If `None`, the Euclidean norm is used. product Product |Operator| used for POD. Returns ------- interpolation_dofs |NumPy array| of the DOFs at which the vectors are interpolated. collateral_basis |VectorArray| containing the generated collateral basis. data Dict containing the following fields: :errors: Sequence of maximum approximation errors during greedy search. """ assert isinstance(U, VectorArrayInterface) logger = getLogger("pymor.algorithms.ei.deim") logger.info("Generating Interpolation Data ...") collateral_basis = pod(U, modes, product=product)[0] interpolation_dofs = np.zeros((0,), dtype=np.int32) interpolation_matrix = np.zeros((0, 0)) errs = [] for i in xrange(len(collateral_basis)): if len(interpolation_dofs) > 0: coefficients = np.linalg.solve( interpolation_matrix, collateral_basis.components(interpolation_dofs, ind=i).T ).T U_interpolated = collateral_basis.lincomb(coefficients, ind=range(len(interpolation_dofs))) ERR = collateral_basis.copy(ind=i) ERR -= U_interpolated else: ERR = collateral_basis.copy(ind=i) err = ERR.l2_norm() if error_norm is None else error_norm(ERR) logger.info("Interpolation error for basis vector {}: {}".format(i, err)) # compute new interpolation dof and collateral basis vector new_dof = ERR.amax()[0][0] if new_dof in interpolation_dofs: logger.info("DOF {} selected twice for interplation! Stopping extension loop.".format(new_dof)) break interpolation_dofs = np.hstack((interpolation_dofs, new_dof)) interpolation_matrix = collateral_basis.components(interpolation_dofs, ind=range(len(interpolation_dofs))).T errs.append(err) logger.info("") if len(interpolation_dofs) < len(collateral_basis): collateral_basis.remove(ind=range(len(interpolation_dofs), len(collateral_basis))) logger.info("Finished.".format(new_dof)) data = {"errors": errs} return interpolation_dofs, collateral_basis, data
def fenics_nonlinear_demo(args): DIM = int(args['DIM']) N = int(args['N']) ORDER = int(args['ORDER']) from pymor.tools import mpi if mpi.parallel: from pymor.models.mpi import mpi_wrap_model local_models = mpi.call(mpi.function_call_manage, discretize, DIM, N, ORDER) fom = mpi_wrap_model(local_models, use_with=True, pickle_local_spaces=False) else: fom = discretize(DIM, N, ORDER) # ### ROM generation (POD/DEIM) from pymor.algorithms.ei import ei_greedy from pymor.algorithms.newton import newton from pymor.algorithms.pod import pod from pymor.operators.ei import EmpiricalInterpolatedOperator from pymor.reductors.basic import StationaryRBReductor U = fom.solution_space.empty() residuals = fom.solution_space.empty() for mu in fom.parameter_space.sample_uniformly(10): UU, data = newton(fom.operator, fom.rhs.as_vector(), mu=mu, rtol=1e-6, return_residuals=True) U.append(UU) residuals.append(data['residuals']) dofs, cb, _ = ei_greedy(residuals, rtol=1e-7) ei_op = EmpiricalInterpolatedOperator(fom.operator, collateral_basis=cb, interpolation_dofs=dofs, triangular=True) rb, svals = pod(U, rtol=1e-7) fom_ei = fom.with_(operator=ei_op) reductor = StationaryRBReductor(fom_ei, rb) rom = reductor.reduce() # the reductor currently removes all solver_options so we need to add them again rom = rom.with_(operator=rom.operator.with_( solver_options=fom.operator.solver_options)) # ### ROM validation import time import numpy as np # ensure that FFC is not called during runtime measurements rom.solve(1) errs = [] speedups = [] for mu in fom.parameter_space.sample_randomly(10): tic = time.time() U = fom.solve(mu) t_fom = time.time() - tic tic = time.time() u_red = rom.solve(mu) t_rom = time.time() - tic U_red = reductor.reconstruct(u_red) errs.append(((U - U_red).l2_norm() / U.l2_norm())[0]) speedups.append(t_fom / t_rom) print(f'Maximum relative ROM error: {max(errs)}') print(f'Median of ROM speedup: {np.median(speedups)}')
def deim(U, modes=None, error_norm=None, product=None): """Generate data for empirical interpolation using DEIM algorithm. Given a |VectorArray| `U`, this method generates a collateral basis and interpolation DOFs for empirical interpolation of the vectors contained in `U`. The returned objects can be used to instantiate an |EmpiricalInterpolatedOperator| (with `triangular=False`). The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`. Parameters ---------- U A |VectorArray| of vectors to interpolate. modes Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`. error_norm Norm w.r.t. which to calculate the interpolation error. If `None`, the Euclidean norm is used. product Inner product |Operator| used for the POD. Returns ------- interpolation_dofs |NumPy array| of the DOFs at which the vectors are interpolated. collateral_basis |VectorArray| containing the generated collateral basis. data Dict containing the following fields: :errors: Sequence of maximum approximation errors during greedy search. """ assert isinstance(U, VectorArrayInterface) logger = getLogger('pymor.algorithms.ei.deim') logger.info('Generating Interpolation Data ...') collateral_basis = pod(U, modes, product=product)[0] interpolation_dofs = np.zeros((0,), dtype=np.int32) interpolation_matrix = np.zeros((0, 0)) errs = [] for i in range(len(collateral_basis)): if len(interpolation_dofs) > 0: coefficients = np.linalg.solve(interpolation_matrix, collateral_basis[i].dofs(interpolation_dofs).T).T U_interpolated = collateral_basis[:len(interpolation_dofs)].lincomb(coefficients) ERR = collateral_basis[i].copy() ERR -= U_interpolated else: ERR = collateral_basis[i].copy() err = np.max(ERR.l2_norm() if error_norm is None else error_norm(ERR)) logger.info('Interpolation error for basis vector {}: {}'.format(i, err)) # compute new interpolation dof and collateral basis vector new_dof = ERR.amax()[0][0] if new_dof in interpolation_dofs: logger.info('DOF {} selected twice for interplation! Stopping extension loop.'.format(new_dof)) break interpolation_dofs = np.hstack((interpolation_dofs, new_dof)) interpolation_matrix = collateral_basis[:len(interpolation_dofs)].dofs(interpolation_dofs).T errs.append(err) logger.info('') if len(interpolation_dofs) < len(collateral_basis): del collateral_basis[len(interpolation_dofs):len(collateral_basis)] logger.info('Finished.') data = {'errors': errs} return interpolation_dofs, collateral_basis, data
def main( dim: int = Argument(..., help='Spatial dimension of the problem.'), n: int = Argument( ..., help='Number of mesh intervals per spatial dimension.'), order: int = Argument(..., help='Finite element order.'), ): """Reduces a FEniCS-based nonlinear diffusion problem using POD/DEIM.""" from pymor.tools import mpi if mpi.parallel: from pymor.models.mpi import mpi_wrap_model local_models = mpi.call(mpi.function_call_manage, discretize, dim, n, order) fom = mpi_wrap_model(local_models, use_with=True, pickle_local_spaces=False) else: fom = discretize(dim, n, order) parameter_space = fom.parameters.space((0, 1000.)) # ### ROM generation (POD/DEIM) from pymor.algorithms.ei import ei_greedy from pymor.algorithms.newton import newton from pymor.algorithms.pod import pod from pymor.operators.ei import EmpiricalInterpolatedOperator from pymor.reductors.basic import StationaryRBReductor U = fom.solution_space.empty() residuals = fom.solution_space.empty() for mu in parameter_space.sample_uniformly(10): UU, data = newton(fom.operator, fom.rhs.as_vector(), mu=mu, rtol=1e-6, return_residuals=True) U.append(UU) residuals.append(data['residuals']) dofs, cb, _ = ei_greedy(residuals, rtol=1e-7) ei_op = EmpiricalInterpolatedOperator(fom.operator, collateral_basis=cb, interpolation_dofs=dofs, triangular=True) rb, svals = pod(U, rtol=1e-7) fom_ei = fom.with_(operator=ei_op) reductor = StationaryRBReductor(fom_ei, rb) rom = reductor.reduce() # the reductor currently removes all solver_options so we need to add them again rom = rom.with_(operator=rom.operator.with_( solver_options=fom.operator.solver_options)) # ### ROM validation import time import numpy as np # ensure that FFC is not called during runtime measurements rom.solve(1) errs = [] speedups = [] for mu in parameter_space.sample_randomly(10): tic = time.perf_counter() U = fom.solve(mu) t_fom = time.perf_counter() - tic tic = time.perf_counter() u_red = rom.solve(mu) t_rom = time.perf_counter() - tic U_red = reductor.reconstruct(u_red) errs.append(((U - U_red).norm() / U.norm())[0]) speedups.append(t_fom / t_rom) print(f'Maximum relative ROM error: {max(errs)}') print(f'Median of ROM speedup: {np.median(speedups)}')
time_stepper=time_stepper, num_values=20, visualizer=visualizer, name='C++-Model') return fom # discretize fom = discretize(50, 10000, 4) parameter_space = fom.parameters.space(0.1, 1) # generate solution snapshots snapshots = fom.solution_space.empty() for mu in parameter_space.sample_uniformly(2): snapshots.append(fom.solve(mu)) # apply POD reduced_basis = pod(snapshots, modes=4)[0] # reduce the model reductor = InstationaryRBReductor(fom, reduced_basis, check_orthonormality=True) rom = reductor.reduce() # stochastic error estimation mu_max = None err_max = -1. for mu in parameter_space.sample_randomly(10): U_RB = (reductor.reconstruct(rom.solve(mu))) U = fom.solve(mu) err = np.max((U_RB-U).norm()) if err > err_max: err_max = err mu_max = mu
def extend_basis(self, U, method='gram_schmidt', pod_modes=1, pod_orthonormalize=True, copy_U=True): """Extend basis by new vectors. Parameters ---------- U |VectorArray| containing the new basis vectors. method Basis extension method to use. The following methods are available: :trivial: Vectors in `U` are appended to the basis. Duplicate vectors in the sense of :func:`~pymor.algorithms.basic.almost_equal` are removed. :gram_schmidt: New basis vectors are orthonormalized w.r.t. to the old basis using the :func:`~pymor.algorithms.gram_schmidt.gram_schmidt` algorithm. :pod: Append the first POD modes of the defects of the projections of the vectors in U onto the existing basis (e.g. for use in POD-Greedy algorithm). .. warning:: In case of the `'gram_schmidt'` and `'pod'` extension methods, the existing reduced basis is assumed to be orthonormal w.r.t. the given inner product. pod_modes In case `method == 'pod'`, the number of POD modes that shall be appended to the basis. pod_orthonormalize If `True` and `method == 'pod'`, re-orthonormalize the new basis vectors obtained by the POD in order to improve numerical accuracy. copy_U If `copy_U` is `False`, the new basis vectors might be removed from `U`. Raises ------ ExtensionError Raised when the selected extension method does not yield a basis of increased dimension. """ assert method in ('trivial', 'gram_schmidt', 'pod') basis_length = len(self.RB) if method == 'trivial': remove = set() for i in range(len(U)): if np.any(almost_equal(U[i], self.RB)): remove.add(i) self.RB.append(U[[i for i in range(len(U)) if i not in remove]], remove_from_other=(not copy_U)) elif method == 'gram_schmidt': self.RB.append(U, remove_from_other=(not copy_U)) gram_schmidt(self.RB, offset=basis_length, product=self.product, copy=False) elif method == 'pod': if self.product is None: U_proj_err = U - self.RB.lincomb(U.dot(self.RB)) else: U_proj_err = U - self.RB.lincomb(self.product.apply2(U, self.RB)) self.RB.append(pod(U_proj_err, modes=pod_modes, product=self.product, orthonormalize=False)[0]) if pod_orthonormalize: gram_schmidt(self.RB, offset=basis_length, product=self.product, copy=False) if len(self.RB) <= basis_length: raise ExtensionError
def deim(U, modes=None, atol=None, rtol=None, product=None, pod_options={}): """Generate data for empirical interpolation using DEIM algorithm. Given a |VectorArray| `U`, this method generates a collateral basis and interpolation DOFs for empirical interpolation of the vectors contained in `U`. The returned objects can be used to instantiate an |EmpiricalInterpolatedOperator| (with `triangular=False`). The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`. Parameters ---------- U A |VectorArray| of vectors to interpolate. modes Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`. atol Absolute POD tolerance. rtol Relative POD tolerance. product Inner product |Operator| used for the POD. pod_options Dictionary of additional options to pass to the :func:`~pymor.algorithms.pod.pod` algorithm. Returns ------- interpolation_dofs |NumPy array| of the DOFs at which the vectors are interpolated. collateral_basis |VectorArray| containing the generated collateral basis. data Dict containing the following fields: :svals: POD singular values. """ assert isinstance(U, VectorArrayInterface) logger = getLogger('pymor.algorithms.ei.deim') logger.info('Generating Interpolation Data ...') collateral_basis, svals = pod(U, modes=modes, atol=atol, rtol=rtol, product=product, **pod_options) interpolation_dofs = np.zeros((0, ), dtype=np.int32) interpolation_matrix = np.zeros((0, 0)) for i in range(len(collateral_basis)): logger.info(f'Choosing interpolation point for basis vector {i}.') if len(interpolation_dofs) > 0: coefficients = solve( interpolation_matrix, collateral_basis[i].dofs(interpolation_dofs).T).T U_interpolated = collateral_basis[:len(interpolation_dofs )].lincomb(coefficients) ERR = collateral_basis[i].copy() ERR -= U_interpolated else: ERR = collateral_basis[i].copy() # compute new interpolation dof and collateral basis vector new_dof = ERR.amax()[0][0] if new_dof in interpolation_dofs: logger.info( f'DOF {new_dof} selected twice for interplation! Stopping extension loop.' ) break interpolation_dofs = np.hstack((interpolation_dofs, new_dof)) interpolation_matrix = collateral_basis[:len(interpolation_dofs)].dofs( interpolation_dofs).T if len(interpolation_dofs) < len(collateral_basis): del collateral_basis[len(interpolation_dofs):len(collateral_basis)] logger.info('Finished.') data = {'svals': svals} return interpolation_dofs, collateral_basis, data
def pod_basis_extension(basis, U, count=1, copy_basis=True, product=None, orthonormalize=True): """Extend basis with the first `count` POD modes of the projection of `U` onto the orthogonal complement of the basis. Note that the provided basis is assumed to be orthonormal w.r.t. the provided scalar product! Parameters ---------- basis |VectorArray| containing the basis to extend. The basis is expected to be orthonormal w.r.t. `product`. U |VectorArray| containing the vectors to which the POD is applied. count Number of POD modes that are to be appended to the basis. product The scalar product w.r.t. which to orthonormalize; if `None`, the Euclidean product is used. copy_basis If `copy_basis` is `False`, the old basis is extended in-place. orthonormalize If `True`, re-orthonormalize the new basis vectors obtained by the POD in order to improve numerical accuracy. Returns ------- new_basis The extended basis. extension_data Dict containing the following fields: :hierarchic: `True` if `new_basis` contains `basis` as its first vectors. Raises ------ ExtensionError POD produces no new vectors. This is the case when no vector in `U` is linearly independent from the basis. """ if basis is None: return pod(U, modes=count, product=product)[0], {'hierarchic': True} basis_length = len(basis) new_basis = basis.copy() if copy_basis else basis if product is None: U_proj_err = U - basis.lincomb(U.dot(basis)) else: U_proj_err = U - basis.lincomb(product.apply2(U, basis)) new_basis.append(pod(U_proj_err, modes=count, product=product, orthonormalize=False)[0]) if orthonormalize: gram_schmidt(new_basis, offset=len(basis), product=product, copy=False) if len(new_basis) <= basis_length: raise ExtensionError return new_basis, {'hierarchic': True}
def deim(U, modes=None, atol=None, rtol=None, product=None, pod_options={}): """Generate data for empirical interpolation using DEIM algorithm. Given a |VectorArray| `U`, this method generates a collateral basis and interpolation DOFs for empirical interpolation of the vectors contained in `U`. The returned objects can be used to instantiate an |EmpiricalInterpolatedOperator| (with `triangular=False`). The collateral basis is determined by the first :func:`~pymor.algorithms.pod.pod` modes of `U`. Parameters ---------- U A |VectorArray| of vectors to interpolate. modes Dimension of the collateral basis i.e. number of POD modes of the vectors in `U`. atol Absolute POD tolerance. rtol Relative POD tolerance. product Inner product |Operator| used for the POD. pod_options Dictionary of additional options to pass to the :func:`~pymor.algorithms.pod.pod` algorithm. Returns ------- interpolation_dofs |NumPy array| of the DOFs at which the vectors are interpolated. collateral_basis |VectorArray| containing the generated collateral basis. data Dict containing the following fields: :svals: POD singular values. """ assert isinstance(U, VectorArrayInterface) logger = getLogger('pymor.algorithms.ei.deim') logger.info('Generating Interpolation Data ...') collateral_basis, svals = pod(U, modes=modes, atol=atol, rtol=rtol, product=product, **pod_options) interpolation_dofs = np.zeros((0,), dtype=np.int32) interpolation_matrix = np.zeros((0, 0)) for i in range(len(collateral_basis)): logger.info('Choosing interpolation point for basis vector {}.'.format(i)) if len(interpolation_dofs) > 0: coefficients = np.linalg.solve(interpolation_matrix, collateral_basis[i].dofs(interpolation_dofs).T).T U_interpolated = collateral_basis[:len(interpolation_dofs)].lincomb(coefficients) ERR = collateral_basis[i].copy() ERR -= U_interpolated else: ERR = collateral_basis[i].copy() # compute new interpolation dof and collateral basis vector new_dof = ERR.amax()[0][0] if new_dof in interpolation_dofs: logger.info('DOF {} selected twice for interplation! Stopping extension loop.'.format(new_dof)) break interpolation_dofs = np.hstack((interpolation_dofs, new_dof)) interpolation_matrix = collateral_basis[:len(interpolation_dofs)].dofs(interpolation_dofs).T if len(interpolation_dofs) < len(collateral_basis): del collateral_basis[len(interpolation_dofs):len(collateral_basis)] logger.info('Finished.') data = {'svals': svals} return interpolation_dofs, collateral_basis, data
def extend_basis(self, U, method='gram_schmidt', pod_modes=1, pod_orthonormalize=True, copy_U=True): """Extend basis by new vectors. Parameters ---------- U |VectorArray| containing the new basis vectors. method Basis extension method to use. The following methods are available: :trivial: Vectors in `U` are appended to the basis. Duplicate vectors in the sense of :func:`~pymor.algorithms.basic.almost_equal` are removed. :gram_schmidt: New basis vectors are orthonormalized w.r.t. to the old basis using the :func:`~pymor.algorithms.gram_schmidt.gram_schmidt` algorithm. :pod: Append the first POD modes of the defects of the projections of the vectors in U onto the existing basis (e.g. for use in POD-Greedy algorithm). .. warning:: In case of the `'gram_schmidt'` and `'pod'` extension methods, the existing reduced basis is assumed to be orthonormal w.r.t. the given inner product. pod_modes In case `method == 'pod'`, the number of POD modes that shall be appended to the basis. pod_orthonormalize If `True` and `method == 'pod'`, re-orthonormalize the new basis vectors obtained by the POD in order to improve numerical accuracy. copy_U If `copy_U` is `False`, the new basis vectors might be removed from `U`. Raises ------ ExtensionError Raised when the selected extension method does not yield a basis of increased dimension. """ assert method in ('trivial', 'gram_schmidt', 'pod') basis_length = len(self.RB) if method == 'trivial': remove = set() for i in range(len(U)): if np.any(almost_equal(U[i], self.RB)): remove.add(i) self.RB.append(U[[i for i in range(len(U)) if i not in remove]], remove_from_other=(not copy_U)) elif method == 'gram_schmidt': self.RB.append(U, remove_from_other=(not copy_U)) gram_schmidt(self.RB, offset=basis_length, product=self.product, copy=False) elif method == 'pod': if self.product is None: U_proj_err = U - self.RB.lincomb(U.dot(self.RB)) else: U_proj_err = U - self.RB.lincomb( self.product.apply2(U, self.RB)) self.RB.append( pod(U_proj_err, modes=pod_modes, product=self.product, orthonormalize=False)[0]) if pod_orthonormalize: gram_schmidt(self.RB, offset=basis_length, product=self.product, copy=False) if len(self.RB) <= basis_length: raise ExtensionError
d = InstationaryDiscretization(T=1e-0, operator=operator, rhs=rhs, initial_data=initial_data, time_stepper=time_stepper, num_values=20, parameter_space=parameter_space, visualizer=visualizer, name='C++-Discretization', cache_region=None) return d # discretize d = discretize(50, 10000, 4) # generate solution snapshots snapshots = d.solution_space.empty() for mu in d.parameter_space.sample_uniformly(2): snapshots.append(d.solve(mu)) # apply POD reduced_basis = pod(snapshots, 4)[0] # reduce the model rd, rc, _ = reduce_generic_rb(d, reduced_basis) # stochastic error estimation mu_max = None err_max = -1. for mu in d.parameter_space.sample_randomly(10): U_RB = (rc.reconstruct(rd.solve(mu))) U = d.solve(mu) err = np.max((U_RB-U).l2_norm()) if err > err_max: err_max = err mu_max = mu
def main( tol: float = Argument(..., help='Prescribed mean l2 approximation error.'), dist: int = Argument(..., help='Number of slices for distributed HAPOD.'), inc: int = Argument(..., help='Number of steps for incremental HAPOD.'), grid: int = Option(60, help='Use grid with (2*NI)*NI elements.'), nt: int = Option(100, help='Number of time steps.'), omega: float = Option(0.9, help='Parameter omega from HAPOD algorithm.'), procs: int = Option( 0, help='Number of processes to use for parallelization.'), snap: int = Option(20, help='Number of snapshot trajectories to compute.'), threads: int = Option( 0, help='Number of threads to use for parallelization.'), ): """Compression of snapshot data with the HAPOD algorithm from [HLR18].""" assert procs == 0 or threads == 0 executor = ProcessPoolExecutor(procs) if procs > 0 else \ ThreadPoolExecutor(threads) if threads > 0 else \ None p = burgers_problem_2d() m, data = discretize_instationary_fv(p, grid_type=RectGrid, diameter=np.sqrt(2) / grid, nt=nt) U = m.solution_space.empty() for mu in p.parameter_space.sample_randomly(snap): U.append(m.solve(mu)) tic = time.perf_counter() pod_modes = pod(U, l2_err=tol * np.sqrt(len(U)), product=m.l2_product)[0] pod_time = time.perf_counter() - tic tic = time.perf_counter() dist_modes = dist_vectorarray_hapod(dist, U, tol, omega, product=m.l2_product, executor=executor)[0] dist_time = time.perf_counter() - tic tic = time.perf_counter() inc_modes = inc_vectorarray_hapod(inc, U, tol, omega, product=m.l2_product)[0] inc_time = time.perf_counter() - tic print(f'Snapshot matrix: {U.dim} x {len(U)}') print( format_table([ ['Method', 'Error', 'Modes', 'Time'], [ 'POD', np.linalg.norm( m.l2_norm(U - pod_modes.lincomb( m.l2_product.apply2(U, pod_modes))) / np.sqrt(len(U))), len(pod_modes), pod_time ], [ 'DIST HAPOD', np.linalg.norm( m.l2_norm(U - dist_modes.lincomb( m.l2_product.apply2(U, dist_modes))) / np.sqrt(len(U))), len(dist_modes), dist_time ], [ 'INC HAPOD', np.linalg.norm( m.l2_norm(U - inc_modes.lincomb( m.l2_product.apply2(U, inc_modes))) / np.sqrt(len(U))), len(inc_modes), inc_time ] ]))