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 __init__(self, block_space, component): assert isinstance(block_space, BlockVectorSpace) assert 0 <= component < len(block_space.subspaces) blocks = [ ZeroOperator(space, space) if i != component else IdentityOperator(space) for i, space in enumerate(block_space.subspaces) ] super().__init__(blocks)
def action_ZeroOperator(self, op): range_basis, source_basis = self.range_basis, self.source_basis if source_basis is not None and range_basis is not None: from pymor.operators.numpy import NumpyMatrixOperator return NumpyMatrixOperator(np.zeros((len(range_basis), len(source_basis))), name=op.name) else: new_source = NumpyVectorSpace(len(source_basis)) if source_basis is not None else op.source new_range = NumpyVectorSpace(len(range_basis)) if range_basis is not None else op.range return ZeroOperator(new_range, new_source, name=op.name)
def action_ZeroOperator(self, ops): without_zero = [(op, coeff) for op, coeff in zip(ops, self.coefficients) if not isinstance(op, ZeroOperator)] if len(without_zero) == 0: return ZeroOperator(ops[0].range, ops[0].source, name=self.name) else: new_ops, new_coeffs = zip(*without_zero) return assemble_lincomb(new_ops, new_coeffs, solver_options=self.solver_options, name=self.name)
def jacobian(self, U, mu=None): mu = self.parse_parameter(mu) options = self.solver_options.get( 'jacobian') if self.solver_options else None if len(self.interpolation_dofs) == 0: if isinstance(self.source, NumpyVectorSpace) and isinstance( self.range, NumpyVectorSpace): return NumpyMatrixOperator(np.zeros( (self.range.dim, self.source.dim)), solver_options=options, source_id=self.source.id, range_id=self.range.id, name=self.name + '_jacobian') else: return ZeroOperator(self.source, self.range, name=self.name + '_jacobian') elif hasattr(self, 'operator'): return EmpiricalInterpolatedOperator(self.operator.jacobian(U, mu=mu), self.interpolation_dofs, self.collateral_basis, self.triangular, solver_options=options, name=self.name + '_jacobian') else: restricted_source = self.restricted_operator.source U_components = restricted_source.make_array( U.components(self.source_dofs)) JU = self.restricted_operator.jacobian(U_components, mu=mu) \ .apply(restricted_source.make_array(np.eye(len(self.source_dofs)))) try: if self.triangular: interpolation_coefficients = solve_triangular( self.interpolation_matrix, JU.data.T, lower=True, unit_diagonal=True).T else: interpolation_coefficients = np.linalg.solve( self.interpolation_matrix, JU.data.T).T except ValueError: # this exception occurs when AU contains NaNs ... interpolation_coefficients = np.empty( (len(JU), len(self.collateral_basis))) + np.nan J = self.collateral_basis.lincomb(interpolation_coefficients) if isinstance(J.space, NumpyVectorSpace): J = NumpyMatrixOperator(J.data.T, range_id=self.range.id) else: J = VectorArrayOperator(J) return Concatenation(J, ComponentProjection(self.source_dofs, self.source), solver_options=options, name=self.name + '_jacobian')
def action_zero_coeff(self, ops): if all(coeff != 0 for coeff in self.coefficients): raise RuleNotMatchingError without_zero = [(op, coeff) for op, coeff in zip(ops, self.coefficients) if coeff != 0] if len(without_zero) == 0: return ZeroOperator(ops[0].range, ops[0].source, name=self.name) else: new_ops, new_coeffs = zip(*without_zero) return assemble_lincomb(new_ops, new_coeffs, solver_options=self.solver_options, name=self.name)
def action_EmpiricalInterpolatedOperator(self, op): range_basis, source_basis = self.range_basis, self.source_basis if len(op.interpolation_dofs) == 0: return self.apply(ZeroOperator(op.range, op.source, op.name)) elif not hasattr(op, 'restricted_operator') or source_basis is None: raise RuleNotMatchingError('Has no restricted operator or source_basis is None') if range_basis is not None: projected_collateral_basis = NumpyVectorSpace.make_array(op.collateral_basis.dot(range_basis)) else: projected_collateral_basis = op.collateral_basis return ProjectedEmpiciralInterpolatedOperator(op.restricted_operator, op.interpolation_matrix, NumpyVectorSpace.make_array(source_basis.dofs(op.source_dofs)), projected_collateral_basis, op.triangular, None, op.name)
def action_ZeroOperator(self, op, range_basis, source_basis, product=None): if source_basis is not None and range_basis is not None: from pymor.operators.numpy import NumpyMatrixOperator return NumpyMatrixOperator(np.zeros( (len(range_basis), len(source_basis))), source_id=op.source.id, range_id=op.range.id, name=op.name) else: new_source = (NumpyVectorSpace(len(source_basis), op.source.id) if source_basis is not None else op.source) new_range = (NumpyVectorSpace(len(range_basis), op.range.id) if range_basis is not None else op.range) return ZeroOperator(new_source, new_range, name=op.name)
def __init__(self, blocks): blocks = np.array(blocks) assert 1 <= blocks.ndim <= 2 if self.blocked_source and self.blocked_range: assert blocks.ndim == 2 elif self.blocked_source: if blocks.ndim == 1: blocks.shape = (1, len(blocks)) else: if blocks.ndim == 1: blocks.shape = (len(blocks), 1) self.blocks = blocks assert all( isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all( any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all( any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range spaces for every column/row source_spaces = [None for j in range(blocks.shape[1])] range_spaces = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_spaces[j] is None or op.source == source_spaces[j] source_spaces[j] = op.source assert range_spaces[i] is None or op.range == range_spaces[i] range_spaces[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self.blocks[i, j] = ZeroOperator(range_spaces[i], source_spaces[j]) self.source = BlockVectorSpace( source_spaces) if self.blocked_source else source_spaces[0] self.range = BlockVectorSpace( range_spaces) if self.blocked_range else range_spaces[0] self.num_source_blocks = len(source_spaces) self.num_range_blocks = len(range_spaces) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators())
def d_mu(self, parameter, index=0): """Return the operator's derivative with respect to a given parameter. Parameters ---------- parameter The parameter w.r.t. which to return the derivative. index Index of the parameter's component w.r.t which to return the derivative. Returns ------- New |Operator| representing the partial derivative. """ if parameter in self.parameters: raise NotImplementedError else: from pymor.operators.constructions import ZeroOperator return ZeroOperator(self.range, self.source, name=self.name + '_d_mu')
def __init__(self, T, initial_data, operator, rhs, mass=None, time_stepper=None, num_values=None, output_functional=None, products=None, error_estimator=None, visualizer=None, name=None): if isinstance(rhs, VectorArray): assert rhs in operator.range rhs = VectorOperator(rhs, name='rhs') if isinstance(initial_data, VectorArray): assert initial_data in operator.source initial_data = VectorOperator(initial_data, name='initial_data') mass = mass or IdentityOperator(operator.source) rhs = rhs or ZeroOperator(operator.source, NumpyVectorSpace(1)) assert isinstance(time_stepper, TimeStepper) assert initial_data.source.is_scalar assert operator.source == initial_data.range assert rhs.linear and rhs.range == operator.range and rhs.source.is_scalar assert mass.linear and mass.source == mass.range == operator.source assert output_functional is None or output_functional.source == operator.source super().__init__(products=products, error_estimator=error_estimator, visualizer=visualizer, name=name) self.parameters_internal = {'t': 1} self.__auto_init(locals()) self.solution_space = operator.source self.linear = operator.linear and (output_functional is None or output_functional.linear) if output_functional is not None: self.dim_output = output_functional.range.dim
def d_mu(self, component, index=()): """Return the operator's derivative with respect to an index of a parameter component. Parameters ---------- component Parameter component index index in the parameter component Returns ------- New |Operator| representing the partial derivative. """ if self.parametric: raise NotImplementedError else: from pymor.operators.constructions import ZeroOperator return ZeroOperator(self.range, self.source, name=self.name + '_d_mu')
def projected(self, range_basis, source_basis, product=None, name=None): assert source_basis is None or source_basis in self.source assert range_basis is None or range_basis in self.range assert product is None or product.source == product.range == self.range if len(self.interpolation_dofs) == 0: return ZeroOperator(self.source, self.range, self.name).projected(range_basis, source_basis, product, name) elif not hasattr(self, 'restricted_operator') or source_basis is None: return super().projected(range_basis, source_basis, product, name) else: name = name or self.name + '_projected' if range_basis is not None: if product is None: projected_collateral_basis = NumpyVectorArray(self.collateral_basis.dot(range_basis)) else: projected_collateral_basis = NumpyVectorArray(product.apply2(self.collateral_basis, range_basis)) else: projected_collateral_basis = self.collateral_basis return ProjectedEmpiciralInterpolatedOperator(self.restricted_operator, self.interpolation_matrix, NumpyVectorArray(source_basis.components(self.source_dofs), copy=False), projected_collateral_basis, self.triangular, None, name)
def thermalblock_zero_factory(xblocks, yblocks, diameter, seed): from pymor.operators.constructions import ZeroOperator _, _, U, V, sp, rp = thermalblock_factory(xblocks, yblocks, diameter, seed) return ZeroOperator(V.space, U.space), None, U, V, sp, rp
def discretize_quadratic_pdeopt_stationary_cg(problem, diameter=np.sqrt(2) / 100., weights=None, parameter_scales=None, domain_of_interest=None, desired_temperature=None, mu_for_u_d=None, mu_for_tikhonov=False, parameters_in_q=True, product='h1_l2_boundary', solver_options=None, use_corrected_functional=True, use_corrected_gradient=False, adjoint_approach=False): if use_corrected_functional: print('I am using the corrected functional!!') else: print('I am using the OLD functional!!') if use_corrected_gradient: print('I am using the corrected gradient!!') else: if adjoint_approach: print( 'I am using the adjoint approach for computing the gradient!!') print('I am using the OLD gradient!!') mu_bar = _construct_mu_bar(problem) print(mu_bar) primal_fom, data = discretize_stationary_cg(problem, diameter=diameter, grid_type=RectGrid, energy_product=mu_bar) #Preassemble non parametric parts: simplify, put the constant part in only one function simplified_operators = [ ZeroOperator(primal_fom.solution_space, primal_fom.solution_space) ] simplified_coefficients = [1] to_pre_assemble = ZeroOperator(primal_fom.solution_space, primal_fom.solution_space) if isinstance(primal_fom.operator, LincombOperator): for (i, coef) in enumerate(primal_fom.operator.coefficients): if isinstance(coef, Parametric): simplified_coefficients.append(coef) simplified_operators.append(primal_fom.operator.operators[i]) else: to_pre_assemble += coef * primal_fom.operator.operators[i] else: to_pre_assemble += primal_fom.operator simplified_operators[0] += to_pre_assemble simplified_operators[0] = simplified_operators[0].assemble() lincomb_operator = LincombOperator( simplified_operators, simplified_coefficients, solver_options=primal_fom.operator.solver_options) simplified_rhs = [ ZeroOperator(primal_fom.solution_space, NumpyVectorSpace(1)) ] simplified_rhs_coefficients = [1] to_pre_assemble = ZeroOperator(primal_fom.solution_space, NumpyVectorSpace(1)) if isinstance(primal_fom.rhs, LincombOperator): for (i, coef) in enumerate(primal_fom.rhs.coefficients): if isinstance(coef, Parametric): simplified_rhs_coefficients.append(coef) simplified_rhs.append(primal_fom.rhs.operators[i]) else: to_pre_assemble += coef * primal_fom.rhs.operators[i] else: to_pre_assemble += primal_fom.rhs simplified_rhs[0] += to_pre_assemble simplified_rhs[0] = simplified_rhs[0].assemble() lincomb_rhs = LincombOperator(simplified_rhs, simplified_rhs_coefficients) primal_fom = primal_fom.with_(operator=lincomb_operator, rhs=lincomb_rhs) grid = data['grid'] d = grid.dim # prepare data functions if desired_temperature is not None: u_desired = ConstantFunction(desired_temperature, d) if domain_of_interest is None: domain_of_interest = ConstantFunction(1., d) if mu_for_u_d is not None: domain_of_interest = ConstantFunction(1., d) modifified_mu = mu_for_u_d.copy() for key in mu_for_u_d.keys(): if len(mu_for_u_d[key]) == 0: modifified_mu.pop(key) u_d = primal_fom.solve(modifified_mu) else: assert desired_temperature is not None u_d = InterpolationOperator(grid, u_desired).as_vector() if grid.reference_element is square: L2_OP = L2ProductQ1 else: L2_OP = L2ProductP1 Restricted_L2_OP = L2_OP(grid, data['boundary_info'], dirichlet_clear_rows=False, coefficient_function=domain_of_interest) l2_u_d_squared = Restricted_L2_OP.apply2(u_d, u_d)[0][0] constant_part = 0.5 * l2_u_d_squared # assemble output functional from pdeopt.theta import build_output_coefficient if weights is not None: weight_for_J = weights.pop('state') else: weight_for_J = 1. if isinstance(weight_for_J, dict): assert len( weight_for_J ) == 4, 'you need to give all derivatives including second order' state_functional = ExpressionParameterFunctional( weight_for_J['function'], weight_for_J['parameter_type'], derivative_expressions=weight_for_J['derivative'], second_derivative_expressions=weight_for_J['second_derivatives']) elif isinstance(weight_for_J, float) or isinstance(weight_for_J, int): state_functional = ConstantParameterFunctional(weight_for_J) else: assert 0, 'state weight needs to be an integer or a dict with derivatives' if mu_for_tikhonov: if mu_for_u_d is not None: mu_for_tikhonov = mu_for_u_d else: assert isinstance(mu_for_tikhonov, dict) output_coefficient = build_output_coefficient( primal_fom.parameter_type, weights, mu_for_tikhonov, parameter_scales, state_functional, constant_part) else: output_coefficient = build_output_coefficient( primal_fom.parameter_type, weights, None, parameter_scales, state_functional, constant_part) output_functional = {} output_functional['output_coefficient'] = output_coefficient output_functional['linear_part'] = LincombOperator( [VectorOperator(Restricted_L2_OP.apply(u_d))], [-state_functional]) # j(.) output_functional['bilinear_part'] = LincombOperator( [Restricted_L2_OP], [0.5 * state_functional]) # k(.,.) output_functional['d_u_linear_part'] = LincombOperator( [VectorOperator(Restricted_L2_OP.apply(u_d))], [-state_functional]) # j(.) output_functional['d_u_bilinear_part'] = LincombOperator( [Restricted_L2_OP], [state_functional]) # 2k(.,.) l2_boundary_product = RobinBoundaryOperator( grid, data['boundary_info'], robin_data=(ConstantFunction(1, 2), ConstantFunction(1, 2)), name='l2_boundary_product') # choose product if product == 'h1_l2_boundary': opt_product = primal_fom.h1_semi_product + l2_boundary_product # h1_semi + l2_boundary elif product == 'fixed_energy': opt_product = primal_fom.energy_product # energy w.r.t. mu_bar (see above) else: assert 0, 'product: {} is not nown'.format(product) print('my product is {}'.format(product)) primal_fom = primal_fom.with_( products=dict(opt=opt_product, l2_boundary=l2_boundary_product, **primal_fom.products)) pde_opt_fom = QuadraticPdeoptStationaryModel( primal_fom, output_functional, opt_product=opt_product, use_corrected_functional=use_corrected_functional, use_corrected_gradient=use_corrected_gradient) return pde_opt_fom, data, mu_bar
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 restricted(self, dofs): from pymor.tools.mpi import parallel if parallel: raise NotImplementedError('SubMesh does not work in parallel') with self.logger.block( f'Restricting operator to {len(dofs)} dofs ...'): if len(dofs) == 0: return ZeroOperator(NumpyVectorSpace(0), NumpyVectorSpace(0)), np.array( [], dtype=np.int) if self.source.V.mesh().id() != self.range.V.mesh().id(): raise NotImplementedError self.logger.info('Computing affected cells ...') mesh = self.source.V.mesh() range_dofmap = self.range.V.dofmap() affected_cell_indices = set() for c in df.cells(mesh): cell_index = c.index() local_dofs = range_dofmap.cell_dofs(cell_index) for ld in local_dofs: if ld in dofs: affected_cell_indices.add(cell_index) continue affected_cell_indices = list(sorted(affected_cell_indices)) if any(i.integral_type() not in ('cell', 'exterior_facet') for i in self.form.integrals()): # enlarge affected_cell_indices if needed raise NotImplementedError self.logger.info('Computing source DOFs ...') source_dofmap = self.source.V.dofmap() source_dofs = set() for cell_index in affected_cell_indices: local_dofs = source_dofmap.cell_dofs(cell_index) source_dofs.update(local_dofs) source_dofs = np.array(sorted(source_dofs), dtype=np.intc) self.logger.info('Building submesh ...') subdomain = df.MeshFunction('size_t', mesh, mesh.geometry().dim()) for ci in affected_cell_indices: subdomain.set_value(ci, 1) submesh = df.SubMesh(mesh, subdomain, 1) self.logger.info('Building UFL form on submesh ...') form_r, V_r_source, V_r_range, source_function_r = self._restrict_form( submesh, source_dofs) self.logger.info('Building DirichletBCs on submesh ...') bc_r = self._restrict_dirichlet_bcs(submesh, source_dofs, V_r_source) self.logger.info('Computing source DOF mapping ...') restricted_source_dofs = self._build_dof_map( self.source.V, V_r_source, source_dofs) self.logger.info('Computing range DOF mapping ...') restricted_range_dofs = self._build_dof_map( self.range.V, V_r_range, dofs) op_r = FenicsOperator(form_r, FenicsVectorSpace(V_r_source), FenicsVectorSpace(V_r_range), source_function_r, dirichlet_bcs=bc_r, parameter_setter=self.parameter_setter, parameters=self.parameters) return (RestrictedFenicsOperator(op_r, restricted_range_dofs), source_dofs[np.argsort(restricted_source_dofs)])
def test_d_mu_of_LincombOperator(): dict_of_d_mus = {'mu': ['100', '2 * mu[0]'], 'nu': 'cos(nu)'} dict_of_second_derivative = { 'mu': [{ 'mu': ['0', '2'], 'nu': '0' }, { 'mu': ['2', '0'], 'nu': '0' }], 'nu': { 'mu': ['0', '0'], 'nu': '-sin(nu)' } } pf = ProjectionParameterFunctional('mu', (2, ), (0, )) epf = ExpressionParameterFunctional( '100 * mu[0] + 2 * mu[1] * mu[0] + sin(nu)', { 'mu': (2, ), 'nu': () }, 'functional_with_derivative', dict_of_d_mus, dict_of_second_derivative) 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) second_derivative_first_mu_first_mu = operator.d_mu('mu', 0).d_mu('mu', 0) second_derivative_first_mu_second_mu = operator.d_mu('mu', 0).d_mu('mu', 1) second_derivative_first_mu_nu = operator.d_mu('mu', 0).d_mu('nu') second_derivative_second_mu_first_mu = operator.d_mu('mu', 1).d_mu('mu', 0) second_derivative_second_mu_second_mu = operator.d_mu('mu', 1).d_mu('mu', 1) second_derivative_second_mu_nu = operator.d_mu('mu', 1).d_mu('nu') second_derivative_nu_first_mu = operator.d_mu('nu').d_mu('mu', 0) second_derivative_nu_second_mu = operator.d_mu('nu').d_mu('mu', 1) second_derivative_nu_nu = operator.d_mu('nu').d_mu('nu') hes_mu_1_mu_1 = second_derivative_first_mu_first_mu.evaluate_coefficients( mu) hes_mu_1_mu_2 = second_derivative_first_mu_second_mu.evaluate_coefficients( mu) hes_mu_1_nu = second_derivative_first_mu_nu.evaluate_coefficients(mu) hes_mu_2_mu_1 = second_derivative_second_mu_first_mu.evaluate_coefficients( mu) hes_mu_2_mu_2 = second_derivative_second_mu_second_mu.evaluate_coefficients( mu) hes_mu_2_nu = second_derivative_second_mu_nu.evaluate_coefficients(mu) hes_nu_mu_1 = second_derivative_nu_first_mu.evaluate_coefficients(mu) hes_nu_mu_2 = second_derivative_nu_second_mu.evaluate_coefficients(mu) hes_nu_nu = second_derivative_nu_nu.evaluate_coefficients(mu) assert operator.evaluate_coefficients(mu) == [1., 10, 1040.] assert eval_mu_1 == [0., 1., 100.] assert eval_mu_2 == [0., 0., 2. * 10] assert eval_nu == [0., 0., 1.] assert hes_mu_1_mu_1 == [0., 0., 0.] assert hes_mu_1_mu_2 == [0., 0., 2] assert hes_mu_1_nu == [0., 0., 0] assert hes_mu_2_mu_1 == [0., 0., 2] assert hes_mu_2_mu_2 == [0., 0., 0] assert hes_mu_2_nu == [0., 0., 0] assert hes_nu_mu_1 == [0., 0., 0] assert hes_nu_mu_2 == [0., 0., 0] assert hes_nu_nu == [0., 0., -0]