Dear all,

*Background*: I have a BlockVector<double> *u* that needs to be updated via 
a series of rank-2 update to get BlockVector<double> *v*. For notation 
convenience, let's call this series of rank-2 update as *P*. I can define *P 
*as either a LinearOperator or BlockLinearOperator, since I need to solve a 
linear system later that involves *P* using an iterative solver (for 
example, conjugate-gradient method). Defining *P *as a BlockLinearOperator 
does not work in this case, since I only know how to perform the rank-2 
updates on *u* but not the block structure of the operator *P*. 

*Solution*: My solution is to define *P* as a LinearOperator that operates 
on the source BlockVector *u* to get the destination BlockVector *v*. I 
know this is unconventional, because LinearOperator usually operates on 
Vector but not BlockVector. But theoretically, a LinearOperator should 
still be able to operator on a BlockVector to produce another BlockVector.

*Issue*: I can define the needed LinearOperator *P1 *and* P2 *that operate 
on a BlockVector without a problem. I can call *P1*.vmult(*v*, *u*) and *P2*
.vmult(*v*, *u*) correctly. However, when I define a composite operator 
such as (for demonstration purpose)
*auto total_op = P1 * P2; *
and then call
*total_op.vmult(v, u);*
I get the following error message:
"""
--------------------------------------------------------
An error occurred in line <1487> of file 
</home/taojin/dealii-9.4.0/bin/include/deal.II/lac/block_vector_base.h> in 
function
    dealii::BlockVectorBase<VectorType>::BlockType& 
dealii::BlockVectorBase<VectorType>::block(unsigned int) [with VectorType = 
dealii::Vector<double>; dealii::BlockVectorBase<VectorType>::BlockType = 
dealii::Vector<double>]
The violated condition was: 
    ::dealii::deal_II_exceptions::internals::compare_less_than(i, 
n_blocks())
Additional information: 
    Index 0 is not in the half-open range [0,0). In the current case, this
    half-open range is in fact empty, suggesting that you are accessing an
    element of an empty collection such as a vector that has not been set
    to the correct size.
"""

*Fix*: This error message is due to the definition of operator* (Line 582 
in linear_operator.h)
template <typename Range,
          typename Intermediate,
          typename Domain,
          typename Payload>
