コード例 #1
0
    def _build_primal_rom(self):
        if self.reductor_type == 'simple_coercive':
            print('building simple coercive primal reductor...')
            primal_reductor = SimpleCoerciveRBReductor(
                self.fom.primal_model,
                RB=self.RBPrimal,
                product=self.opt_product,
                coercivity_estimator=self.coercivity_estimator)
        elif self.reductor_type == 'non_assembled':
            print('building non assembled for primal reductor...')
            primal_reductor = NonAssembledCoerciveRBReductor(
                self.fom.primal_model,
                RB=self.RBPrimal,
                product=self.opt_product,
                coercivity_estimator=self.coercivity_estimator)
        else:
            print('building coercive primal reductor...')
            primal_reductor = CoerciveRBReductor(
                self.fom.primal_model,
                RB=self.RBPrimal,
                product=self.opt_product,
                coercivity_estimator=self.coercivity_estimator)

        primal_rom = primal_reductor.reduce()
        return primal_rom, primal_reductor
コード例 #2
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
コード例 #3
0
def thermalblock_demo(args):
    args['--grid'] = int(args['--grid'])
    args['RBSIZE'] = int(args['RBSIZE'])
    args['--test'] = int(args['--test'])
    args['--ipython-engines'] = int(args['--ipython-engines'])
    args['--extension-alg'] = args['--extension-alg'].lower()
    assert args['--extension-alg'] in {'trivial', 'gram_schmidt'}
    args['--product'] = args['--product'].lower()
    assert args['--product'] in {'trivial', 'h1'}
    args['--reductor'] = args['--reductor'].lower()
    assert args['--reductor'] in {'traditional', 'residual_basis'}
    args['--cache-region'] = args['--cache-region'].lower()
    args['--validation-mus'] = int(args['--validation-mus'])
    args['--rho'] = float(args['--rho'])
    args['--gamma'] = float(args['--gamma'])
    args['--theta'] = float(args['--theta'])

    problem = thermal_block_problem(num_blocks=(2, 2))
    functionals = [
        ExpressionParameterFunctional('diffusion[0]', {'diffusion': 2}),
        ExpressionParameterFunctional('diffusion[1]**2', {'diffusion': 2}),
        ExpressionParameterFunctional('diffusion[0]', {'diffusion': 2}),
        ExpressionParameterFunctional('diffusion[1]', {'diffusion': 2})
    ]
    problem = problem.with_(
        diffusion=problem.diffusion.with_(coefficients=functionals), )

    print('Discretize ...')
    fom, _ = discretize_stationary_cg(problem, diameter=1. / args['--grid'])

    if args['--list-vector-array']:
        from pymor.discretizers.builtin.list import convert_to_numpy_list_vector_array
        fom = convert_to_numpy_list_vector_array(fom)

    if args['--cache-region'] != 'none':
        # building a cache_id is only needed for persistent CacheRegions
        cache_id = f"pymordemos.thermalblock_adaptive {args['--grid']}"
        fom.enable_caching(args['--cache-region'], cache_id)

    if args['--plot-solutions']:
        print('Showing some solutions')
        Us = ()
        legend = ()
        for mu in problem.parameter_space.sample_randomly(2):
            print(f"Solving for diffusion = \n{mu['diffusion']} ... ")
            sys.stdout.flush()
            Us = Us + (fom.solve(mu), )
            legend = legend + (str(mu['diffusion']), )
        fom.visualize(Us,
                      legend=legend,
                      title='Detailed Solutions for different parameters',
                      block=True)

    print('RB generation ...')

    product = fom.h1_0_semi_product if args['--product'] == 'h1' else None
    coercivity_estimator = ExpressionParameterFunctional(
        'min([diffusion[0], diffusion[1]**2])', fom.parameters)
    reductors = {
        'residual_basis':
        CoerciveRBReductor(fom,
                           product=product,
                           coercivity_estimator=coercivity_estimator),
        'traditional':
        SimpleCoerciveRBReductor(fom,
                                 product=product,
                                 coercivity_estimator=coercivity_estimator)
    }
    reductor = reductors[args['--reductor']]

    pool = new_parallel_pool(ipython_num_engines=args['--ipython-engines'],
                             ipython_profile=args['--ipython-profile'])
    greedy_data = rb_adaptive_greedy(
        fom,
        reductor,
        problem.parameter_space,
        validation_mus=args['--validation-mus'],
        rho=args['--rho'],
        gamma=args['--gamma'],
        theta=args['--theta'],
        use_estimator=not args['--without-estimator'],
        error_norm=fom.h1_0_semi_norm,
        max_extensions=args['RBSIZE'],
        visualize=not args['--no-visualize-refinement'])

    rom = greedy_data['rom']

    if args['--pickle']:
        print(
            f"\nWriting reduced model to file {args['--pickle']}_reduced ...")
        with open(args['--pickle'] + '_reduced', 'wb') as f:
            dump(rom, f)
        print(
            f"Writing detailed model and reductor to file {args['--pickle']}_detailed ..."
        )
        with open(args['--pickle'] + '_detailed', 'wb') as f:
            dump((fom, reductor), f)

    print('\nSearching for maximum error on random snapshots ...')

    results = reduction_error_analysis(
        rom,
        fom=fom,
        reductor=reductor,
        estimator=True,
        error_norms=(fom.h1_0_semi_norm, ),
        condition=True,
        test_mus=problem.parameter_space.sample_randomly(args['--test']),
        basis_sizes=25 if args['--plot-error-sequence'] else 1,
        plot=True,
        pool=pool)

    real_rb_size = rom.solution_space.dim

    print('''
*** RESULTS ***

Problem:
   number of blocks:                   2x2
   h:                                  sqrt(2)/{args[--grid]}

Greedy basis generation:
   estimator disabled:                 {args[--without-estimator]}
   extension method:                   {args[--extension-alg]}
   product:                            {args[--product]}
   prescribed basis size:              {args[RBSIZE]}
   actual basis size:                  {real_rb_size}
   elapsed time:                       {greedy_data[time]}
'''.format(**locals()))
    print(results['summary'])

    sys.stdout.flush()

    if args['--plot-error-sequence']:
        from matplotlib import pyplot as plt
        plt.show(results['figure'])
    if args['--plot-err']:
        mumax = results['max_error_mus'][0, -1]
        U = fom.solve(mumax)
        URB = reductor.reconstruct(rom.solve(mumax))
        fom.visualize(
            (U, URB, U - URB),
            legend=('Detailed Solution', 'Reduced Solution', 'Error'),
            title='Maximum Error Solution',
            separate_colorbars=True,
            block=True)
