Esempio n. 1
0
    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
Esempio n. 2
0
    def __init__(self, T, initial_data, operator, rhs, mass=None, time_stepper=None, num_values=None,
                 output_functional=None, products=None, parameter_space=None, estimator=None, visualizer=None,
                 name=None):

        if isinstance(rhs, VectorArrayInterface):
            assert rhs in operator.range
            rhs = VectorOperator(rhs, name='rhs')
        if isinstance(initial_data, VectorArrayInterface):
            assert initial_data in operator.source
            initial_data = VectorOperator(initial_data, name='initial_data')

        assert isinstance(time_stepper, TimeStepperInterface)
        assert initial_data.source.is_scalar
        assert operator.source == initial_data.range
        assert rhs is None \
            or rhs.linear and rhs.range == operator.range and rhs.source.is_scalar
        assert mass is None \
            or mass.linear and mass.source == mass.range == operator.source
        assert output_functional is None or output_functional.source == operator.source

        super().__init__(products=products, estimator=estimator, visualizer=visualizer, name=name)

        self.build_parameter_type(initial_data, operator, rhs, mass, output_functional, provides={'_t': 0})
        self.__auto_init(locals())
        self.solution_space = operator.source
        self.linear = operator.linear and (output_functional is None or output_functional.linear)
Esempio n. 3
0
    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
Esempio n. 4
0
    def __init__(self,
                 T,
                 initial_data,
                 operator,
                 rhs,
                 mass=None,
                 time_stepper=None,
                 num_values=None,
                 outputs=None,
                 products=None,
                 parameter_space=None,
                 estimator=None,
                 visualizer=None,
                 cache_region=None,
                 name=None):

        if isinstance(rhs, VectorArrayInterface):
            assert rhs in operator.range
            rhs = VectorOperator(rhs, name='rhs')
        if isinstance(initial_data, VectorArrayInterface):
            assert initial_data in operator.source
            initial_data = VectorOperator(initial_data, name='initial_data')

        assert isinstance(time_stepper, TimeStepperInterface)
        assert initial_data.source.is_scalar
        assert operator.source == initial_data.range
        assert rhs is None \
            or rhs.linear and rhs.range == operator.range and rhs.source.is_scalar
        assert mass is None \
            or mass.linear and mass.source == mass.range == operator.source

        super().__init__(products=products,
                         estimator=estimator,
                         visualizer=visualizer,
                         cache_region=cache_region,
                         name=name)
        self.T = T
        self.initial_data = initial_data
        self.operator = operator
        self.rhs = rhs
        self.mass = mass
        self.solution_space = self.operator.source
        self.time_stepper = time_stepper
        self.num_values = num_values
        self.outputs = FrozenDict(outputs or {})
        self.linear = operator.linear and all(
            output.linear for output in self.outputs.values())
        self.build_parameter_type(self.initial_data,
                                  self.operator,
                                  self.rhs,
                                  self.mass,
                                  provides={'_t': 0})
        self.parameter_space = parameter_space
Esempio n. 5
0
def thermalblock_vector_factory(xblocks, yblocks, diameter, seed):
    from pymor.operators.constructions import VectorOperator
    _, _, U, V, sp, rp = thermalblock_factory(xblocks, yblocks, diameter, seed)
    op = VectorOperator(U.copy(ind=0))
    U = NumpyVectorArray(np.random.random((7, 1)), copy=False)
    sp = NumpyMatrixOperator(np.eye(1) * 2)
    return op, None, U, V, sp, rp
Esempio n. 6
0
def thermalblock_vector_factory(xblocks, yblocks, diameter, seed):
    from pymor.operators.constructions import VectorOperator
    _, _, U, V, sp, rp = thermalblock_factory(xblocks, yblocks, diameter, seed)
    op = VectorOperator(U[0])
    U = op.source.make_array(np.random.random((7, 1)))
    sp = NumpyMatrixOperator(np.eye(1) * 2)
    return op, None, U, V, sp, rp
