def test_to_matrix_LincombOperator(): np.random.seed(0) A = np.random.randn(3, 3) B = np.random.randn(3, 2) a = np.random.randn() b = np.random.randn() C = a * A + b * B.dot(B.T) Aop = NumpyMatrixOperator(A) Bop = NumpyMatrixOperator(B) Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b]) assert_type_and_allclose(C, Cop, 'dense') Aop = NumpyMatrixOperator(sps.csc_matrix(A)) Bop = NumpyMatrixOperator(B) Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b]) assert_type_and_allclose(C, Cop, 'dense') Aop = NumpyMatrixOperator(A) Bop = NumpyMatrixOperator(sps.csc_matrix(B)) Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b]) assert_type_and_allclose(C, Cop, 'dense') Aop = NumpyMatrixOperator(sps.csc_matrix(A)) Bop = NumpyMatrixOperator(sps.csc_matrix(B)) Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b]) assert_type_and_allclose(C, Cop, 'sparse')
def __init__(self, op): operators = [self._wrapper[op.component(i)] for i in xrange(op.num_components())] coefficients = [self._wrapper[op.coefficient(i)] for i in xrange(op.num_components())] if op.has_affine_part(): operators.append(self._wrapper[op.affine_part()]) coefficients.append(1.) LincombOperator.__init__(self, operators, coefficients)
def test_lincomb_adjoint(): op = LincombOperator([NumpyMatrixOperator(np.eye(10)), NumpyMatrixOperator(np.eye(10))], [1+3j, ExpressionParameterFunctional('c[0] + 3', {'c': 1})]) mu = op.parameters.parse(1j) U = op.range.random() V = op.apply_adjoint(U, mu=mu) VV = op.H.apply(U, mu=mu) assert np.all(almost_equal(V, VV)) VVV = op.apply(U, mu=mu).conj() assert np.all(almost_equal(V, VVV))
def lincomb(operators, coefficients, name=None): import warnings warnings.warn( 'OperatorInterface.lincomb is deprecated!' + 'Use pymor.operators.constructions.LincombOperator instead.') from pymor.operators.constructions import LincombOperator op = LincombOperator(operators, coefficients, name=None) if op.parametric: return op else: return op.assemble()
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 test_to_matrix(): np.random.seed(0) A = np.random.randn(2, 2) B = np.random.randn(3, 3) C = np.random.randn(3, 3) X = np.bmat([[np.eye(2) + A, np.zeros((2, 3))], [np.zeros((3, 2)), B.dot(C.T)]]) C = sps.csc_matrix(C) Aop = NumpyMatrixOperator(A) Bop = NumpyMatrixOperator(B) Cop = NumpyMatrixOperator(C) Xop = BlockDiagonalOperator([ LincombOperator([IdentityOperator(NumpyVectorSpace(2)), Aop], [1, 1]), Concatenation(Bop, AdjointOperator(Cop)) ]) assert np.allclose(X, to_matrix(Xop)) assert np.allclose(X, to_matrix(Xop, format='csr').toarray()) np.random.seed(0) V = np.random.randn(10, 2) Vva = NumpyVectorArray(V.T) Vop = VectorArrayOperator(Vva) assert np.allclose(V, to_matrix(Vop)) Vop = VectorArrayOperator(Vva, transposed=True) assert np.allclose(V, to_matrix(Vop).T)
def _add_sub(self, other, sign): if not isinstance(other, Operator): return NotImplemented from pymor.operators.constructions import LincombOperator if self.name != 'LincombOperator' or not isinstance( self, LincombOperator): if other.name == 'LincombOperator' and isinstance( other, LincombOperator): operators = (self, ) + other.operators coefficients = (1., ) + (other.coefficients if sign == 1. else tuple(-c for c in other.coefficients)) else: operators, coefficients = (self, other), (1., sign) elif other.name == 'LincombOperator' and isinstance( other, LincombOperator): operators = self.operators + other.operators coefficients = self.coefficients + ( other.coefficients if sign == 1. else tuple( -c for c in other.coefficients)) else: operators, coefficients = self.operators + ( other, ), self.coefficients + (sign, ) return LincombOperator(operators, coefficients, solver_options=self.solver_options)
def mpi_wrap_operator(obj_id, mpi_range, mpi_source, with_apply2=False, pickle_local_spaces=True, space_type=MPIVectorSpace): """Wrap MPI distributed local |Operators| to a global |Operator| on rank 0. Given MPI distributed local |Operators| referred to by the :class:`~pymor.tools.mpi.ObjectId` `obj_id`, return a new |Operator| which manages these distributed operators from rank 0. This is done by instantiating :class:`MPIOperator`. Additionally, the structure of the wrapped operators is preserved. E.g. |LincombOperators| will be wrapped as a |LincombOperator| of :class:`MPIOperators <MPIOperator>`. Parameters ---------- See :class:`MPIOperator`. Returns ------- The wrapped |Operator|. """ op = mpi.get_object(obj_id) if isinstance(op, LincombOperator): obj_ids = mpi.call(_mpi_wrap_operator_LincombOperator_manage_operators, obj_id) return LincombOperator([mpi_wrap_operator(o, mpi_range, mpi_source, with_apply2, pickle_local_spaces, space_type) for o in obj_ids], op.coefficients, name=op.name) elif isinstance(op, VectorArrayOperator): array_obj_id, local_spaces = mpi.call(_mpi_wrap_operator_VectorArrayOperator_manage_array, obj_id, pickle_local_spaces) if all(ls == local_spaces[0] for ls in local_spaces): local_spaces = (local_spaces[0],) return VectorArrayOperator(space_type(local_spaces).make_array(array_obj_id), adjoint=op.adjoint, name=op.name) else: return MPIOperator(obj_id, mpi_range, mpi_source, with_apply2, pickle_local_spaces, space_type)
def __add__(self, other): if not isinstance(other, OperatorInterface): return NotImplemented from pymor.operators.constructions import LincombOperator if isinstance(other, LincombOperator): return NotImplemented else: return LincombOperator([self, other], [1., 1.])
def action_IdentityOperator(self, ops): coeff = sum(self.coefficients) if coeff == 0: return ZeroOperator(ops[0].source, ops[0].source, name=self.name) else: return LincombOperator( [IdentityOperator(ops[0].source, name=self.name)], [coeff], name=self.name)
def __mul__(self, other): assert isinstance(other, (Number, ParameterFunctional)) from pymor.operators.constructions import LincombOperator if self.name != 'LincombOperator' or not isinstance( self, LincombOperator): return LincombOperator((self, ), (other, )) else: return self.with_(coefficients=tuple(c * other for c in self.coefficients))
def _build_dual_models(self): assert self.primal_rom is not None assert self.RBPrimal is not None RBbasis = self.RBPrimal rhs_operators = list( self.fom.output_functional_dict['d_u_linear_part'].operators) rhs_coefficients = list( self.fom.output_functional_dict['d_u_linear_part'].coefficients) bilinear_part = self.fom.output_functional_dict['d_u_bilinear_part'] for i in range(len(RBbasis)): u = RBbasis[i] if isinstance(bilinear_part, LincombOperator): for j, op in enumerate(bilinear_part.operators): rhs_operators.append(VectorOperator(op.apply(u))) rhs_coefficients.append( ExpressionParameterFunctional( 'basis_coefficients[{}]'.format(i), {'basis_coefficients': len(RBbasis)}) * bilinear_part.coefficients[j]) else: rhs_operators.append( VectorOperator(bilinear_part.apply(u, None))) rhs_coefficients.append(1. * ExpressionParameterFunctional( 'basis_coefficients[{}]'.format(i), {'basis_coefficients': len(RBbasis)})) dual_rhs_operator = LincombOperator(rhs_operators, rhs_coefficients) dual_intermediate_fom = self.fom.primal_model.with_( rhs=dual_rhs_operator) if self.reductor_type == 'simple_coercive': print('building simple coercive dual reductor...') dual_reductor = SimpleCoerciveRBReductor( dual_intermediate_fom, RB=self.RBDual, product=self.opt_product, coercivity_estimator=self.coercivity_estimator) elif self.reductor_type == 'non_assembled': print('building non assembled dual reductor...') dual_reductor = NonAssembledCoerciveRBReductor( dual_intermediate_fom, RB=self.RBDual, product=self.opt_product, coercivity_estimator=self.coercivity_estimator) else: print('building coercive dual reductor...') dual_reductor = CoerciveRBReductor( dual_intermediate_fom, RB=self.RBDual, product=self.opt_product, coercivity_estimator=self.coercivity_estimator) dual_rom = dual_reductor.reduce() return dual_intermediate_fom, dual_rom, dual_reductor
def __add__(self, other): """Sum of two operators.""" if other == 0: return self if not isinstance(other, Operator): return NotImplemented from pymor.operators.constructions import LincombOperator if isinstance(other, LincombOperator): return NotImplemented else: return LincombOperator([self, other], [1., 1.])
def test_d_mu_of_LincombOperator(): dict_of_d_mus = {'mu': ['100', '2'], 'nu': 'cos(nu)'} pf = ProjectionParameterFunctional('mu', (2, ), (0, )) epf = ExpressionParameterFunctional('100 * mu[0] + 2 * mu[1] + sin(nu)', { 'mu': (2, ), 'nu': () }, 'functional_with_derivative', dict_of_d_mus) mu = {'mu': (10, 2), 'nu': 0} space = NumpyVectorSpace(1) zero_op = ZeroOperator(space, space) operators = [zero_op, zero_op, zero_op] coefficients = [1., pf, epf] operator = LincombOperator(operators, coefficients) op_sensitivity_to_first_mu = operator.d_mu('mu', 0) op_sensitivity_to_second_mu = operator.d_mu('mu', 1) op_sensitivity_to_nu = operator.d_mu('nu', ()) eval_mu_1 = op_sensitivity_to_first_mu.evaluate_coefficients(mu) eval_mu_2 = op_sensitivity_to_second_mu.evaluate_coefficients(mu) eval_nu = op_sensitivity_to_nu.evaluate_coefficients(mu) assert operator.evaluate_coefficients(mu) == [1., 10, 1004.] assert eval_mu_1 == [0., 1., 100.] assert eval_mu_2 == [0., 0., 2.] assert eval_nu == [0., 0., 1.]
def create_product(products, product_type): if product_type is None: return None, 'None' if not isinstance(product_type, tuple): return products[product_type], product_type else: prods = [products[tt] for tt in product_type] product_name = product_type[0] for ii in np.arange(1, len(product_type)): product_name += '_plus_' + product_type[ii] return LincombOperator(operators=prods, coefficients=[1 for pp in prods ]), product_name
def _reduce(self): d = self.d self.logger.info('Computing oswald interpolations ...') oi = d.estimator.oswald_interpolation_error oi_red = [] for i, OI_i_space in enumerate(oi.range.subspaces): oi_i = oi._blocks[i, i] basis = self.bases[oi_i.source.id] self.bases[OI_i_space.id] = oi_i.apply(basis) oi_red.append( NumpyMatrixOperator(np.eye(len(basis)), source_id=oi_i.source.id, range_id=oi_i.range.id)) oi_red = unblock(BlockDiagonalOperator(oi_red)) self.logger.info('Computing flux reconstructions ...') fr = d.estimator.flux_reconstruction for i, RT_i_space in enumerate(fr.range.subspaces): self.bases[RT_i_space.id] = RT_i_space.empty() red_aff_components = [] for i_aff, aff_component in enumerate(fr.operators): red_aff_component = [] for i, RT_i_space in enumerate(aff_component.range.subspaces): fr_i = aff_component._blocks[i, i] basis = self.bases[fr_i.source.id] self.bases[RT_i_space.id].append(fr_i.apply(basis)) M = np.zeros((len(basis) * len(fr.operators), len(basis))) M[i_aff * len(basis):(i_aff + 1) * len(basis), :] = np.eye( len(basis)) red_aff_component.append( NumpyMatrixOperator(M, source_id=fr_i.source.id, range_id=fr_i.range.id)) red_aff_components.append(BlockDiagonalOperator(red_aff_component)) fr_red = LincombOperator(red_aff_components, fr.coefficients) fr_red = unblock(fr_red) red_estimator = d.estimator.with_(flux_reconstruction=fr_red, oswald_interpolation_error=oi_red) rd = super()._reduce() rd = rd.with_(estimator=red_estimator) return rd
def mpi_wrap_operator(obj_id, functional=False, vector=False, with_apply2=False, pickle_subtypes=True, array_type=MPIVectorArray): """Wrap MPI distributed local |Operators| to a global |Operator| on rank 0. Given MPI distributed local |Operators| referred to by the `~pymor.tools.mpi.ObjectId` `obj_id`, return a new |Operator| which manages these distributed operators from rank 0. This is done by instantiating :class:`MPIOperator`. Additionally, the structure of the wrapped operators is preserved. E.g. |LincombOperators| will be wrapped as a |LincombOperator| of :class:`MPIOperators`. Parameters ---------- See :class:`MPIOperator`. Returns ------- The wrapped |Operator|. """ op = mpi.get_object(obj_id) if isinstance(op, LincombOperator): obj_ids = mpi.call(_mpi_wrap_operator_LincombOperator_manage_operators, obj_id) return LincombOperator([ mpi_wrap_operator(o, functional, vector, with_apply2, pickle_subtypes, array_type) for o in obj_ids ], op.coefficients, name=op.name) elif isinstance(op, VectorArrayOperator): array_obj_id, subtypes = mpi.call( _mpi_wrap_operator_VectorArrayOperator_manage_array, obj_id, pickle_subtypes) if all(subtype == subtypes[0] for subtype in subtypes): subtypes = (subtypes[0], ) return VectorArrayOperator(array_type(type(op._array), subtypes, array_obj_id), transposed=op.transposed, name=op.name) else: return MPIOperator(obj_id, functional, vector, with_apply2, pickle_subtypes, array_type)
def _localizedly_project_operator(self, op): if isinstance(op, LincombOperator): ops = [ self._localizedly_project_operator(foo) for foo in op.operators ] return LincombOperator(ops, coefficients=op.coefficients) assert op.linear and not op.parametric mats = [[ None if (op.uid, source_space, range_space) not in self.reduced_localized_operators else self.reduced_localized_operators[(op.uid, source_space, range_space)].matrix for source_space in self.source_spaces ] for range_space in self.range_spaces] result = NumpyMatrixOperator(scipy.sparse.bmat(mats).tocsc()) return result
def unblock_op(op, sparse=False): assert op._blocks[0][0] is not None if isinstance(op._blocks[0][0], LincombOperator): coefficients = op._blocks[0][0].coefficients operators = [ None for kk in np.arange(len(op._blocks[0][0].operators)) ] for kk in np.arange(len(op._blocks[0][0].operators)): ops = [[ op._blocks[ii][jj].operators[kk] if op._blocks[ii][jj] is not None else None for jj in np.arange(op.num_source_blocks) ] for ii in np.arange(op.num_range_blocks)] operators[kk] = unblock_op(BlockOperator(ops)) return LincombOperator(operators=operators, coefficients=coefficients) else: assert all( all([ isinstance(block, NumpyMatrixOperator ) if block is not None else True for block in row ]) for row in op._blocks) if op.source.dim == 0 and op.range.dim == 0: return NumpyMatrixOperator(np.zeros((0, 0))) elif op.source.dim == 1: mat = np.concatenate([ op._blocks[ii][0]._matrix for ii in np.arange(op.num_range_blocks) ], axis=1) elif op.range.dim == 1: mat = np.concatenate([ op._blocks[0][jj]._matrix for jj in np.arange(op.num_source_blocks) ], axis=1) else: mat = bmat([[ coo_matrix(op._blocks[ii][jj]._matrix) if op._blocks[ii][jj] is not None else coo_matrix( (op._range_dims[ii], op._source_dims[jj])) for jj in np.arange(op.num_source_blocks) ] for ii in np.arange(op.num_range_blocks)]) mat = mat.toarray() return NumpyMatrixOperator(mat)
def test_output_d_mu(): from pymordemos.linear_optimization import create_fom grid_intervals = 10 training_samples = 3 fom, mu_bar = create_fom(grid_intervals, vector_valued_output=True) easy_fom, _ = create_fom(grid_intervals, vector_valued_output=False) parameter_space = fom.parameters.space(0, np.pi) training_set = parameter_space.sample_uniformly(training_samples) #verifying that the adjoint and sensitivity gradients are the same and that solve_d_mu works for mu in training_set: gradient_with_adjoint_approach = fom.output_d_mu(mu, return_array=True, use_adjoint=True) gradient_with_sensitivities = fom.output_d_mu(mu, return_array=True, use_adjoint=False) assert np.allclose(gradient_with_adjoint_approach, gradient_with_sensitivities) u_d_mu = fom.solve_d_mu('diffusion', 1, mu=mu).to_numpy() u_d_mu_ = fom.compute(solution_d_mu=True, mu=mu)['solution_d_mu']['diffusion'][1].to_numpy() assert np.allclose(u_d_mu, u_d_mu_) # test the complex case complex_fom = easy_fom.with_(operator=easy_fom.operator.with_( operators=[op* (1+2j) for op in easy_fom.operator.operators])) complex_gradient_adjoint = complex_fom.output_d_mu(mu, return_array=True, use_adjoint=True) complex_gradient = complex_fom.output_d_mu(mu, return_array=True, use_adjoint=False) assert np.allclose(complex_gradient_adjoint, complex_gradient) complex_fom = easy_fom.with_(output_functional=easy_fom.output_functional.with_( operators=[op* (1+2j) for op in easy_fom.output_functional.operators])) complex_gradient_adjoint = complex_fom.output_d_mu(mu, return_array=True, use_adjoint=True) complex_gradient = complex_fom.output_d_mu(mu, return_array=True, use_adjoint=False) assert np.allclose(complex_gradient_adjoint, complex_gradient) # another fom to test the 3d case ops, coefs = fom.operator.operators, fom.operator.coefficients ops += (fom.operator.operators[1],) coefs += (ProjectionParameterFunctional('nu', 1, 0),) fom_ = fom.with_(operator=LincombOperator(ops, coefs)) parameter_space = fom_.parameters.space(0, np.pi) training_set = parameter_space.sample_uniformly(training_samples) for mu in training_set: gradient_with_adjoint_approach = fom_.output_d_mu(mu, return_array=True, use_adjoint=True) gradient_with_sensitivities = fom_.output_d_mu(mu, return_array=True, use_adjoint=False) assert np.allclose(gradient_with_adjoint_approach, gradient_with_sensitivities)
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].data rhs_data[:] = np.ones(len(rhs_data)) rhs_data[0] = 0 rhs_data[len(rhs_data) - 1] = 0 rhs = VectorFunctional(rhs_vec) # hack together a visualizer ... grid = OnedGrid(domain=(0, 1), num_intervals=n) visualizer = Matplotlib1DVisualizer(grid) time_stepper = ExplicitEulerTimeStepper(nt) parameter_space = CubicParameterSpace(operator.parameter_type, 0.1, 1) 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
def _localizedly_project_functional(self, op): if isinstance(op, LincombOperator): ops = [ self._localizedly_project_functional(foo) for foo in op.operators ] return LincombOperator(ops, coefficients=op.coefficients) assert op.linear and not op.parametric v = op.as_vector() def project_block(ids, basis): o = self.l.localize_vector_array(v, ids) if basis is None: return o.data else: return basis.dot(o).T mats = [ project_block(ids, basis) for ids, basis in zip(self.range_spaces, self.range_bases) ] return NumpyVectorSpace.make_array(np.concatenate(mats, axis=1))
def discretize(grid_and_problem_data, solver_options, mpi_comm): ################ Setup logger = getLogger('discretize_elliptic_block_swipdg.discretize') logger.info('discretizing ... ') grid, boundary_info = grid_and_problem_data['grid'], grid_and_problem_data[ 'boundary_info'] local_all_dirichlet_boundary_info = make_subdomain_boundary_info( grid, {'type': 'xt.grid.boundaryinfo.alldirichlet'}) local_subdomains, num_local_subdomains, num_global_subdomains = _get_subdomains( grid) local_all_neumann_boundary_info = make_subdomain_boundary_info( grid, {'type': 'xt.grid.boundaryinfo.allneumann'}) block_space = make_block_dg_space(grid) global_rt_space = make_rt_space(grid) subdomain_rt_spaces = [ global_rt_space.restrict_to_dd_subdomain_view(grid, ii) for ii in range(num_global_subdomains) ] local_patterns = [ block_space.local_space(ii).compute_pattern('face_and_volume') for ii in range(block_space.num_blocks) ] coupling_patterns = { 'in_in': {}, 'out_out': {}, 'in_out': {}, 'out_in': {} } coupling_matrices = { 'in_in': {}, 'out_out': {}, 'in_out': {}, 'out_in': {} } for ii in range(num_global_subdomains): ii_size = block_space.local_space(ii).size() for jj in grid.neighboring_subdomains(ii): jj_size = block_space.local_space(jj).size() if ii < jj: # Assemble primally (visit each coupling only once). coupling_patterns['in_in'][(ii, jj)] = block_space.local_space( ii).compute_pattern('face_and_volume') coupling_patterns['out_out'][( ii, jj)] = block_space.local_space(jj).compute_pattern( 'face_and_volume') coupling_patterns['in_out'][( ii, jj)] = block_space.compute_coupling_pattern( ii, jj, 'face') coupling_patterns['out_in'][( ii, jj)] = block_space.compute_coupling_pattern( jj, ii, 'face') coupling_matrices['in_in'][(ii, jj)] = Matrix( ii_size, ii_size, coupling_patterns['in_in'][(ii, jj)]) coupling_matrices['out_out'][(ii, jj)] = Matrix( jj_size, jj_size, coupling_patterns['out_out'][(ii, jj)]) coupling_matrices['in_out'][(ii, jj)] = Matrix( ii_size, jj_size, coupling_patterns['in_out'][(ii, jj)]) coupling_matrices['out_in'][(ii, jj)] = Matrix( jj_size, ii_size, coupling_patterns['out_in'][(ii, jj)]) boundary_patterns = {} for ii in grid.boundary_subdomains(): boundary_patterns[ii] = block_space.local_space(ii).compute_pattern( 'face_and_volume') ################ Assemble LHS and RHS lambda_, kappa = grid_and_problem_data['lambda'], grid_and_problem_data[ 'kappa'] if isinstance(lambda_, dict): lambda_funcs = lambda_['functions'] lambda_coeffs = lambda_['coefficients'] else: lambda_funcs = [ lambda_, ] lambda_coeffs = [ 1, ] logger.debug('block op ... ') ops, block_ops = zip(*(discretize_lhs( lf, grid, block_space, local_patterns, boundary_patterns, coupling_matrices, kappa, local_all_neumann_boundary_info, boundary_info, coupling_patterns, solver_options) for lf in lambda_funcs)) global_operator = LincombOperator(ops, lambda_coeffs, solver_options=solver_options, name='GlobalOperator') logger.debug('block op global done ') block_op = LincombOperator(block_ops, lambda_coeffs, name='lhs', solver_options=solver_options) logger.debug('block op done ') f = grid_and_problem_data['f'] if isinstance(f, dict): f_funcs = f['functions'] f_coeffs = f['coefficients'] else: f_funcs = [ f, ] f_coeffs = [ 1, ] rhss, block_rhss = zip(*(discretize_rhs( ff, grid, block_space, global_operator, block_ops, block_op) for ff in f_funcs)) global_rhs = LincombOperator(rhss, f_coeffs) block_rhs = LincombOperator(block_rhss, f_coeffs) solution_space = block_op.source ################ Assemble interpolation and reconstruction operators logger.info('discretizing interpolation ') # Oswald interpolation error operator oi_op = BlockDiagonalOperator([ OswaldInterpolationErrorOperator(ii, block_op.source, grid, block_space) for ii in range(num_global_subdomains) ], name='oswald_interpolation_error') # Flux reconstruction operator fr_op = LincombOperator([ BlockDiagonalOperator([ FluxReconstructionOperator(ii, block_op.source, grid, block_space, global_rt_space, subdomain_rt_spaces, lambda_xi, kappa) for ii in range(num_global_subdomains) ]) for lambda_xi in lambda_funcs ], lambda_coeffs, name='flux_reconstruction') ################ Assemble inner products and error estimator operators logger.info('discretizing inner products ') lambda_bar, lambda_hat = grid_and_problem_data[ 'lambda_bar'], grid_and_problem_data['lambda_hat'] mu_bar, mu_hat = grid_and_problem_data['mu_bar'], grid_and_problem_data[ 'mu_hat'] operators = {} local_projections = [] local_rt_projections = [] local_oi_projections = [] local_div_ops = [] local_l2_products = [] data = dict(grid=grid, block_space=block_space, local_projections=local_projections, local_rt_projections=local_rt_projections, local_oi_projections=local_oi_projections, local_div_ops=local_div_ops, local_l2_products=local_l2_products) for ii in range(num_global_subdomains): neighborhood = grid.neighborhood_of(ii) ################ Assemble local inner products local_dg_space = block_space.local_space(ii) # we want a larger pattern to allow for axpy with other matrices tmp_local_matrix = Matrix( local_dg_space.size(), local_dg_space.size(), local_dg_space.compute_pattern('face_and_volume')) local_energy_product_ops = [] local_energy_product_coeffs = [] for func, coeff in zip(lambda_funcs, lambda_coeffs): local_energy_product_ops.append( make_elliptic_matrix_operator(func, kappa, tmp_local_matrix.copy(), local_dg_space, over_integrate=0)) local_energy_product_coeffs.append(coeff) local_energy_product_ops.append( make_penalty_product_matrix_operator( grid, ii, local_all_dirichlet_boundary_info, local_dg_space, func, kappa, over_integrate=0)) local_energy_product_coeffs.append(coeff) local_l2_product = make_l2_matrix_operator(tmp_local_matrix.copy(), local_dg_space) del tmp_local_matrix local_assembler = make_system_assembler(local_dg_space) for local_product_op in local_energy_product_ops: local_assembler.append(local_product_op) local_assembler.append(local_l2_product) local_assembler.assemble() local_energy_product_name = 'local_energy_dg_product_{}'.format(ii) local_energy_product = LincombOperator([ DuneXTMatrixOperator(op.matrix(), source_id='domain_{}'.format(ii), range_id='domain_{}'.format(ii)) for op in local_energy_product_ops ], local_energy_product_coeffs, name=local_energy_product_name) operators[local_energy_product_name] = \ local_energy_product.assemble(mu_bar).with_(name=local_energy_product_name) local_l2_product = DuneXTMatrixOperator( local_l2_product.matrix(), source_id='domain_{}'.format(ii), range_id='domain_{}'.format(ii)) local_l2_products.append(local_l2_product) # assemble local elliptic product matrix = make_local_elliptic_matrix_operator(grid, ii, local_dg_space, lambda_bar, kappa) matrix.assemble() local_elliptic_product = DuneXTMatrixOperator( matrix.matrix(), range_id='domain_{}'.format(ii), source_id='domain_{}'.format(ii)) ################ Assemble local to global projections # assemble projection (solution space) -> (ii space) local_projection = BlockProjectionOperator(block_op.source, ii) local_projections.append(local_projection) # assemble projection (RT spaces on neighborhoods of subdomains) -> (local RT space on ii) ops = np.full(num_global_subdomains, None) for kk in neighborhood: component = grid.neighborhood_of(kk).index(ii) assert fr_op.range.subspaces[kk].subspaces[ component].id == 'LOCALRT_{}'.format(ii) ops[kk] = BlockProjectionOperator(fr_op.range.subspaces[kk], component) local_rt_projection = BlockRowOperator( ops, source_spaces=fr_op.range.subspaces, name='local_rt_projection_{}'.format(ii)) local_rt_projections.append(local_rt_projection) # assemble projection (OI spaces on neighborhoods of subdomains) -> (ii space) ops = np.full(num_global_subdomains, None) for kk in neighborhood: component = grid.neighborhood_of(kk).index(ii) assert oi_op.range.subspaces[kk].subspaces[ component].id == 'domain_{}'.format(ii) ops[kk] = BlockProjectionOperator(oi_op.range.subspaces[kk], component) local_oi_projection = BlockRowOperator( ops, source_spaces=oi_op.range.subspaces, name='local_oi_projection_{}'.format(ii)) local_oi_projections.append(local_oi_projection) ################ Assemble additional operators for error estimation # assemble local divergence operator local_rt_space = global_rt_space.restrict_to_dd_subdomain_view( grid, ii) local_div_op = make_divergence_matrix_operator_on_subdomain( grid, ii, local_dg_space, local_rt_space) local_div_op.assemble() local_div_op = DuneXTMatrixOperator( local_div_op.matrix(), source_id='LOCALRT_{}'.format(ii), range_id='domain_{}'.format(ii), name='local_divergence_{}'.format(ii)) local_div_ops.append(local_div_op) ################ Assemble error estimator operators -- Nonconformity operators['nc_{}'.format(ii)] = \ Concatenation([local_oi_projection.T, local_elliptic_product, local_oi_projection], name='nonconformity_{}'.format(ii)) ################ Assemble error estimator operators -- Residual if len(f_funcs) == 1: assert f_coeffs[0] == 1 local_div = Concatenation([local_div_op, local_rt_projection]) local_rhs = VectorFunctional( block_rhs.operators[0]._array._blocks[ii]) operators['r_fd_{}'.format(ii)] = \ Concatenation([local_rhs, local_div], name='r1_{}'.format(ii)) operators['r_dd_{}'.format(ii)] = \ Concatenation([local_div.T, local_l2_product, local_div], name='r2_{}'.format(ii)) ################ Assemble error estimator operators -- Diffusive flux operators['df_aa_{}'.format(ii)] = LincombOperator( [ assemble_estimator_diffusive_flux_aa( lambda_xi, lambda_xi_prime, grid, ii, block_space, lambda_hat, kappa, solution_space) for lambda_xi in lambda_funcs for lambda_xi_prime in lambda_funcs ], [ ProductParameterFunctional([c1, c2]) for c1 in lambda_coeffs for c2 in lambda_coeffs ], name='diffusive_flux_aa_{}'.format(ii)) operators['df_bb_{}'.format( ii)] = assemble_estimator_diffusive_flux_bb( grid, ii, subdomain_rt_spaces, lambda_hat, kappa, local_rt_projection) operators['df_ab_{}'.format(ii)] = LincombOperator( [ assemble_estimator_diffusive_flux_ab( lambda_xi, grid, ii, block_space, subdomain_rt_spaces, lambda_hat, kappa, local_rt_projection, local_projection) for lambda_xi in lambda_funcs ], lambda_coeffs, name='diffusive_flux_ab_{}'.format(ii)) ################ Final assembly logger.info('final assembly ') # instantiate error estimator min_diffusion_evs = np.array([ min_diffusion_eigenvalue(grid, ii, lambda_hat, kappa) for ii in range(num_global_subdomains) ]) subdomain_diameters = np.array( [subdomain_diameter(grid, ii) for ii in range(num_global_subdomains)]) if len(f_funcs) == 1: assert f_coeffs[0] == 1 local_eta_rf_squared = np.array([ apply_l2_product(grid, ii, f_funcs[0], f_funcs[0], over_integrate=2) for ii in range(num_global_subdomains) ]) else: local_eta_rf_squared = None estimator = EllipticEstimator(grid, min_diffusion_evs, subdomain_diameters, local_eta_rf_squared, lambda_coeffs, mu_bar, mu_hat, fr_op, oswald_interpolation_error=oi_op, mpi_comm=mpi_comm) l2_product = BlockDiagonalOperator(local_l2_products) # instantiate discretization neighborhoods = [ grid.neighborhood_of(ii) for ii in range(num_global_subdomains) ] local_boundary_info = make_subdomain_boundary_info( grid_and_problem_data['grid'], {'type': 'xt.grid.boundaryinfo.alldirichlet'}) d = DuneDiscretization(global_operator=global_operator, global_rhs=global_rhs, neighborhoods=neighborhoods, enrichment_data=(grid, local_boundary_info, lambda_, kappa, f, block_space), operator=block_op, rhs=block_rhs, visualizer=DuneGDTVisualizer(block_space), operators=operators, products={'l2': l2_product}, estimator=estimator, data=data) parameter_range = grid_and_problem_data['parameter_range'] logger.info('final assembly B') d = d.with_(parameter_space=CubicParameterSpace( d.parameter_type, parameter_range[0], parameter_range[1])) logger.info('final assembly C') return d, data
def discretize_elliptic_cg(analytical_problem, diameter=None, domain_discretizer=None, grid=None, boundary_info=None): """Discretizes an |EllipticProblem| using finite elements. Parameters ---------- analytical_problem The |EllipticProblem| 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 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. Returns ------- discretization The |Discretization| that has been generated. data Dictionary with the following entries: :grid: The generated |Grid|. :boundary_info: The generated |BoundaryInfo|. """ assert isinstance(analytical_problem, EllipticProblem) 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 if grid is None: domain_discretizer = domain_discretizer or discretize_domain_default if diameter is None: grid, boundary_info = domain_discretizer(analytical_problem.domain) else: grid, boundary_info = domain_discretizer(analytical_problem.domain, diameter=diameter) assert isinstance(grid, (OnedGrid, TriaGrid, RectGrid)) if isinstance(grid, RectGrid): Operator = cg.DiffusionOperatorQ1 Functional = cg.L2ProductFunctionalQ1 else: Operator = cg.DiffusionOperatorP1 Functional = cg.L2ProductFunctionalP1 p = analytical_problem if p.diffusion_functionals is not None: L0 = Operator(grid, boundary_info, diffusion_constant=0, name='diffusion_boundary_part') Li = [ Operator(grid, boundary_info, diffusion_function=df, dirichlet_clear_diag=True, name='diffusion_{}'.format(i)) for i, df in enumerate(p.diffusion_functions) ] L = LincombOperator(operators=[L0] + Li, coefficients=[1.] + list(p.diffusion_functionals), name='diffusion') else: assert len(p.diffusion_functions) == 1 L = Operator(grid, boundary_info, diffusion_function=p.diffusion_functions[0], name='diffusion') F = Functional(grid, p.rhs, boundary_info, dirichlet_data=p.dirichlet_data, neumann_data=p.neumann_data) if isinstance(grid, (TriaGrid, RectGrid)): visualizer = PatchVisualizer(grid=grid, bounding_box=grid.domain, codim=2) else: visualizer = Matplotlib1DVisualizer(grid=grid, codim=1) empty_bi = EmptyBoundaryInfo(grid) l2_product = cg.L2ProductQ1(grid, empty_bi) if isinstance( grid, RectGrid) else cg.L2ProductP1(grid, empty_bi) h1_semi_product = Operator(grid, empty_bi) products = { 'h1': l2_product + h1_semi_product, 'h1_semi': h1_semi_product, 'l2': l2_product } parameter_space = p.parameter_space if hasattr(p, 'parameter_space') else None discretization = StationaryDiscretization(L, F, products=products, visualizer=visualizer, parameter_space=parameter_space, name='{}_CG'.format(p.name)) return discretization, {'grid': grid, 'boundary_info': boundary_info}
def discretize(grid_and_problem_data, polorder=1, solver_options=None): logger = getLogger('discretize_elliptic_swipdg.discretize') logger.info('discretizing ... ') over_integrate = 2 grid, boundary_info = grid_and_problem_data['grid'], grid_and_problem_data[ 'boundary_info'] _lambda, kappa, f = (grid_and_problem_data['lambda'], grid_and_problem_data['kappa'], grid_and_problem_data['f']) lambda_bar, lambda_bar = grid_and_problem_data[ 'lambda_bar'], grid_and_problem_data['lambda_bar'] mu_bar, mu_hat, parameter_range = ( grid_and_problem_data['mu_bar'], grid_and_problem_data['mu_hat'], grid_and_problem_data['parameter_range']) space = make_dg_space(grid) # prepare operators and functionals if isinstance(_lambda, dict): system_ops = [ make_elliptic_swipdg_matrix_operator(lambda_func, kappa, boundary_info, space, over_integrate) for lambda_func in _lambda['functions'] ] elliptic_ops = [ make_elliptic_matrix_operator(lambda_func, kappa, space, over_integrate) for lambda_func in _lambda['functions'] ] else: system_ops = [ make_elliptic_swipdg_matrix_operator(_lambda, kappa, boundary_info, space, over_integrate), ] elliptic_ops = [ make_elliptic_matrix_operator(_lambda, kappa, space, over_integrate), ] if isinstance(f, dict): rhs_functionals = [ make_l2_volume_vector_functional(f_func, space, over_integrate) for f_func in f['functions'] ] else: rhs_functionals = [ make_l2_volume_vector_functional(f, space, over_integrate), ] l2_matrix_with_system_pattern = system_ops[0].matrix().copy() l2_operator = make_l2_matrix_operator(l2_matrix_with_system_pattern, space) # assemble everything in one grid walk system_assembler = make_system_assembler(space) for op in system_ops: system_assembler.append(op) for op in elliptic_ops: system_assembler.append(op) for func in rhs_functionals: system_assembler.append(func) system_assembler.append(l2_operator) system_assembler.walk() # wrap everything if isinstance(_lambda, dict): op = LincombOperator([ DuneXTMatrixOperator(o.matrix(), dof_communicator=space.dof_communicator) for o in system_ops ], _lambda['coefficients']) elliptic_op = LincombOperator( [DuneXTMatrixOperator(o.matrix()) for o in elliptic_ops], _lambda['coefficients']) else: op = DuneXTMatrixOperator(system_ops[0].matrix()) elliptic_op = DuneXTMatrixOperator(elliptic_ops[0].matrix()) if isinstance(f, dict): rhs = LincombOperator([ VectorFunctional(op.range.make_array([func.vector()])) for func in rhs_functionals ], f['coefficients']) else: rhs = VectorFunctional( op.range.make_array([rhs_functionals[0].vector()])) operators = { 'l2': DuneXTMatrixOperator(l2_matrix_with_system_pattern), 'elliptic': elliptic_op, 'elliptic_mu_bar': DuneXTMatrixOperator(elliptic_op.assemble(mu=mu_bar).matrix) } d = StationaryDiscretization(op, rhs, operators=operators, visualizer=DuneGDTVisualizer(space)) d = d.with_(parameter_space=CubicParameterSpace( d.parameter_type, parameter_range[0], parameter_range[1])) return d, {'space': space}
def _K_apply_inverse_adjoint(self, s, V): Ks = LincombOperator( (self.fom.E, self.fom.A) + self.fom.Ad, (s, -1) + tuple(-np.exp(-taui * s) for taui in self.fom.tau)) return Ks.apply_inverse_adjoint(V, mu=self.mu)
def __mul__(self, other): if not isinstance(other, (Number, ParameterFunctionalInterface)): return NotImplemented from pymor.operators.constructions import LincombOperator return LincombOperator([self], [other])
def _K_apply_inverse_adjoint(self, s, V): Ks = LincombOperator((self.d.E, self.d.A) + self.d.Ad, (s, -1) + tuple(-np.exp(-taui * s) for taui in self.d.tau)) return Ks.apply_inverse_adjoint(V)
def discretize_stationary_cg(analytical_problem, diameter=None, domain_discretizer=None, grid_type=None, grid=None, boundary_info=None, preassemble=True): """Discretizes a |StationaryProblem| using finite elements. Parameters ---------- analytical_problem The |StationaryProblem| 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. 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, 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 grid_type is None or grid is None p = analytical_problem if not (p.nonlinear_advection == p.nonlinear_advection_derivative == p.nonlinear_reaction == p.nonlinear_reaction_derivative is None): raise NotImplementedError if grid is None: domain_discretizer = domain_discretizer or discretize_domain_default if grid_type: domain_discretizer = partial(domain_discretizer, grid_type=grid_type) if diameter is None: grid, boundary_info = domain_discretizer(p.domain) else: grid, boundary_info = domain_discretizer(p.domain, diameter=diameter) assert grid.reference_element in (line, triangle, square) if grid.reference_element is square: DiffusionOperator = DiffusionOperatorQ1 AdvectionOperator = AdvectionOperatorQ1 ReactionOperator = L2ProductQ1 L2Functional = L2ProductFunctionalQ1 BoundaryL2Functional = BoundaryL2ProductFunctional else: DiffusionOperator = DiffusionOperatorP1 AdvectionOperator = AdvectionOperatorP1 ReactionOperator = L2ProductP1 L2Functional = L2ProductFunctionalP1 BoundaryL2Functional = BoundaryL2ProductFunctional Li = [ DiffusionOperator(grid, boundary_info, diffusion_constant=0, name='boundary_part') ] coefficients = [1.] # diffusion part if isinstance(p.diffusion, LincombFunction): Li += [ DiffusionOperator(grid, boundary_info, diffusion_function=df, dirichlet_clear_diag=True, name=f'diffusion_{i}') for i, df in enumerate(p.diffusion.functions) ] coefficients += list(p.diffusion.coefficients) elif p.diffusion is not None: Li += [ DiffusionOperator(grid, boundary_info, diffusion_function=p.diffusion, dirichlet_clear_diag=True, name='diffusion') ] coefficients.append(1.) # advection part if isinstance(p.advection, LincombFunction): Li += [ AdvectionOperator(grid, boundary_info, advection_function=af, dirichlet_clear_diag=True, name=f'advection_{i}') for i, af in enumerate(p.advection.functions) ] coefficients += list(p.advection.coefficients) elif p.advection is not None: Li += [ AdvectionOperator(grid, boundary_info, advection_function=p.advection, dirichlet_clear_diag=True, name='advection') ] coefficients.append(1.) # reaction part if isinstance(p.reaction, LincombFunction): Li += [ ReactionOperator(grid, boundary_info, coefficient_function=rf, dirichlet_clear_diag=True, name=f'reaction_{i}') for i, rf in enumerate(p.reaction.functions) ] coefficients += list(p.reaction.coefficients) elif p.reaction is not None: Li += [ ReactionOperator(grid, boundary_info, coefficient_function=p.reaction, dirichlet_clear_diag=True, name='reaction') ] coefficients.append(1.) # robin boundaries if p.robin_data is not None: assert isinstance(p.robin_data, tuple) and len(p.robin_data) == 2 if isinstance(p.robin_data[0], LincombFunction): for i, rd in enumerate(p.robin_data[0].functions): robin_tuple = (rd, p.robin_data[1]) Li += [ RobinBoundaryOperator(grid, boundary_info, robin_data=robin_tuple, name=f'robin_{i}') ] coefficients += list(p.robin_data[0].coefficients) else: Li += [ RobinBoundaryOperator(grid, boundary_info, robin_data=p.robin_data, name=f'robin') ] coefficients.append(1.) L = LincombOperator(operators=Li, coefficients=coefficients, name='ellipticOperator') # right-hand side rhs = p.rhs or ConstantFunction(0., dim_domain=p.domain.dim) Fi = [] coefficients_F = [] if isinstance(p.rhs, LincombFunction): Fi += [ L2Functional(grid, rh, dirichlet_clear_dofs=True, boundary_info=boundary_info, name=f'rhs_{i}') for i, rh in enumerate(p.rhs.functions) ] coefficients_F += list(p.rhs.coefficients) else: Fi += [ L2Functional(grid, rhs, dirichlet_clear_dofs=True, boundary_info=boundary_info, name='rhs') ] coefficients_F.append(1.) if p.neumann_data is not None and boundary_info.has_neumann: if isinstance(p.neumann_data, LincombFunction): Fi += [ BoundaryL2Functional(grid, -ne, boundary_info=boundary_info, boundary_type='neumann', dirichlet_clear_dofs=True, name=f'neumann_{i}') for i, ne in enumerate(p.neumann_data.functions) ] coefficients_F += list(p.neumann_data.coefficients) else: Fi += [ BoundaryL2Functional(grid, -p.neumann_data, boundary_info=boundary_info, boundary_type='neumann', dirichlet_clear_dofs=True) ] coefficients_F.append(1.) if p.robin_data is not None and boundary_info.has_robin: if isinstance(p.robin_data[0], LincombFunction): Fi += [ BoundaryL2Functional(grid, rob * p.robin_data[1], boundary_info=boundary_info, boundary_type='robin', dirichlet_clear_dofs=True, name=f'robin_{i}') for i, rob in enumerate(p.robin_data[0].functions) ] coefficients_F += list(p.robin_data[0].coefficients) else: Fi += [ BoundaryL2Functional(grid, p.robin_data[0] * p.robin_data[1], boundary_info=boundary_info, boundary_type='robin', dirichlet_clear_dofs=True) ] coefficients_F.append(1.) if p.dirichlet_data is not None and boundary_info.has_dirichlet: if isinstance(p.dirichlet_data, LincombFunction): Fi += [ BoundaryDirichletFunctional(grid, di, boundary_info, name=f'dirichlet{i}') for i, di in enumerate(p.dirichlet_data.functions) ] coefficients_F += list(p.dirichlet_data.coefficients) else: Fi += [ BoundaryDirichletFunctional(grid, p.dirichlet_data, boundary_info) ] coefficients_F.append(1.) F = LincombOperator(operators=Fi, coefficients=coefficients_F, name='rhsOperator') if grid.reference_element in (triangle, square): visualizer = PatchVisualizer(grid=grid, bounding_box=grid.bounding_box(), codim=2) elif grid.reference_element is line: visualizer = OnedVisualizer(grid=grid, codim=1) else: visualizer = None Prod = L2ProductQ1 if grid.reference_element is square else L2ProductP1 empty_bi = EmptyBoundaryInfo(grid) l2_product = Prod(grid, empty_bi, name='l2') l2_0_product = Prod(grid, boundary_info, dirichlet_clear_columns=True, name='l2_0') h1_semi_product = DiffusionOperator(grid, empty_bi, name='h1_semi') h1_0_semi_product = DiffusionOperator(grid, boundary_info, dirichlet_clear_columns=True, name='h1_0_semi') products = { 'h1': l2_product + h1_semi_product, 'h1_semi': h1_semi_product, 'l2': l2_product, 'h1_0': l2_0_product + h1_0_semi_product, 'h1_0_semi': h1_0_semi_product, 'l2_0': l2_0_product } # assemble additionals output functionals if p.outputs: if any(v[0] not in ('l2', 'l2_boundary') for v in p.outputs): raise NotImplementedError outputs = [ L2Functional(grid, v[1], dirichlet_clear_dofs=False).H if v[0] == 'l2' else BoundaryL2Functional( grid, v[1], dirichlet_clear_dofs=False).H for v in p.outputs ] if len(outputs) > 1: from pymor.operators.block import BlockColumnOperator output_functional = BlockColumnOperator(outputs) else: output_functional = outputs[0] else: output_functional = None parameter_space = p.parameter_space if hasattr(p, 'parameter_space') else None m = StationaryModel(L, F, output_functional=output_functional, products=products, visualizer=visualizer, parameter_space=parameter_space, name=f'{p.name}_CG') data = {'grid': grid, 'boundary_info': boundary_info} if preassemble: data['unassembled_m'] = m m = preassemble_(m) return m, data
def discretize_stationary_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, preassemble=True): """Discretizes a |StationaryProblem| using the finite volume method. Parameters ---------- analytical_problem The |StationaryProblem| 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|. 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. 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, 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 grid_type is None or grid is None p = analytical_problem if analytical_problem.robin_data is not None: raise NotImplementedError if p.outputs: raise NotImplementedError if grid is None: domain_discretizer = domain_discretizer or discretize_domain_default if grid_type: domain_discretizer = partial(domain_discretizer, grid_type=grid_type) if diameter is None: grid, boundary_info = domain_discretizer(analytical_problem.domain) else: grid, boundary_info = domain_discretizer(analytical_problem.domain, diameter=diameter) L, L_coefficients = [], [] F, F_coefficients = [], [] if p.rhs is not None or p.neumann_data is not None: F += [L2ProductFunctional(grid, p.rhs, boundary_info=boundary_info, neumann_data=p.neumann_data)] F_coefficients += [1.] # diffusion part if isinstance(p.diffusion, LincombFunction): L += [DiffusionOperator(grid, boundary_info, diffusion_function=df, name=f'diffusion_{i}') for i, df in enumerate(p.diffusion.functions)] L_coefficients += p.diffusion.coefficients if p.dirichlet_data is not None: F += [L2ProductFunctional(grid, None, boundary_info=boundary_info, dirichlet_data=p.dirichlet_data, diffusion_function=df, name=f'dirichlet_{i}') for i, df in enumerate(p.diffusion.functions)] F_coefficients += p.diffusion.coefficients elif p.diffusion is not None: L += [DiffusionOperator(grid, boundary_info, diffusion_function=p.diffusion, name='diffusion')] L_coefficients += [1.] if p.dirichlet_data is not None: F += [L2ProductFunctional(grid, None, boundary_info=boundary_info, dirichlet_data=p.dirichlet_data, diffusion_function=p.diffusion, name='dirichlet')] F_coefficients += [1.] # advection part if isinstance(p.advection, LincombFunction): L += [LinearAdvectionLaxFriedrichs(grid, boundary_info, af, name=f'advection_{i}') for i, af in enumerate(p.advection.functions)] L_coefficients += list(p.advection.coefficients) elif p.advection is not None: L += [LinearAdvectionLaxFriedrichs(grid, boundary_info, p.advection, name='advection')] L_coefficients.append(1.) # nonlinear advection part if p.nonlinear_advection is not None: if num_flux == 'lax_friedrichs': L += [nonlinear_advection_lax_friedrichs_operator(grid, boundary_info, p.nonlinear_advection, dirichlet_data=p.dirichlet_data, lxf_lambda=lxf_lambda)] elif num_flux == 'engquist_osher': L += [nonlinear_advection_engquist_osher_operator(grid, boundary_info, p.nonlinear_advection, p.nonlinear_advection_derivative, gausspoints=eo_gausspoints, intervals=eo_intervals, dirichlet_data=p.dirichlet_data)] elif num_flux == 'simplified_engquist_osher': L += [nonlinear_advection_simplified_engquist_osher_operator(grid, boundary_info, p.nonlinear_advection, p.nonlinear_advection_derivative, dirichlet_data=p.dirichlet_data)] else: raise NotImplementedError L_coefficients.append(1.) # reaction part if isinstance(p.reaction, LincombFunction): raise NotImplementedError elif p.reaction is not None: L += [ReactionOperator(grid, p.reaction, name='reaction')] L_coefficients += [1.] # nonlinear reaction part if p.nonlinear_reaction is not None: L += [NonlinearReactionOperator(grid, p.nonlinear_reaction, p.nonlinear_reaction_derivative)] L_coefficients += [1.] # system operator if len(L_coefficients) == 1 and L_coefficients[0] == 1.: L = L[0] else: L = LincombOperator(operators=L, coefficients=L_coefficients, name='elliptic_operator') # rhs if len(F_coefficients) == 0: F = ZeroOperator(L.range, NumpyVectorSpace(1)) elif len(F_coefficients) == 1 and F_coefficients[0] == 1.: F = F[0] else: F = LincombOperator(operators=F, coefficients=F_coefficients, name='rhs') if grid.reference_element in (triangle, square): visualizer = PatchVisualizer(grid=grid, bounding_box=grid.bounding_box(), codim=0) elif grid.reference_element is line: visualizer = OnedVisualizer(grid=grid, codim=0) else: visualizer = None l2_product = L2Product(grid, name='l2') products = {'l2': l2_product} parameter_space = p.parameter_space if hasattr(p, 'parameter_space') else None m = StationaryModel(L, F, products=products, visualizer=visualizer, parameter_space=parameter_space, name=f'{p.name}_FV') data = {'grid': grid, 'boundary_info': boundary_info} if preassemble: data['unassembled_m'] = m m = preassemble_(m) return m, data
def _discretize_fenics(xblocks, yblocks, grid_num_intervals, element_order): # assemble system matrices - FEniCS code ######################################## import dolfin as df mesh = df.UnitSquareMesh(grid_num_intervals, grid_num_intervals, 'crossed') V = df.FunctionSpace(mesh, 'Lagrange', element_order) u = df.TrialFunction(V) v = df.TestFunction(V) diffusion = df.Expression('(lower0 <= x[0]) * (open0 ? (x[0] < upper0) : (x[0] <= upper0)) *' '(lower1 <= x[1]) * (open1 ? (x[1] < upper1) : (x[1] <= upper1))', lower0=0., upper0=0., open0=0, lower1=0., upper1=0., open1=0, element=df.FunctionSpace(mesh, 'DG', 0).ufl_element()) def assemble_matrix(x, y, nx, ny): diffusion.user_parameters['lower0'] = x/nx diffusion.user_parameters['lower1'] = y/ny diffusion.user_parameters['upper0'] = (x + 1)/nx diffusion.user_parameters['upper1'] = (y + 1)/ny diffusion.user_parameters['open0'] = (x + 1 == nx) diffusion.user_parameters['open1'] = (y + 1 == ny) return df.assemble(df.inner(diffusion * df.nabla_grad(u), df.nabla_grad(v)) * df.dx) mats = [assemble_matrix(x, y, xblocks, yblocks) for x in range(xblocks) for y in range(yblocks)] mat0 = mats[0].copy() mat0.zero() h1_mat = df.assemble(df.inner(df.nabla_grad(u), df.nabla_grad(v)) * df.dx) l2_mat = df.assemble(u * v * df.dx) f = df.Constant(1.) * v * df.dx F = df.assemble(f) bc = df.DirichletBC(V, 0., df.DomainBoundary()) for m in mats: bc.zero(m) bc.apply(mat0) bc.apply(h1_mat) bc.apply(F) # wrap everything as a pyMOR model ################################## # FEniCS wrappers from pymor.bindings.fenics import FenicsVectorSpace, FenicsMatrixOperator, FenicsVisualizer # generic pyMOR classes from pymor.models.basic import StationaryModel from pymor.operators.constructions import LincombOperator, VectorOperator from pymor.parameters.functionals import ProjectionParameterFunctional from pymor.parameters.spaces import CubicParameterSpace # define parameter functionals (same as in pymor.analyticalproblems.thermalblock) def parameter_functional_factory(x, y): return ProjectionParameterFunctional(component_name='diffusion', component_shape=(yblocks, xblocks), index=(yblocks - y - 1, x), name=f'diffusion_{x}_{y}') parameter_functionals = tuple(parameter_functional_factory(x, y) for x in range(xblocks) for y in range(yblocks)) # wrap operators ops = [FenicsMatrixOperator(mat0, V, V)] + [FenicsMatrixOperator(m, V, V) for m in mats] op = LincombOperator(ops, (1.,) + parameter_functionals) rhs = VectorOperator(FenicsVectorSpace(V).make_array([F])) h1_product = FenicsMatrixOperator(h1_mat, V, V, name='h1_0_semi') l2_product = FenicsMatrixOperator(l2_mat, V, V, name='l2') # build model visualizer = FenicsVisualizer(FenicsVectorSpace(V)) parameter_space = CubicParameterSpace(op.parameter_type, 0.1, 1.) fom = StationaryModel(op, rhs, products={'h1_0_semi': h1_product, 'l2': l2_product}, parameter_space=parameter_space, visualizer=visualizer) return fom
def solve_for_local_correction(self, subdomain, Us, mu=None, inverse_options=None): grid, local_boundary_info, affine_lambda, kappa, f, block_space = self.enrichment_data neighborhood = self.neighborhoods[subdomain] neighborhood_space = block_space.restricted_to_neighborhood( neighborhood) # Compute current solution restricted to the neighborhood to be usable as Dirichlet values for the correction # problem. current_solution = [U._list for U in Us] assert np.all(len(v) == 1 for v in current_solution) current_solution = [v[0].impl for v in current_solution] current_solution = neighborhood_space.project_onto_neighborhood( current_solution, neighborhood) current_solution = make_discrete_function(neighborhood_space, current_solution) # Solve the local corrector problem. # LHS ops = [] for lambda_ in affine_lambda['functions']: ops.append( make_elliptic_swipdg_matrix_operator_on_neighborhood( grid, subdomain, local_boundary_info, neighborhood_space, lambda_, kappa, over_integrate=0)) ops_coeffs = affine_lambda['coefficients'].copy() # RHS funcs = [] # We don't have any boundary treatment right now. Things will probably # break in multiple ways in case of non-trivial boundary conditions, # so we can comment this out for now .. # for lambda_ in affine_lambda['functions']: # funcs.append(make_elliptic_swipdg_vector_functional_on_neighborhood( # grid, subdomain, local_boundary_info, # neighborhood_space, # current_solution, lambda_, kappa, # over_integrate=0)) # funcs_coeffs = affine_lambda['coefficients'].copy() funcs.append( make_l2_vector_functional_on_neighborhood(grid, subdomain, neighborhood_space, f, over_integrate=2)) # funcs_coeffs.append(1.) funcs_coeffs = [1] # assemble in one grid walk neighborhood_assembler = make_neighborhood_system_assembler( grid, subdomain, neighborhood_space) for op in ops: neighborhood_assembler.append(op) for func in funcs: neighborhood_assembler.append(func) neighborhood_assembler.assemble() # solve local_space_id = self.solution_space.subspaces[subdomain].id lhs = LincombOperator([ DuneXTMatrixOperator( o.matrix(), source_id=local_space_id, range_id=local_space_id) for o in ops ], ops_coeffs) rhs = LincombOperator([ VectorFunctional(lhs.range.make_array([v.vector()])) for v in funcs ], funcs_coeffs) correction = lhs.apply_inverse(rhs.as_source_array(mu), mu=mu, inverse_options=inverse_options) assert len(correction) == 1 # restrict to subdomain local_sizes = [ block_space.local_space(nn).size() for nn in neighborhood ] local_starts = [ int(np.sum(local_sizes[:nn])) for nn in range(len(local_sizes)) ] local_starts.append(neighborhood_space.mapper.size) localized_corrections_as_np = np.array(correction._list[0].impl, copy=False) localized_corrections_as_np = [ localized_corrections_as_np[local_starts[nn]:local_starts[nn + 1]] for nn in range(len(local_sizes)) ] subdomain_index_in_neighborhood = np.where( np.array(list(neighborhood)) == subdomain)[0] assert len(subdomain_index_in_neighborhood) == 1 subdomain_index_in_neighborhood = subdomain_index_in_neighborhood[0] subdomain_correction = Vector( local_sizes[subdomain_index_in_neighborhood], 0.) subdomain_correction_as_np = np.array(subdomain_correction, copy=False) subdomain_correction_as_np[:] = localized_corrections_as_np[ subdomain_index_in_neighborhood][:] return self.solution_space.subspaces[subdomain].make_array( [subdomain_correction])