Files
mfem/linalg/blockmatrix.cpp
T
Veselin Dobrev 40378a046b Introduce a new Memory class for handling host + device allocations
and transfers.

The Memory class is now used by some MFEM classes (like Array and
Vector) which can be used on the Device. Such classes now provide
methods to access the underlying Memory object, e.g. GetMemory.

Updated ex1/ex1p and ex6/ex6p to not need to enable/disable the
Device at specific points -- the Device is now enabled just at the
start. Also, the same examples can now run on Device (e.g. -d cuda)
without the partial assembly option (-pa) -- full assembly will
be still done on CPU but the sparse matrix action and vector
operations will be done using the Device.

Reverted changes in class DenseMatrix related to using the Device.
At this point, DenseMatrix operations are only used for small matrices
and using the Device in this case is not a good option.
2019-05-16 14:53:39 -07:00

636 lines
17 KiB
C++

// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "../general/array.hpp"
#include "../general/globals.hpp"
#include "matrix.hpp"
#include "sparsemat.hpp"
#include "blockvector.hpp"
#include "blockmatrix.hpp"
namespace mfem
{
BlockMatrix::BlockMatrix(const Array<int> & offsets):
AbstractSparseMatrix(offsets.Last()),
owns_blocks(false),
nRowBlocks(offsets.Size()-1),
nColBlocks(offsets.Size()-1),
row_offsets(const_cast< Array<int>& >(offsets).GetData(), offsets.Size()),
col_offsets(const_cast< Array<int>& >(offsets).GetData(), offsets.Size()),
Aij(nRowBlocks, nColBlocks)
{
Aij = (SparseMatrix *)NULL;
}
BlockMatrix::BlockMatrix(const Array<int> & row_offsets_,
const Array<int> & col_offsets_):
AbstractSparseMatrix(row_offsets_.Last(), col_offsets_.Last()),
owns_blocks(false),
nRowBlocks(row_offsets_.Size()-1),
nColBlocks(col_offsets_.Size()-1),
row_offsets(const_cast< Array<int>& >(row_offsets_).GetData(),
row_offsets_.Size()),
col_offsets(const_cast< Array<int>& >(col_offsets_).GetData(),
col_offsets_.Size()),
Aij(nRowBlocks, nColBlocks)
{
Aij = (SparseMatrix *)NULL;
}
BlockMatrix::~BlockMatrix()
{
if (owns_blocks)
{
for (SparseMatrix ** it = Aij.GetRow(0);
it != Aij.GetRow(0)+(Aij.NumRows()*Aij.NumCols()); ++it)
{
delete *it;
}
}
}
void BlockMatrix::SetBlock(int i, int j, SparseMatrix * mat)
{
#ifdef MFEM_DEBUG
if (nRowBlocks <= i || nColBlocks <= j)
{
mfem_error("BlockMatrix::SetBlock #0");
}
if (mat->Height() != row_offsets[i+1] - row_offsets[i])
{
mfem_error("BlockMatrix::SetBlock #1");
}
if (mat->Width() != col_offsets[j+1] - col_offsets[j])
{
mfem_error("BlockMatrix::SetBlock #2");
}
#endif
Aij(i,j) = mat;
}
SparseMatrix & BlockMatrix::GetBlock(int i, int j)
{
#ifdef MFEM_DEBUG
if (nRowBlocks <= i || nColBlocks <= j)
{
mfem_error("BlockMatrix::Block #0");
}
if (IsZeroBlock(i,j))
{
mfem_error("BlockMatrix::Block #1");
}
#endif
return *Aij(i,j);
}
const SparseMatrix & BlockMatrix::GetBlock(int i, int j) const
{
#ifdef MFEM_DEBUG
if (nRowBlocks <= i || nColBlocks <= j)
{
mfem_error("BlockMatrix::Block const #0");
}
if (IsZeroBlock(i,j))
{
mfem_error("BlockMatrix::Block const #1");
}
#endif
return *Aij(i,j);
}
int BlockMatrix::NumNonZeroElems() const
{
int nnz_elem = 0;
for (int jcol = 0; jcol != nColBlocks; ++jcol)
{
for (int irow = 0; irow != nRowBlocks; ++irow)
{
if (Aij(irow,jcol))
{
nnz_elem+= Aij(irow,jcol)->NumNonZeroElems();
}
}
}
return nnz_elem;
}
double& BlockMatrix::Elem (int i, int j)
{
int iloc, iblock;
int jloc, jblock;
findGlobalRow(i, iblock, iloc);
findGlobalCol(j, jblock, jloc);
if (IsZeroBlock(i, j))
{
mfem_error("BlockMatrix::Elem");
}
return Aij(iblock, jblock)->Elem(iloc, jloc);
}
const double& BlockMatrix::Elem (int i, int j) const
{
int iloc, iblock;
int jloc, jblock;
findGlobalRow(i, iblock, iloc);
findGlobalCol(j, jblock, jloc);
if (IsZeroBlock(i, j))
{
mfem_error("BlockMatrix::Elem");
}
return Aij(iblock, jblock)->Elem(iloc, jloc);
}
int BlockMatrix::RowSize(const int i) const
{
int rowsize = 0;
int iblock, iloc;
findGlobalRow(i, iblock, iloc);
for (int jblock = 0; jblock < nColBlocks; ++jblock)
{
if (Aij(iblock,jblock) != NULL)
{
rowsize += Aij(iblock,jblock)->RowSize(iloc);
}
}
return rowsize;
}
int BlockMatrix::GetRow(const int row, Array<int> &cols, Vector &srow) const
{
int iblock, iloc, rowsize;
findGlobalRow(row, iblock, iloc);
rowsize = RowSize(row);
cols.SetSize(rowsize);
srow.SetSize(rowsize);
Array<int> bcols;
Vector bsrow;
int * it_cols = cols.GetData();
double *it_srow = srow.GetData();
for (int jblock = 0; jblock < nColBlocks; ++jblock)
{
if (Aij(iblock,jblock) != NULL)
{
Aij(iblock,jblock)->GetRow(iloc, bcols, bsrow);
for (int i = 0; i < bcols.Size(); ++i)
{
*(it_cols++) = bcols[i] + col_offsets[jblock];
*(it_srow++) = bsrow(i);
}
}
}
return 0;
}
void BlockMatrix::EliminateRowCol(int rc, DiagonalPolicy dpolicy)
{
// Find the block to which the dof belongs and its local number
int idx, iiblock;
for (iiblock = 0; iiblock < nRowBlocks; ++iiblock)
{
idx = rc - row_offsets[iiblock];
if (idx < 0 ) { break; }
}
iiblock--;
idx = rc - row_offsets[iiblock];
// Asserts
MFEM_ASSERT(nRowBlocks == nColBlocks,
"BlockMatrix::EliminateRowCol: nRowBlocks != nColBlocks");
MFEM_ASSERT(row_offsets[iiblock] == col_offsets[iiblock],
"BlockMatrix::EliminateRowCol: row_offests["
<< iiblock << "] != col_offsets["<<iiblock<<"]");
MFEM_ASSERT(Aij(iiblock, iiblock),
"BlockMatrix::EliminateRowCol: Null diagonal block");
// Apply the constraint idx to the iiblock
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
{
if (iiblock == jjblock) { continue; }
if (Aij(iiblock,jjblock)) { Aij(iiblock,jjblock)->EliminateRow(idx); }
}
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
{
if (iiblock == jjblock) { continue; }
if (Aij(jjblock,iiblock)) { Aij(jjblock,iiblock)->EliminateCol(idx); }
}
Aij(iiblock, iiblock)->EliminateRowCol(idx,dpolicy);
}
void BlockMatrix::EliminateRowCol(Array<int> & ess_bc_dofs, Vector & sol,
Vector & rhs)
{
if (nRowBlocks != nColBlocks)
{
mfem_error("BlockMatrix::EliminateRowCol: nRowBlocks != nColBlocks");
}
for (int iiblock = 0; iiblock < nRowBlocks; ++iiblock)
{
if (row_offsets[iiblock] != col_offsets[iiblock])
{
mfem::out << "BlockMatrix::EliminateRowCol: row_offests["
<< iiblock << "] != col_offsets["<<iiblock<<"]\n";
mfem_error();
}
}
// We also have to do the same for each Aij
Array<int> block_dofs;
Vector block_sol, block_rhs;
for (int iiblock = 0; iiblock < nRowBlocks; ++iiblock)
{
int dsize = row_offsets[iiblock+1] - row_offsets[iiblock];
block_dofs.MakeRef(ess_bc_dofs.GetData()+row_offsets[iiblock], dsize);
block_sol.SetDataAndSize(sol.GetData()+row_offsets[iiblock], dsize);
block_rhs.SetDataAndSize(rhs.GetData()+row_offsets[iiblock], dsize);
if (Aij(iiblock, iiblock))
{
for (int i = 0; i < block_dofs.Size(); ++i)
{
if (block_dofs[i])
{
Aij(iiblock, iiblock)->EliminateRowCol(i,block_sol(i), block_rhs);
}
}
}
else
{
for (int i = 0; i < block_dofs.Size(); ++i)
{
if (block_dofs[i])
{
mfem_error("BlockMatrix::EliminateRowCol: Null diagonal block \n");
}
}
}
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
{
if (jjblock != iiblock && Aij(iiblock, jjblock))
{
for (int i = 0; i < block_dofs.Size(); ++i)
{
if (block_dofs[i])
{
Aij(iiblock, jjblock)->EliminateRow(i);
}
}
}
if (jjblock != iiblock && Aij(jjblock, iiblock))
{
block_rhs.SetDataAndSize(rhs.GetData()+row_offsets[jjblock],
row_offsets[jjblock+1] - row_offsets[jjblock]);
Aij(jjblock, iiblock)->EliminateCols(block_dofs, &block_sol, &block_rhs);
}
}
}
}
void BlockMatrix::EliminateZeroRows(const double threshold)
{
MFEM_VERIFY(nRowBlocks == nColBlocks, "not a square matrix");
for (int iblock = 0; iblock < nRowBlocks; ++iblock)
{
if (Aij(iblock,iblock))
{
double norm;
for (int i = 0; i < Aij(iblock, iblock)->NumRows(); ++i)
{
norm = 0.;
for (int jblock = 0; jblock < nColBlocks; ++jblock)
if (Aij(iblock,jblock))
{
norm += Aij(iblock,jblock)->GetRowNorml1(i);
}
if (norm <= threshold)
{
for (int jblock = 0; jblock < nColBlocks; ++jblock)
{
if (Aij(iblock,jblock))
{
Aij(iblock,jblock)->EliminateRow(
i, (iblock==jblock) ? DIAG_ONE : DIAG_ZERO);
}
}
}
}
}
else
{
double norm;
for (int i = 0; i < row_offsets[iblock+1] - row_offsets[iblock]; ++i)
{
norm = 0.;
for (int jblock = 0; jblock < nColBlocks; ++jblock)
{
if (Aij(iblock,jblock))
{
norm += Aij(iblock,jblock)->GetRowNorml1(i);
}
}
MFEM_VERIFY(!(norm <= threshold), "diagonal block is NULL:"
" iblock = " << iblock << ", i = " << i << ", norm = "
<< norm);
}
}
}
}
void BlockMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
{
for (int iblock = 0; iblock < nRowBlocks; ++iblock)
{
for (int jblock = 0; jblock < nColBlocks; ++jblock)
{
if (!Aij(iblock,jblock)) { continue; }
if (!Aij(iblock,jblock)->Finalized())
{
Aij(iblock,jblock)->Finalize(skip_zeros, fix_empty_rows);
}
}
}
}
void BlockMatrix::Mult(const Vector & x, Vector & y) const
{
if (x.GetData() == y.GetData())
{
mfem_error("Error: x and y can't point to the same datas \n");
}
MFEM_ASSERT(width == x.Size(), "Input vector size (" << x.Size()
<< ") must match matrix width (" << width << ")");
MFEM_ASSERT(height == y.Size(), "Output vector size (" << y.Size()
<< ") must match matrix height (" << height << ")");
y = 0.;
AddMult(x, y, 1.0);
}
void BlockMatrix::AddMult(const Vector & x, Vector & y, const double val) const
{
if (x.GetData() == y.GetData())
{
mfem_error("Error: x and y can't point to the same datas \n");
}
Vector xblockview, yblockview;
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
{
yblockview.SetDataAndSize(y.GetData() + row_offsets[iblock],
row_offsets[iblock+1] - row_offsets[iblock]);
for (int jblock = 0; jblock != nColBlocks; ++jblock)
{
if (Aij(iblock, jblock) != NULL)
{
xblockview.SetDataAndSize(
x.GetData() + col_offsets[jblock],
col_offsets[jblock+1] - col_offsets[jblock]);
Aij(iblock, jblock)->AddMult(xblockview, yblockview, val);
}
}
}
}
void BlockMatrix::MultTranspose(const Vector & x, Vector & y) const
{
if (x.GetData() == y.GetData())
{
mfem_error("Error: x and y can't point to the same datas \n");
}
y = 0.;
AddMultTranspose(x, y, 1.0);
}
void BlockMatrix::AddMultTranspose(const Vector & x, Vector & y,
const double val) const
{
if (x.GetData() == y.GetData())
{
mfem_error("Error: x and y can't point to the same datas \n");
}
Vector xblockview, yblockview;
for (int iblock = 0; iblock != nColBlocks; ++iblock)
{
yblockview.SetDataAndSize(y.GetData() + col_offsets[iblock],
col_offsets[iblock+1] - col_offsets[iblock]);
for (int jblock = 0; jblock != nRowBlocks; ++jblock)
{
if (Aij(jblock, iblock) != NULL)
{
xblockview.SetDataAndSize(
x.GetData() + row_offsets[jblock],
row_offsets[jblock+1] - row_offsets[jblock]);
Aij(jblock, iblock)->AddMultTranspose(xblockview, yblockview, val);
}
}
}
}
SparseMatrix * BlockMatrix::CreateMonolithic() const
{
int nnz = NumNonZeroElems();
int * i_amono = new int[row_offsets[nRowBlocks]+2];
int * j_amono = new int[nnz];
double * data = new double[nnz];
for (int i = 0; i < row_offsets[nRowBlocks]+2; i++)
{
i_amono[i] = 0;
}
int * i_amono_construction = i_amono+1;
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
{
for (int irow(row_offsets[iblock]); irow < row_offsets[iblock+1]; ++irow)
{
int local_row = irow - row_offsets[iblock];
int ind = i_amono_construction[irow];
for (int jblock = 0; jblock < nColBlocks; ++jblock)
{
if (Aij(iblock,jblock) != NULL)
ind += Aij(iblock, jblock)->GetI()[local_row+1]
- Aij(iblock, jblock)->GetI()[local_row];
}
i_amono_construction[irow+1] = ind;
}
}
// Fill in the jarray and copy the data
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
{
for (int jblock = 0; jblock != nColBlocks; ++jblock)
{
if (Aij(iblock,jblock) != NULL)
{
int nrow = row_offsets[iblock+1]-row_offsets[iblock];
int * i_aij = Aij(iblock, jblock)->GetI();
int * j_aij = Aij(iblock, jblock)->GetJ();
double * data_aij = Aij(iblock, jblock)->GetData();
int *i_it = i_amono_construction+row_offsets[iblock];
int loc_start_index = 0;
int loc_end_index = 0;
int glob_start_index = 0;
int shift(col_offsets[jblock]);
for (int * i_it_aij(i_aij+1); i_it_aij != i_aij+nrow+1; ++i_it_aij)
{
glob_start_index = *i_it;
#ifdef MFEM_DEBUG
if (glob_start_index > nnz)
{
mfem::out<<"glob_start_index = " << glob_start_index << "\n";
mfem::out<<"Block:" << iblock << " " << jblock << "\n";
mfem::out<<std::endl;
}
#endif
loc_end_index = *(i_it_aij);
for (int cnt = 0; cnt < loc_end_index-loc_start_index; cnt++)
{
data[glob_start_index+cnt] = data_aij[loc_start_index+cnt];
j_amono[glob_start_index+cnt] = j_aij[loc_start_index+cnt] + shift;
}
*i_it += loc_end_index-loc_start_index;
++i_it;
loc_start_index = loc_end_index;
}
}
}
}
return new SparseMatrix(i_amono, j_amono, data, row_offsets[nRowBlocks],
col_offsets[nColBlocks]);
}
void BlockMatrix::PrintMatlab(std::ostream & os) const
{
Vector row_data;
Array<int> row_ind;
int nnz_elem = NumNonZeroElems();
os<<"% size " << row_offsets.Last() << " " << col_offsets.Last() << "\n";
os<<"% Non Zeros " << nnz_elem << "\n";
int i, j;
std::ios::fmtflags old_fmt = os.flags();
os.setf(std::ios::scientific);
std::streamsize old_prec = os.precision(14);
for (i = 0; i < row_offsets.Last(); i++)
{
GetRow(i, row_ind, row_data);
for (j = 0; j < row_ind.Size(); j++)
{
os << i+1 << " " << row_ind[j]+1 << " " << row_data[j] << std::endl;
}
}
os.precision(old_prec);
os.flags(old_fmt);
}
BlockMatrix * Transpose(const BlockMatrix & A)
{
BlockMatrix * At = new BlockMatrix(A.ColOffsets(), A.RowOffsets());
At->owns_blocks = 1;
for (int irowAt = 0; irowAt < At->NumRowBlocks(); ++irowAt)
{
for (int jcolAt = 0; jcolAt < At->NumColBlocks(); ++jcolAt)
{
if (!A.IsZeroBlock(jcolAt, irowAt))
{
At->SetBlock(irowAt, jcolAt, Transpose(A.GetBlock(jcolAt, irowAt)));
}
}
}
return At;
}
BlockMatrix * Mult(const BlockMatrix & A, const BlockMatrix & B)
{
BlockMatrix * C= new BlockMatrix(A.RowOffsets(), B.ColOffsets());
C->owns_blocks = 1;
Array<SparseMatrix *> CijPieces(A.NumColBlocks());
for (int irowC = 0; irowC < A.NumRowBlocks(); ++irowC)
{
for (int jcolC = 0; jcolC < B.NumColBlocks(); ++jcolC)
{
CijPieces.SetSize(0, static_cast<SparseMatrix *>(NULL));
for (int k = 0; k < A.NumColBlocks(); ++k)
{
if (!A.IsZeroBlock(irowC, k) && !B.IsZeroBlock(k, jcolC))
{
CijPieces.Append(Mult(A.GetBlock(irowC, k), B.GetBlock(k, jcolC)));
}
}
if (CijPieces.Size() > 1)
{
C->SetBlock(irowC, jcolC, Add(CijPieces));
for (SparseMatrix ** it = CijPieces.GetData();
it != CijPieces.GetData()+CijPieces.Size(); ++it)
{
delete *it;
}
}
else if (CijPieces.Size() == 1)
{
C->SetBlock(irowC, jcolC, CijPieces[0]);
}
}
}
return C;
}
}