def build_rom(self, projected_operators, error_estimator): fom = self.fom return InstationaryModel(T=fom.T, time_stepper=fom.time_stepper, num_values=fom.num_values, error_estimator=error_estimator, **projected_operators)
def discretize(n, nt, blocks): h = 1. / blocks ops = [WrappedDiffusionOperator.create(n, h * i, h * (i + 1)) for i in range(blocks)] pfs = [ProjectionParameterFunctional('diffusion_coefficients', blocks, i) for i in range(blocks)] operator = LincombOperator(ops, pfs) initial_data = operator.source.zeros() # use data property of WrappedVector to setup rhs # note that we cannot use the data property of ListVectorArray, # since ListVectorArray will always return a copy rhs_vec = operator.range.zeros() rhs_data = rhs_vec._list[0].to_numpy() rhs_data[:] = np.ones(len(rhs_data)) rhs_data[0] = 0 rhs_data[len(rhs_data) - 1] = 0 rhs = VectorOperator(rhs_vec) # hack together a visualizer ... grid = OnedGrid(domain=(0, 1), num_intervals=n) visualizer = OnedVisualizer(grid) time_stepper = ExplicitEulerTimeStepper(nt) fom = InstationaryModel(T=1e-0, operator=operator, rhs=rhs, initial_data=initial_data, time_stepper=time_stepper, num_values=20, visualizer=visualizer, name='C++-Model') return fom
def build_rom(self, projected_operators, estimator): fom = self.fom return InstationaryModel(T=fom.T, time_stepper=fom.time_stepper, num_values=fom.num_values, parameter_space=self.fom.parameter_space, estimator=estimator, **projected_operators)
def discretize_instationary_cg(analytical_problem, diameter=None, domain_discretizer=None, grid_type=None, grid=None, boundary_info=None, num_values=None, time_stepper=None, nt=None, preassemble=True): """Discretizes an |InstationaryProblem| with a |StationaryProblem| as stationary part using finite elements. Parameters ---------- analytical_problem The |InstationaryProblem| to discretize. diameter If not `None`, `diameter` is passed as an argument to the `domain_discretizer`. domain_discretizer Discretizer to be used for discretizing the analytical domain. This has to be a function `domain_discretizer(domain_description, diameter, ...)`. If `None`, |discretize_domain_default| is used. grid_type If not `None`, this parameter is forwarded to `domain_discretizer` to specify the type of the generated |Grid|. grid Instead of using a domain discretizer, the |Grid| can also be passed directly using this parameter. boundary_info A |BoundaryInfo| specifying the boundary types of the grid boundary entities. Must be provided if `grid` is specified. num_values The number of returned vectors of the solution trajectory. If `None`, each intermediate vector that is calculated is returned. time_stepper The :class:`time-stepper <pymor.algorithms.timestepping.TimeStepperInterface>` to be used by :class:`~pymor.models.basic.InstationaryModel.solve`. nt If `time_stepper` is not specified, the number of time steps for implicit Euler time stepping. preassemble If `True`, preassemble all operators in the resulting |Model|. Returns ------- m The |Model| that has been generated. data Dictionary with the following entries: :grid: The generated |Grid|. :boundary_info: The generated |BoundaryInfo|. :unassembled_m: In case `preassemble` is `True`, the generated |Model| before preassembling operators. """ assert isinstance(analytical_problem, InstationaryProblem) assert isinstance(analytical_problem.stationary_part, StationaryProblem) assert grid is None or boundary_info is not None assert boundary_info is None or grid is not None assert grid is None or domain_discretizer is None assert (time_stepper is None) != (nt is None) p = analytical_problem m, data = discretize_stationary_cg(p.stationary_part, diameter=diameter, domain_discretizer=domain_discretizer, grid_type=grid_type, grid=grid, boundary_info=boundary_info) if p.initial_data.parametric: I = InterpolationOperator(data['grid'], p.initial_data) else: I = p.initial_data.evaluate(data['grid'].centers(data['grid'].dim)) I = m.solution_space.make_array(I) if time_stepper is None: if p.stationary_part.diffusion is None: time_stepper = ExplicitEulerTimeStepper(nt=nt) else: time_stepper = ImplicitEulerTimeStepper(nt=nt) mass = m.l2_0_product m = InstationaryModel(operator=m.operator, rhs=m.rhs, mass=mass, initial_data=I, T=p.T, products=m.products, output_functional=m.output_functional, time_stepper=time_stepper, parameter_space=p.parameter_space, visualizer=m.visualizer, num_values=num_values, name=f'{p.name}_CG') if preassemble: data['unassembled_m'] = m m = preassemble_(m) return m, data
def discretize_instationary_fv(analytical_problem, diameter=None, domain_discretizer=None, grid_type=None, num_flux='lax_friedrichs', lxf_lambda=1., eo_gausspoints=5, eo_intervals=1, grid=None, boundary_info=None, num_values=None, time_stepper=None, nt=None, preassemble=True): """Discretizes an |InstationaryProblem| with a |StationaryProblem| as stationary part using the finite volume method. Parameters ---------- analytical_problem The |InstationaryProblem| to discretize. diameter If not `None`, `diameter` is passed to the `domain_discretizer`. domain_discretizer Discretizer to be used for discretizing the analytical domain. This has to be a function `domain_discretizer(domain_description, diameter, ...)`. If further arguments should be passed to the discretizer, use :func:`functools.partial`. If `None`, |discretize_domain_default| is used. grid_type If not `None`, this parameter is forwarded to `domain_discretizer` to specify the type of the generated |Grid|. num_flux The numerical flux to use in the finite volume formulation. Allowed values are `'lax_friedrichs'`, `'engquist_osher'`, `'simplified_engquist_osher'` (see :mod:`pymor.discretizers.builtin.fv`). lxf_lambda The stabilization parameter for the Lax-Friedrichs numerical flux (ignored, if different flux is chosen). eo_gausspoints Number of Gauss points for the Engquist-Osher numerical flux (ignored, if different flux is chosen). eo_intervals Number of sub-intervals to use for integration when using Engquist-Osher numerical flux (ignored, if different flux is chosen). grid Instead of using a domain discretizer, the |Grid| can also be passed directly using this parameter. boundary_info A |BoundaryInfo| specifying the boundary types of the grid boundary entities. Must be provided if `grid` is specified. num_values The number of returned vectors of the solution trajectory. If `None`, each intermediate vector that is calculated is returned. time_stepper The :class:`time-stepper <pymor.algorithms.timestepping.TimeStepper>` to be used by :class:`~pymor.models.basic.InstationaryModel.solve`. nt If `time_stepper` is not specified, the number of time steps for implicit Euler time stepping. preassemble If `True`, preassemble all operators in the resulting |Model|. Returns ------- m The |Model| that has been generated. data Dictionary with the following entries: :grid: The generated |Grid|. :boundary_info: The generated |BoundaryInfo|. :unassembled_m: In case `preassemble` is `True`, the generated |Model| before preassembling operators. """ assert isinstance(analytical_problem, InstationaryProblem) assert isinstance(analytical_problem.stationary_part, StationaryProblem) assert grid is None or boundary_info is not None assert boundary_info is None or grid is not None assert grid is None or domain_discretizer is None assert (time_stepper is None) != (nt is None) p = analytical_problem m, data = discretize_stationary_fv(p.stationary_part, diameter=diameter, domain_discretizer=domain_discretizer, grid_type=grid_type, num_flux=num_flux, lxf_lambda=lxf_lambda, eo_gausspoints=eo_gausspoints, eo_intervals=eo_intervals, grid=grid, boundary_info=boundary_info) grid = data['grid'] if p.initial_data.parametric: def initial_projection(U, mu): I = p.initial_data.evaluate(grid.quadrature_points(0, order=2), mu).squeeze() I = np.sum(I * grid.reference_element.quadrature(order=2)[1], axis=1) * (1. / grid.reference_element.volume) I = m.solution_space.make_array(I) return I.lincomb(U).to_numpy() I = NumpyGenericOperator(initial_projection, dim_range=grid.size(0), linear=True, range_id=m.solution_space.id, parameter_type=p.initial_data.parameter_type) else: I = p.initial_data.evaluate(grid.quadrature_points(0, order=2)).squeeze() I = np.sum(I * grid.reference_element.quadrature(order=2)[1], axis=1) * (1. / grid.reference_element.volume) I = m.solution_space.make_array(I) if time_stepper is None: if p.stationary_part.diffusion is None: time_stepper = ExplicitEulerTimeStepper(nt=nt) else: time_stepper = ImplicitEulerTimeStepper(nt=nt) rhs = None if isinstance(m.rhs, ZeroOperator) else m.rhs m = InstationaryModel(operator=m.operator, rhs=rhs, mass=None, initial_data=I, T=p.T, products=m.products, time_stepper=time_stepper, parameter_space=p.parameter_space, visualizer=m.visualizer, num_values=num_values, name=f'{p.name}_FV') if preassemble: data['unassembled_m'] = m m = preassemble_(m) return m, data
def discretize_instationary_from_disk(parameter_file, T=None, steps=None, u0=None, time_stepper=None): """Load a linear affinely decomposed |InstationaryModel| from file. Similarly to :func:`discretize_stationary_from_disk`, the model is specified via an `ini.`-file of the following form :: [system-matrices] L_1.mat: l_1(μ_1,...,μ_n) L_2.mat: l_2(μ_1,...,μ_n) ... [rhs-vectors] F_1.mat: f_1(μ_1,...,μ_n) F_2.mat: f_2(μ_1,...,μ_n) ... [mass-matrix] D.mat [initial-solution] u0: u0.mat [parameter] μ_1: a_1,b_1 ... μ_n: a_n,b_n [products] Prod1: P_1.mat Prod2: P_2.mat ... [time] T: final time steps: number of time steps Parameters ---------- parameter_file Path to the '.ini' parameter file. T End-time of desired solution. If `None`, the value specified in the parameter file is used. steps Number of time steps to. If `None`, the value specified in the parameter file is used. u0 Initial solution. If `None` the initial solution is obtained from parameter file. time_stepper The desired :class:`time stepper <pymor.algorithms.timestepping.TimeStepper>` to use. If `None`, implicit Euler time stepping is used. Returns ------- m The |InstationaryModel| that has been generated. """ assert ".ini" == parameter_file[-4:], "Given file is not an .ini file" assert os.path.isfile(parameter_file) base_path = os.path.dirname(parameter_file) # Get input from parameter file config = configparser.ConfigParser() config.optionxform = str config.read(parameter_file) # Assert that all needed entries given assert 'system-matrices' in config.sections() assert 'mass-matrix' in config.sections() assert 'rhs-vectors' in config.sections() assert 'parameter' in config.sections() system_mat = config.items('system-matrices') mass_mat = config.items('mass-matrix') rhs_vec = config.items('rhs-vectors') parameter = config.items('parameter') # Dict of parameters types and ranges parameter_type = {} parameter_range = {} # get parameters for i in range(len(parameter)): parameter_name = parameter[i][0] parameter_list = tuple( float(j) for j in parameter[i][1].replace(" ", "").split(',')) parameter_range[parameter_name] = parameter_list # Assume scalar parameter dependence parameter_type[parameter_name] = 0 # Create parameter space parameter_space = CubicParameterSpace(parameter_type=parameter_type, ranges=parameter_range) # Assemble operators system_operators, system_functionals = [], [] # get parameter functionals and system matrices for i in range(len(system_mat)): path = os.path.join(base_path, system_mat[i][0]) expr = system_mat[i][1] parameter_functional = ExpressionParameterFunctional( expr, parameter_type=parameter_type) system_operators.append( NumpyMatrixOperator.from_file(path, source_id='STATE', range_id='STATE')) system_functionals.append(parameter_functional) system_lincombOperator = LincombOperator(system_operators, coefficients=system_functionals) # get rhs vectors rhs_operators, rhs_functionals = [], [] for i in range(len(rhs_vec)): path = os.path.join(base_path, rhs_vec[i][0]) expr = rhs_vec[i][1] parameter_functional = ExpressionParameterFunctional( expr, parameter_type=parameter_type) op = NumpyMatrixOperator.from_file(path, range_id='STATE') assert isinstance(op.matrix, np.ndarray) op = op.with_(matrix=op.matrix.reshape((-1, 1))) rhs_operators.append(op) rhs_functionals.append(parameter_functional) rhs_lincombOperator = LincombOperator(rhs_operators, coefficients=rhs_functionals) # get mass matrix path = os.path.join(base_path, mass_mat[0][1]) mass_operator = NumpyMatrixOperator.from_file(path, source_id='STATE', range_id='STATE') # Obtain initial solution if not given if u0 is None: u_0 = config.items('initial-solution') path = os.path.join(base_path, u_0[0][1]) op = NumpyMatrixOperator.from_file(path, range_id='STATE') assert isinstance(op.matrix, np.ndarray) u0 = op.with_(matrix=op.matrix.reshape((-1, 1))) # get products if given if 'products' in config.sections(): product = config.items('products') products = {} for i in range(len(product)): product_name = product[i][0] product_path = os.path.join(base_path, product[i][1]) products[product_name] = NumpyMatrixOperator.from_file( product_path, source_id='STATE', range_id='STATE') else: products = None # Further specifications if 'time' in config.sections(): if T is None: assert 'T' in config.options('time') T = float(config.get('time', 'T')) if steps is None: assert 'steps' in config.options('time') steps = int(config.get('time', 'steps')) # Use implicit euler time stepper if no time-stepper given if time_stepper is None: time_stepper = ImplicitEulerTimeStepper(steps) else: time_stepper = time_stepper(steps) # Create and return instationary model return InstationaryModel(operator=system_lincombOperator, rhs=rhs_lincombOperator, parameter_space=parameter_space, initial_data=u0, T=T, time_stepper=time_stepper, mass=mass_operator, products=products)