Esempio n. 7
0
    def __init__(self, T, initial_data, operator, rhs, mass=None, time_stepper=None, num_values=None,
                 products=None, operators=None, parameter_space=None, estimator=None, visualizer=None,
                 cache_region=None, name=None):

        if isinstance(initial_data, VectorArrayInterface):
            initial_data = VectorOperator(initial_data, name='initial_data')

        assert isinstance(time_stepper, TimeStepperInterface)
        assert initial_data.source.is_scalar
        assert operator.source == operator.range == initial_data.range
        assert rhs is None \
            or rhs.linear and rhs.source == operator.source and rhs.range.is_scalar
        assert mass is None \
            or mass.linear and mass.source == mass.range == operator.source

        super().__init__(initial_data=initial_data, operator=operator, rhs=rhs, mass=mass,
                         operators=operators, products=products, estimator=estimator,
                         visualizer=visualizer, cache_region=cache_region, name=name)
        self.T = T
        self.solution_space = self.operator.source
        self.time_stepper = time_stepper
        self.num_values = num_values
        self.build_parameter_type(self.initial_data, self.operator, self.rhs, self.mass, provides={'_t': 0})
        self.parameter_space = parameter_space
        if hasattr(time_stepper, 'nt'):
            self.add_with_arguments = self.add_with_arguments | {'time_stepper_nt'}