LinearOperator<Range, Domain, Payload>
operator*(const LinearOperator<Range, Intermediate, Payload> & first_op,
          const LinearOperator<Intermediate, Domain, Payload> &second_op)
{
......
      return_op.vmult = [first_op, second_op](Range &v, const Domain &u) {
        GrowingVectorMemory<Intermediate> vector_memory;

        typename VectorMemory<*Intermediate*>::Pointer i(vector_memory);
        second_op.reinit_range_vector(*i, /*bool omit_zeroing_entries 
=*/true);
        second_op.vmult(*i, u);
        first_op.vmult(v, *i);
......
}
It looks like that *Intermediate* only has the correct memory size but not 
the block information. My fix is to add one line of code 

*        (*i).reinit(u.n_blocks());*
in my own definition operator*(). This fixes the error and works correctly.

*Questions*:
1. Does it even make sense to define a LinearOperator operating on a 
BlockVector? If yes, should we expand the dealii capability to consider 
this option?
2. For my rank-2 update operation *P*, I cannot define it as a 
BlockLinearOperator. Even though *P *operates on a BlockVector, but  *P *itself 
does not possess blocks. Any thoughts?
3. Another solution is to convert BlockVector *u* to a Vector and define  *P 
*as a LinearOperator operating on Vector. After the rank-2 update, convert 
the Vector back to a BlockVector. However, it seems rather awkward. Does 
the community have better ideas?

I have attached my code and a test case here.
Best,

Tao


-- 
The deal.II project is located at http://www.dealii.org/
For mailing list/forum options, see 
https://groups.google.com/d/forum/dealii?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"deal.II User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/dealii/4f0dfa54-cec3-4725-ae17-a2ef155e94a3n%40googlegroups.com.
/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2019 - 2021 by the deal.II authors
 *
 * This file is part of the deal.II library.
 *
 * The deal.II library is free software; you can use it, redistribute
 * it, and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * The full text of the license can be found in the file LICENSE.md at
 * the top level directory of deal.II.
 *
 * ---------------------------------------------------------------------

 * This tutorial program was contributed by Martin Kronbichler
 */


#include <deal.II/lac/linear_operator.h>
#include <deal.II/lac/vector_memory.templates.h>
#include <deal.II/lac/linear_operator_tools.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/block_vector.h>
#include <deal.II/lac/block_linear_operator.h>

using namespace dealii;

// define my own operator*() to make the following line work:
// const auto total_op = op_1 * op_2;
// total_op.vmult(v, u);
// where v and u are BlockVector, and op_1 and op_2 are LinearOperator that operates on BlockVector

LinearOperator<BlockVector<double>, BlockVector<double>, dealii::internal::LinearOperatorImplementation::EmptyPayload>
operator*(const LinearOperator<BlockVector<double>, BlockVector<double>, dealii::internal::LinearOperatorImplementation::EmptyPayload> & first_op,
          const LinearOperator<BlockVector<double>, BlockVector<double>, dealii::internal::LinearOperatorImplementation::EmptyPayload> &second_op)
{
  std::cout << "calling usr-defined operator*() function" << std::endl;

  if (first_op.is_null_operator || second_op.is_null_operator)
    {
      LinearOperator<BlockVector<double>, BlockVector<double>, dealii::internal::LinearOperatorImplementation::EmptyPayload> return_op;
      return_op.reinit_domain_vector = second_op.reinit_domain_vector;
      return_op.reinit_range_vector  = first_op.reinit_range_vector;
      return null_operator(return_op);
    }
  else
    {
      LinearOperator<BlockVector<double>, BlockVector<double>, dealii::internal::LinearOperatorImplementation::EmptyPayload> return_op{
        static_cast<const dealii::internal::LinearOperatorImplementation::EmptyPayload &>(first_op) *
        static_cast<const dealii::internal::LinearOperatorImplementation::EmptyPayload &>(second_op)};

      return_op.reinit_domain_vector = second_op.reinit_domain_vector;
      return_op.reinit_range_vector  = first_op.reinit_range_vector;

      // ensure to have valid computation objects by catching first_op and
      // second_op by value

      return_op.vmult = [first_op, second_op](BlockVector<double> &v, const BlockVector<double> &u) {
        GrowingVectorMemory<BlockVector<double>> vector_memory;

        typename VectorMemory<BlockVector<double>>::Pointer i(vector_memory);

        (*i).reinit(u.n_blocks());

        second_op.reinit_range_vector(*i, /*bool omit_zeroing_entries =*/true);
        second_op.vmult(*i, u);
        first_op.vmult(v, *i);
      };

      return_op.vmult_add = [first_op, second_op](BlockVector<double> &v, const BlockVector<double> &u) {
        GrowingVectorMemory<BlockVector<double>> vector_memory;

        typename VectorMemory<BlockVector<double>>::Pointer i(vector_memory);

        (*i).reinit(u.n_blocks());

        second_op.reinit_range_vector(*i, /*bool omit_zeroing_entries =*/true);
        second_op.vmult(*i, u);
        first_op.vmult_add(v, *i);
      };

      return_op.Tvmult = [first_op, second_op](BlockVector<double> &v, const BlockVector<double> &u) {
        GrowingVectorMemory<BlockVector<double>> vector_memory;

        typename VectorMemory<BlockVector<double>>::Pointer i(vector_memory);

        (*i).reinit(u.n_blocks());

        first_op.reinit_domain_vector(*i, /*bool omit_zeroing_entries =*/true);
        first_op.Tvmult(*i, u);
        second_op.Tvmult(v, *i);
      };

      return_op.Tvmult_add = [first_op, second_op](BlockVector<double> &v, const BlockVector<double> &u) {
        GrowingVectorMemory<BlockVector<double>> vector_memory;

        typename VectorMemory<BlockVector<double>>::Pointer i(vector_memory);

        (*i).reinit(u.n_blocks());

        first_op.reinit_domain_vector(*i, /*bool omit_zeroing_entries =*/true);
        first_op.Tvmult(*i, u);
        second_op.Tvmult_add(v, *i);
      };

      return return_op;
    }
}


int
main()
{
  using namespace dealii;

  // LinearOperator
  using Payload = dealii::internal::LinearOperatorImplementation::EmptyPayload;
  LinearOperator<BlockVector<double>, BlockVector<double>, Payload> usr_linearOperator_1;

  // For demonstration purpose, define LinearOperator using two FullMatrix
  FullMatrix<double> usr_matrix_1(3, 3);
  usr_matrix_1(0,0) = 1;
  usr_matrix_1(0,1) = 2;
  usr_matrix_1(0,2) = 3;

  usr_matrix_1(1,0) = 4;
  usr_matrix_1(1,1) = 5;
  usr_matrix_1(1,2) = 6;

  usr_matrix_1(2,0) = 7;
  usr_matrix_1(2,1) = 8;
  usr_matrix_1(2,2) = 9;

  FullMatrix<double> usr_matrix_2(2, 2);
  usr_matrix_2(0,0) = -1;
  usr_matrix_2(0,1) = -2;

  usr_matrix_2(1,0) = -3;
  usr_matrix_2(1,1) = -4;

  usr_linearOperator_1.vmult = [& usr_matrix_1, & usr_matrix_2](BlockVector<double> &v, const BlockVector<double> &u) {
    usr_matrix_1.vmult(v.block(0), u.block(0));
    usr_matrix_2.vmult(v.block(1), u.block(1));
  };

  usr_linearOperator_1.vmult_add = [&usr_linearOperator_1](BlockVector<double> &v, const BlockVector<double> &u) {
    BlockVector<double> temp_v(u);
    usr_linearOperator_1.vmult(temp_v, u);
    v += temp_v;
  };

  usr_linearOperator_1.Tvmult = [& usr_matrix_1, & usr_matrix_2](BlockVector<double> &v, const BlockVector<double> &u) {
    usr_matrix_1.Tvmult(v.block(0), u.block(0));
    usr_matrix_2.Tvmult(v.block(1), u.block(1));
  };

  usr_linearOperator_1.Tvmult_add = [usr_linearOperator_1](BlockVector<double> &v, const BlockVector<double> &u) {
    BlockVector<double> temp_v(u);
    usr_linearOperator_1.Tvmult(temp_v, u);
    v += temp_v;
  };

  usr_linearOperator_1.reinit_range_vector = [& usr_matrix_1, & usr_matrix_2](BlockVector<double> &v, bool omit_zeroing_entries) {
    (v.block(0)).reinit(usr_matrix_1.m(), omit_zeroing_entries);
    (v.block(1)).reinit(usr_matrix_2.m(), omit_zeroing_entries);
  };

  usr_linearOperator_1.reinit_domain_vector = [& usr_matrix_1, & usr_matrix_2](BlockVector<double> &v, bool omit_zeroing_entries) {
    (v.block(0)).reinit(usr_matrix_1.n(), omit_zeroing_entries);
    (v.block(1)).reinit(usr_matrix_2.n(), omit_zeroing_entries);
  };

  LinearOperator<BlockVector<double>, BlockVector<double>, Payload> usr_linearOperator_2;
  FullMatrix<double> usr_matrix_3(3, 3);
  usr_matrix_3(0,0) = 11;
  usr_matrix_3(0,1) = 21;
  usr_matrix_3(0,2) = 31;

  usr_matrix_3(1,0) = 41;
  usr_matrix_3(1,1) = 51;
  usr_matrix_3(1,2) = 61;

  usr_matrix_3(2,0) = 71;
  usr_matrix_3(2,1) = 81;
  usr_matrix_3(2,2) = 91;

  FullMatrix<double> usr_matrix_4(2, 2);
  usr_matrix_4(0,0) = -11;
  usr_matrix_4(0,1) = -21;

  usr_matrix_4(1,0) = -31;
  usr_matrix_4(1,1) = -41;

  usr_linearOperator_2.vmult = [& usr_matrix_3, & usr_matrix_4](BlockVector<double> &v, const BlockVector<double> &u) {
    usr_matrix_3.vmult(v.block(0), u.block(0));
    usr_matrix_4.vmult(v.block(1), u.block(1));
  };

  usr_linearOperator_2.vmult_add = [&usr_linearOperator_2](BlockVector<double> &v, const BlockVector<double> &u) {
    BlockVector<double> temp_v(u);
    usr_linearOperator_2.vmult(temp_v, u);
    v += temp_v;
  };

  usr_linearOperator_2.Tvmult = [& usr_matrix_3, & usr_matrix_4](BlockVector<double> &v, const BlockVector<double> &u) {
    usr_matrix_3.Tvmult(v.block(0), u.block(0));
    usr_matrix_4.Tvmult(v.block(1), u.block(1));
  };

  usr_linearOperator_2.Tvmult_add = [usr_linearOperator_2](BlockVector<double> &v, const BlockVector<double> &u) {
    BlockVector<double> temp_v(u);
    usr_linearOperator_2.Tvmult(temp_v, u);
    v += temp_v;
  };

  usr_linearOperator_2.reinit_range_vector = [& usr_matrix_3, & usr_matrix_4](BlockVector<double> &v, bool omit_zeroing_entries) {
    (v.block(0)).reinit(usr_matrix_3.m(), omit_zeroing_entries);
    (v.block(1)).reinit(usr_matrix_4.m(), omit_zeroing_entries);
  };

  usr_linearOperator_2.reinit_domain_vector = [& usr_matrix_3, & usr_matrix_4](BlockVector<double> &v, bool omit_zeroing_entries) {
    (v.block(0)).reinit(usr_matrix_3.n(), omit_zeroing_entries);
    (v.block(1)).reinit(usr_matrix_4.n(), omit_zeroing_entries);
  };

  std::vector<types::global_dof_index> block_vector_sizes(2);
  block_vector_sizes[0] = 3;
  block_vector_sizes[1] = 2;

  BlockVector<double> u(block_vector_sizes);
  u.block(0)[0] = 1;
  u.block(0)[1] = 2;
  u.block(0)[2] = 3;

  u.block(1)[0] = -1;
  u.block(1)[1] = -2;

  BlockVector<double> v(block_vector_sizes);

  auto op_total = usr_linearOperator_1 * usr_linearOperator_2;

  op_total.vmult(v, u);

  std::cout << "usr_linearOperator_1 * usr_linearOperator_2 =>" << std::endl;
  std::cout << "v.block(0)[0] = " << v.block(0)[0] << std::endl;
  std::cout << "v.block(0)[1] = " << v.block(0)[1] << std::endl;
  std::cout << "v.block(0)[2] = " << v.block(0)[2] << std::endl;

  std::cout << "v.block(1)[0] = " << v.block(1)[0] << std::endl;
  std::cout << "v.block(1)[1] = " << v.block(1)[1] << std::endl;

  op_total = usr_linearOperator_2 * usr_linearOperator_1;

  op_total.vmult(v, u);

  std::cout << "usr_linearOperator_2 * usr_linearOperator_1 =>" << std::endl;
  std::cout << "v.block(0)[0] = " << v.block(0)[0] << std::endl;
  std::cout << "v.block(0)[1] = " << v.block(0)[1] << std::endl;
  std::cout << "v.block(0)[2] = " << v.block(0)[2] << std::endl;

  std::cout << "v.block(1)[0] = " << v.block(1)[0] << std::endl;
  std::cout << "v.block(1)[1] = " << v.block(1)[1] << std::endl;

  return 0;
}

Reply via email to