コード例 #4
0
ファイル: thermalblock.py プロジェクト: weslowrie/pymor
def main(args):

    args = parse_arguments(args)

    pool = new_parallel_pool(ipython_num_engines=args['--ipython-engines'], ipython_profile=args['--ipython-profile'])

    if args['--fenics']:
        fom, fom_summary = discretize_fenics(args['XBLOCKS'], args['YBLOCKS'], args['--grid'], args['--order'])
    else:
        fom, fom_summary = discretize_pymor(args['XBLOCKS'], args['YBLOCKS'], args['--grid'], args['--list-vector-array'])

    if args['--cache-region'] != 'none':
        # building a cache_id is only needed for persistent CacheRegions
        cache_id = (f"pymordemos.thermalblock {args['--fenics']} {args['XBLOCKS']} {args['YBLOCKS']}"
                    f"{args['--grid']} {args['--order']}")
        fom.enable_caching(args['--cache-region'], cache_id)

    if args['--plot-solutions']:
        print('Showing some solutions')
        Us = ()
        legend = ()
        for mu in fom.parameter_space.sample_randomly(2):
            print(f"Solving for diffusion = \n{mu['diffusion']} ... ")
            sys.stdout.flush()
            Us = Us + (fom.solve(mu),)
            legend = legend + (str(mu['diffusion']),)
        fom.visualize(Us, legend=legend, title='Detailed Solutions for different parameters',
                      separate_colorbars=False, block=True)

    print('RB generation ...')

    # define estimator for coercivity constant
    from pymor.parameters.functionals import ExpressionParameterFunctional
    coercivity_estimator = ExpressionParameterFunctional('min(diffusion)', fom.parameter_type)

    # inner product for computation of Riesz representatives
    product = fom.h1_0_semi_product if args['--product'] == 'h1' else None

    if args['--reductor'] == 'residual_basis':
        from pymor.reductors.coercive import CoerciveRBReductor
        reductor = CoerciveRBReductor(fom, product=product, coercivity_estimator=coercivity_estimator,
                                      check_orthonormality=False)
    elif args['--reductor'] == 'traditional':
        from pymor.reductors.coercive import SimpleCoerciveRBReductor
        reductor = SimpleCoerciveRBReductor(fom, product=product, coercivity_estimator=coercivity_estimator,
                                            check_orthonormality=False)
    else:
        assert False  # this should never happen

    if args['--alg'] == 'naive':
        rom, red_summary = reduce_naive(fom=fom, reductor=reductor, basis_size=args['RBSIZE'])
    elif args['--alg'] == 'greedy':
        parallel = not (args['--fenics'] and args['--greedy-without-estimator'])  # cannot pickle FEniCS model
        rom, red_summary = reduce_greedy(fom=fom, reductor=reductor, snapshots_per_block=args['SNAPSHOTS'],
                                         extension_alg_name=args['--extension-alg'],
                                         max_extensions=args['RBSIZE'],
                                         use_estimator=not args['--greedy-without-estimator'],
                                         pool=pool if parallel else None)
    elif args['--alg'] == 'adaptive_greedy':
        parallel = not (args['--fenics'] and args['--greedy-without-estimator'])  # cannot pickle FEniCS model
        rom, red_summary = reduce_adaptive_greedy(fom=fom, reductor=reductor, validation_mus=args['SNAPSHOTS'],
                                                  extension_alg_name=args['--extension-alg'],
                                                  max_extensions=args['RBSIZE'],
                                                  use_estimator=not args['--greedy-without-estimator'],
                                                  rho=args['--adaptive-greedy-rho'],
                                                  gamma=args['--adaptive-greedy-gamma'],
                                                  theta=args['--adaptive-greedy-theta'],
                                                  pool=pool if parallel else None)
    elif args['--alg'] == 'pod':
        rom, red_summary = reduce_pod(fom=fom, reductor=reductor, snapshots_per_block=args['SNAPSHOTS'],
                                      basis_size=args['RBSIZE'])
    else:
        assert False  # this should never happen

    if args['--pickle']:
        print(f"\nWriting reduced model to file {args['--pickle']}_reduced ...")
        with open(args['--pickle'] + '_reduced', 'wb') as f:
            dump(rom, f)
        if not args['--fenics']:  # FEniCS data structures do not support serialization
            print(f"Writing detailed model and reductor to file {args['--pickle']}_detailed ...")
            with open(args['--pickle'] + '_detailed', 'wb') as f:
                dump((fom, reductor), f)

    print('\nSearching for maximum error on random snapshots ...')

    results = reduction_error_analysis(rom,
                                       fom=fom,
                                       reductor=reductor,
                                       estimator=True,
                                       error_norms=(fom.h1_0_semi_norm, fom.l2_norm),
                                       condition=True,
                                       test_mus=args['--test'],
                                       basis_sizes=0 if args['--plot-error-sequence'] else 1,
                                       plot=args['--plot-error-sequence'],
                                       pool=None if args['--fenics'] else pool,  # cannot pickle FEniCS model
                                       random_seed=999)

    print('\n*** RESULTS ***\n')
    print(fom_summary)
    print(red_summary)
    print(results['summary'])
    sys.stdout.flush()

    if args['--plot-error-sequence']:
        import matplotlib.pyplot
        matplotlib.pyplot.show(results['figure'])
    if args['--plot-err']:
        mumax = results['max_error_mus'][0, -1]
        U = fom.solve(mumax)
        URB = reductor.reconstruct(rom.solve(mumax))
        fom.visualize((U, URB, U - URB), legend=('Detailed Solution', 'Reduced Solution', 'Error'),
                    title='Maximum Error Solution', separate_colorbars=True, block=True)

    return results
