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