Esempio n. 8
0
    def __init__(self,
                 operator,
                 rhs,
                 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')

        assert rhs.range == operator.range and rhs.source.is_scalar and rhs.linear
        assert output_functional is None or output_functional.source == operator.source

        super().__init__(products=products,
                         error_estimator=error_estimator,
                         visualizer=visualizer,
                         name=name)

        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.output_space = output_functional.range
Esempio n. 9
0
    def __init__(self,
                 operator,
                 rhs,
                 outputs=None,
                 products=None,
                 parameter_space=None,
                 estimator=None,
                 visualizer=None,
                 cache_region=None,
                 name=None):

        if isinstance(rhs, VectorArrayInterface):
            assert rhs in operator.range
            rhs = VectorOperator(rhs, name='rhs')

        assert rhs.range == operator.range and rhs.source.is_scalar and rhs.linear

        super().__init__(products=products,
                         estimator=estimator,
                         visualizer=visualizer,
                         cache_region=cache_region,
                         name=name)
        self.operator = operator
        self.rhs = rhs
        self.outputs = FrozenDict(outputs or {})
        self.solution_space = self.operator.source
        self.build_parameter_type(operator, rhs)
        self.parameter_space = parameter_space
Esempio n. 10
0
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
Esempio n. 11
0
def discretize(dim, n, order):
    # ### problem definition
    import dolfin as df

    if dim == 2:
        mesh = df.UnitSquareMesh(n, n)
    elif dim == 3:
        mesh = df.UnitCubeMesh(n, n, n)
    else:
        raise NotImplementedError

    V = df.FunctionSpace(mesh, "CG", order)

    g = df.Constant(1.0)
    c = df.Constant(1.)

    class DirichletBoundary(df.SubDomain):
        def inside(self, x, on_boundary):
            return abs(x[0] - 1.0) < df.DOLFIN_EPS and on_boundary

    db = DirichletBoundary()
    bc = df.DirichletBC(V, g, db)

    u = df.Function(V)
    v = df.TestFunction(V)
    f = df.Expression("x[0]*sin(x[1])", degree=2)
    F = df.inner(
        (1 + c * u**2) * df.grad(u), df.grad(v)) * df.dx - f * v * df.dx

    df.solve(F == 0,
             u,
             bc,
             solver_parameters={"newton_solver": {
                 "relative_tolerance": 1e-6
             }})

    # ### pyMOR wrapping
    from pymor.bindings.fenics import FenicsVectorSpace, FenicsOperator, FenicsVisualizer
    from pymor.models.basic import StationaryModel
    from pymor.operators.constructions import VectorOperator

    space = FenicsVectorSpace(V)
    op = FenicsOperator(
        F,
        space,
        space,
        u, (bc, ),
        parameter_setter=lambda mu: c.assign(mu['c'].item()),
        parameters={'c': 1},
        solver_options={'inverse': {
            'type': 'newton',
            'rtol': 1e-6
        }})
    rhs = VectorOperator(op.range.zeros())

    fom = StationaryModel(op, rhs, visualizer=FenicsVisualizer(space))

    return fom
Esempio n. 12
0
 def action_NumpyMatrixOperator(self, op):
     vector = op.source.dim == 1
     functional = op.range.dim == 1
     if vector and functional:
         raise NotImplementedError
     if vector:
         space = NumpyListVectorSpace(op.range.dim, op.range.id)
         return VectorOperator(space.from_numpy(op.matrix.reshape((1, -1))), op.name)
     elif functional:
         space = NumpyListVectorSpace(op.source.dim, op.source.id)
         return VectorFunctional(space.from_numpy(op.matrix.ravel()), op.name)
     else:
         return op.with_(new_type=NumpyListVectorArrayMatrixOperator)
Esempio n. 13
0
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
Esempio n. 14
0
    def __init__(self,
                 T,
                 initial_data,
                 operator,
                 rhs=None,
                 mass=None,
                 time_stepper=None,
                 num_values=None,
                 products=None,
                 operators=None,
                 functionals=None,
                 vector_operators=None,
                 parameter_space=None,
                 estimator=None,
                 visualizer=None,
                 cache_region='disk',
                 name=None):
        functionals = functionals or {}
        operators = operators or {}
        vector_operators = vector_operators or {}
        if isinstance(initial_data, VectorArrayInterface):
            initial_data = VectorOperator(initial_data, name='initial_data')

        assert isinstance(initial_data, OperatorInterface)
        assert initial_data.source == NumpyVectorSpace(1)
        assert 'initial_data' not in vector_operators or initial_data == vector_operators[
            'initial_data']

        assert isinstance(operator, OperatorInterface)
        assert operator.source == operator.range == initial_data.range
        assert 'operator' not in operators or operator == operators['operator']

        assert rhs is None or isinstance(rhs, OperatorInterface) and rhs.linear
        assert rhs is None or rhs.source == operator.source and rhs.range.dim == 1
        assert 'rhs' not in functionals or rhs == functionals['rhs']

        assert mass is None or isinstance(mass,
                                          OperatorInterface) and mass.linear
        assert mass is None or mass.source == mass.range == operator.source
        assert 'mass' not in operators or mass == operators['mass']

        assert isinstance(time_stepper, TimeStepperInterface)
        assert all(f.source == operator.source for f in functionals.values())

        operators_with_operator_mass = {'operator': operator, 'mass': mass}
        operators_with_operator_mass.update(operators)

        functionals_with_rhs = {'rhs': rhs} if rhs else {}
        functionals_with_rhs.update(functionals)

        vector_operators_with_initial_data = {'initial_data': initial_data}
        vector_operators_with_initial_data.update(vector_operators)

        super(InstationaryDiscretization, self).__init__(
            operators=operators_with_operator_mass,
            functionals=functionals_with_rhs,
            vector_operators=vector_operators_with_initial_data,
            products=products,
            estimator=estimator,
            visualizer=visualizer,
            cache_region=cache_region,
            name=name)
        self.T = T
        self.solution_space = operator.source
        self.initial_data = initial_data
        self.operator = operator
        self.rhs = rhs
        self.mass = mass
        self.time_stepper = time_stepper
        self.num_values = num_values
        self.build_parameter_type(inherits=(initial_data, operator, rhs, mass),
                                  provides={'_t': 0})
        self.parameter_space = parameter_space

        if hasattr(time_stepper, 'nt'):
            self.with_arguments = self.with_arguments.union(
                {'time_stepper_nt'})
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