コード例 #5
0
def main(
    xblocks: int = Argument(..., help='Number of blocks in x direction.'),
    yblocks: int = Argument(..., help='Number of blocks in y direction.'),
    snapshots: int = Argument(
        ...,
        help='naive: ignored\n\n'
        'greedy/pod: Number of training_set parameters per block '
        '(in total SNAPSHOTS^(XBLOCKS * YBLOCKS) parameters).\n\n'
        'adaptive_greedy: size of validation set.\n\n'),
    rbsize: int = Argument(..., help='Size of the reduced basis.'),
    adaptive_greedy_gamma: float = Option(
        0.2, help='See pymor.algorithms.adaptivegreedy.'),
    adaptive_greedy_rho: float = Option(
        1.1, help='See pymor.algorithms.adaptivegreedy.'),
    adaptive_greedy_theta: float = Option(
        0., help='See pymor.algorithms.adaptivegreedy.'),
    alg: Choices('naive greedy adaptive_greedy pod') = Option(
        'greedy', help='The model reduction algorithm to use.'),
    cache_region: Choices('none memory disk persistent') = Option(
        'none',
        help='Name of cache region to use for caching solution snapshots.'),
    extension_alg: Choices('trivial gram_schmidt') = Option(
        'gram_schmidt', help='Basis extension algorithm to be used.'),
    fenics: bool = Option(False, help='Use FEniCS model.'),
    greedy_with_error_estimator: bool = Option(
        True, help='Use error estimator for basis generation.'),
    grid: int = Option(100, help='Use grid with 4*NI*NI elements'),
    ipython_engines: int = Option(
        None,
        help='If positive, the number of IPython cluster engines to use for '
        'parallel greedy search. If zero, no parallelization is performed.'),
    ipython_profile: str = Option(
        None, help='IPython profile to use for parallelization.'),
    list_vector_array: bool = Option(
        False,
        help=
        'Solve using ListVectorArray[NumpyVector] instead of NumpyVectorArray.'
    ),
    order: int = Option(
        1,
        help=
        'Polynomial order of the Lagrange finite elements to use in FEniCS.'),
    pickle: str = Option(
        None,
        help=
        'Pickle reduced model, as well as reductor and high-dimensional model '
        'to files with this prefix.'),
    product: Choices('euclidean h1') = Option(
        'h1',
        help=
        'Product w.r.t. which to orthonormalize and calculate Riesz representatives.'
    ),
    plot_err: bool = Option(False, help='Plot error'),
    plot_error_sequence: bool = Option(
        False, help='Plot reduction error vs. basis size.'),
    plot_solutions: bool = Option(False, help='Plot some example solutions.'),
    reductor: Choices('traditional residual_basis') = Option(
        'residual_basis', help='Reductor (error estimator) to choose.'),
    test: int = Option(
        10, help='Use COUNT snapshots for stochastic error estimation.'),
):
    """Thermalblock demo."""

    if fenics and cache_region != 'none':
        raise ValueError(
            'Caching of high-dimensional solutions is not supported for FEniCS model.'
        )
    if not fenics and order != 1:
        raise ValueError(
            'Higher-order finite elements only supported for FEniCS model.')

    pool = new_parallel_pool(ipython_num_engines=ipython_engines,
                             ipython_profile=ipython_profile)

    if fenics:
        fom, fom_summary = discretize_fenics(xblocks, yblocks, grid, order)
    else:
        fom, fom_summary = discretize_pymor(xblocks, yblocks, grid,
                                            list_vector_array)

    parameter_space = fom.parameters.space(0.1, 1.)

    if cache_region != 'none':
        # building a cache_id is only needed for persistent CacheRegions
        cache_id = (f"pymordemos.thermalblock {fenics} {xblocks} {yblocks}"
                    f"{grid} {order}")
        fom.enable_caching(cache_region.value, cache_id)

    if plot_solutions:
        print('Showing some solutions')
        Us = ()
        legend = ()
        for mu in parameter_space.sample_randomly(2):
            print(f"Solving for diffusion = \n{mu['diffusion']} ... ")
            sys.stdout.flush()
            Us = Us + (fom.solve(mu), )
            legend = legend + (str(mu['diffusion']), )
        fom.visualize(Us,
                      legend=legend,
                      title='Detailed Solutions for different parameters',
                      separate_colorbars=False,
                      block=True)

    print('RB generation ...')

    # define estimator for coercivity constant
    from pymor.parameters.functionals import ExpressionParameterFunctional
    coercivity_estimator = ExpressionParameterFunctional(
        'min(diffusion)', fom.parameters)

    # inner product for computation of Riesz representatives
    product = fom.h1_0_semi_product if product == 'h1' else None

    if reductor == 'residual_basis':
        from pymor.reductors.coercive import CoerciveRBReductor
        reductor = CoerciveRBReductor(
            fom,
            product=product,
            coercivity_estimator=coercivity_estimator,
            check_orthonormality=False)
    elif reductor == 'traditional':
        from pymor.reductors.coercive import SimpleCoerciveRBReductor
        reductor = SimpleCoerciveRBReductor(
            fom,
            product=product,
            coercivity_estimator=coercivity_estimator,
            check_orthonormality=False)
    else:
        assert False  # this should never happen

    if alg == 'naive':
        rom, red_summary = reduce_naive(fom=fom,
                                        reductor=reductor,
                                        parameter_space=parameter_space,
                                        basis_size=rbsize)
    elif alg == 'greedy':
        parallel = greedy_with_error_estimator or not fenics  # cannot pickle FEniCS model
        rom, red_summary = reduce_greedy(
            fom=fom,
            reductor=reductor,
            parameter_space=parameter_space,
            snapshots_per_block=snapshots,
            extension_alg_name=extension_alg.value,
            max_extensions=rbsize,
            use_error_estimator=greedy_with_error_estimator,
            pool=pool if parallel else None)
    elif alg == 'adaptive_greedy':
        parallel = greedy_with_error_estimator or not fenics  # cannot pickle FEniCS model
        rom, red_summary = reduce_adaptive_greedy(
            fom=fom,
            reductor=reductor,
            parameter_space=parameter_space,
            validation_mus=snapshots,
            extension_alg_name=extension_alg.value,
            max_extensions=rbsize,
            use_error_estimator=greedy_with_error_estimator,
            rho=adaptive_greedy_rho,
            gamma=adaptive_greedy_gamma,
            theta=adaptive_greedy_theta,
            pool=pool if parallel else None)
    elif alg == 'pod':
        rom, red_summary = reduce_pod(fom=fom,
                                      reductor=reductor,
                                      parameter_space=parameter_space,
                                      snapshots_per_block=snapshots,
                                      basis_size=rbsize)
    else:
        assert False  # this should never happen

    if pickle:
        print(f"\nWriting reduced model to file {pickle}_reduced ...")
        with open(pickle + '_reduced', 'wb') as f:
            dump((rom, parameter_space), f)
        if not fenics:  # FEniCS data structures do not support serialization
            print(
                f"Writing detailed model and reductor to file {pickle}_detailed ..."
            )
            with open(pickle + '_detailed', 'wb') as f:
                dump((fom, reductor), f)

    print('\nSearching for maximum error on random snapshots ...')

    results = reduction_error_analysis(
        rom,
        fom=fom,
        reductor=reductor,
        error_estimator=True,
        error_norms=(fom.h1_0_semi_norm, fom.l2_norm),
        condition=True,
        test_mus=parameter_space.sample_randomly(test, seed=999),
        basis_sizes=0 if plot_error_sequence else 1,
        plot=plot_error_sequence,
        pool=None if fenics else pool  # cannot pickle FEniCS model
    )

    print('\n*** RESULTS ***\n')
    print(fom_summary)
    print(red_summary)
    print(results['summary'])
    sys.stdout.flush()

    if plot_error_sequence:
        import matplotlib.pyplot
        matplotlib.pyplot.show()
    if plot_err:
        mumax = results['max_error_mus'][0, -1]
        U = fom.solve(mumax)
        URB = reductor.reconstruct(rom.solve(mumax))
        fom.visualize(
            (U, URB, U - URB),
            legend=('Detailed Solution', 'Reduced Solution', 'Error'),
            title='Maximum Error Solution',
            separate_colorbars=True,
            block=True)

    global test_results
    test_results = results
コード例 #6
0
ファイル: thermalblock_adaptive.py プロジェクト: meretp/pymor
def main(
    rbsize: int = Argument(..., help='Size of the reduced basis.'),

    cache_region: Choices('none memory disk persistent') = Option(
        'none',
        help='Name of cache region to use for caching solution snapshots.'
    ),
    error_estimator: bool = Option(True, help='Use error estimator for basis generation.'),
    gamma: float = Option(0.2, help='Weight factor for age penalty term in refinement indicators.'),
    grid: int = Option(100, help='Use grid with 2*NI*NI elements.'),
    ipython_engines: int = Option(
        0,
        help='If positive, the number of IPython cluster engines to use for parallel greedy search. '
             'If zero, no parallelization is performed.'
    ),
    ipython_profile: str = Option(None, help='IPython profile to use for parallelization.'),
    list_vector_array: bool = Option(
        False,
        help='Solve using ListVectorArray[NumpyVector] instead of NumpyVectorArray.'
    ),
    pickle: str = Option(
        None,
        help='Pickle reduced discretization, as well as reductor and high-dimensional model to files with this prefix.'
    ),
    plot_err: bool = Option(False, help='Plot error.'),
    plot_solutions: bool = Option(False, help='Plot some example solutions.'),
    plot_error_sequence: bool = Option(False, help='Plot reduction error vs. basis size.'),
    product: Choices('euclidean h1') = Option(
        'h1',
        help='Product  w.r.t. which to orthonormalize and calculate Riesz representatives.'
    ),
    reductor: Choices('traditional residual_basis') = Option(
        'residual_basis',
        help='Reductor (error estimator) to choose (traditional, residual_basis).'
    ),
    rho: float = Option(1.1, help='Maximum allowed ratio between error on validation set and on training set.'),
    test: int = Option(10, help='Use COUNT snapshots for stochastic error estimation.'),
    theta: float = Option(0., help='Ratio of elements to refine.'),
    validation_mus: int = Option(0, help='Size of validation set.'),
    visualize_refinement: bool = Option(True, help='Visualize the training set refinement indicators.'),
):
    """Modified thermalblock demo using adaptive greedy basis generation algorithm."""

    problem = thermal_block_problem(num_blocks=(2, 2))
    functionals = [ExpressionParameterFunctional('diffusion[0]', {'diffusion': 2}),
                   ExpressionParameterFunctional('diffusion[1]**2', {'diffusion': 2}),
                   ExpressionParameterFunctional('diffusion[0]', {'diffusion': 2}),
                   ExpressionParameterFunctional('diffusion[1]', {'diffusion': 2})]
    problem = problem.with_(
        diffusion=problem.diffusion.with_(coefficients=functionals),
    )

    print('Discretize ...')
    fom, _ = discretize_stationary_cg(problem, diameter=1. / grid)

    if list_vector_array:
        from pymor.discretizers.builtin.list import convert_to_numpy_list_vector_array
        fom = convert_to_numpy_list_vector_array(fom)

    if cache_region != 'none':
        # building a cache_id is only needed for persistent CacheRegions
        cache_id = f"pymordemos.thermalblock_adaptive {grid}"
        fom.enable_caching(cache_region.value, cache_id)

    if plot_solutions:
        print('Showing some solutions')
        Us = ()
        legend = ()
        for mu in problem.parameter_space.sample_randomly(2):
            print(f"Solving for diffusion = \n{mu['diffusion']} ... ")
            sys.stdout.flush()
            Us = Us + (fom.solve(mu),)
            legend = legend + (str(mu['diffusion']),)
        fom.visualize(Us, legend=legend, title='Detailed Solutions for different parameters', block=True)

    print('RB generation ...')

    product_op = fom.h1_0_semi_product if product == 'h1' else None
    coercivity_estimator = ExpressionParameterFunctional('min([diffusion[0], diffusion[1]**2])',
                                                         fom.parameters)
    reductors = {'residual_basis': CoerciveRBReductor(fom, product=product_op,
                                                      coercivity_estimator=coercivity_estimator),
                 'traditional': SimpleCoerciveRBReductor(fom, product=product_op,
                                                         coercivity_estimator=coercivity_estimator)}
    reductor = reductors[reductor]

    pool = new_parallel_pool(ipython_num_engines=ipython_engines, ipython_profile=ipython_profile)
    greedy_data = rb_adaptive_greedy(
        fom, reductor, problem.parameter_space,
        validation_mus=validation_mus,
        rho=rho,
        gamma=gamma,
        theta=theta,
        use_error_estimator=error_estimator,
        error_norm=fom.h1_0_semi_norm,
        max_extensions=rbsize,
        visualize=visualize_refinement
    )

    rom = greedy_data['rom']

    if pickle:
        print(f"\nWriting reduced model to file {pickle}_reduced ...")
        with open(pickle + '_reduced', 'wb') as f:
            dump(rom, f)
        print(f"Writing detailed model and reductor to file {pickle}_detailed ...")
        with open(pickle + '_detailed', 'wb') as f:
            dump((fom, reductor), f)

    print('\nSearching for maximum error on random snapshots ...')

    results = reduction_error_analysis(rom,
                                       fom=fom,
                                       reductor=reductor,
                                       error_estimator=True,
                                       error_norms=(fom.h1_0_semi_norm,),
                                       condition=True,
                                       test_mus=problem.parameter_space.sample_randomly(test),
                                       basis_sizes=25 if plot_error_sequence else 1,
                                       plot=True,
                                       pool=pool)

    real_rb_size = rom.solution_space.dim

    print('''
*** RESULTS ***

Problem:
   number of blocks:                   2x2
   h:                                  sqrt(2)/{grid}

Greedy basis generation:
   error estimator enalbed:            {error_estimator}
   product:                            {product}
   prescribed basis size:              {rbsize}
   actual basis size:                  {real_rb_size}
   elapsed time:                       {greedy_data[time]}
'''.format(**locals()))
    print(results['summary'])

    sys.stdout.flush()

    if plot_error_sequence:
        from matplotlib import pyplot as plt
        plt.show()
    if plot_err:
        mumax = results['max_error_mus'][0, -1]
        U = fom.solve(mumax)
        URB = reductor.reconstruct(rom.solve(mumax))
        fom.visualize((U, URB, U - URB), legend=('Detailed Solution', 'Reduced Solution', 'Error'),
                      title='Maximum Error Solution', separate_colorbars=True, block=True)