Files
mfem/general/table.hpp
T

290 lines
9.7 KiB
C++

// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_TABLE
#define MFEM_TABLE
// Data types for Table.
#include "mem_alloc.hpp"
#include "array.hpp"
#include "globals.hpp"
#include <ostream>
#include <istream>
namespace mfem
{
/// Helper struct for defining a connectivity table, see Table::MakeFromList.
struct Connection
{
int from, to;
Connection() = default;
Connection(int from, int to) : from(from), to(to) { }
bool operator==(const Connection &rhs) const
{ return (from == rhs.from) && (to == rhs.to); }
bool operator<(const Connection &rhs) const
{ return (from == rhs.from) ? (to < rhs.to) : (from < rhs.from); }
};
/** @brief Table stores the connectivity of elements of TYPE I to elements of
TYPE II. For example, it may be the element-to-face connectivity table. */
class Table
{
protected:
int size; ///< The number of TYPE I elements.
/// @name Arrays for the connectivity information in the CSR storage.
/// @{
/// The length of the I array is 'size + 1',
Array<int> I;
/// @brief The length of the J array is equal to the number of connections
/// between TYPE I and TYPE II elements.
Array<int> J;
/// @}
public:
/// Creates an empty table
Table() { size = -1; }
/// Merge constructor: combine two tables into one table.
Table(const Table &table1,
const Table &table2, int offset2);
/// Merge constructor: combine three tables into one table.
Table(const Table &table1,
const Table &table2, int offset2,
const Table &table3, int offset3);
/// Create a table with an upper limit for the number of connections.
explicit Table(int dim, int connections_per_row = 3);
/// Create a table from a list of connections, see MakeFromList().
Table(int nrows, Array<Connection> &list) : size(-1)
{ MakeFromList(nrows, list); }
/// @brief Create a table with one entry per row with column indices given by
/// @a partitioning.
Table(int nrows, int *partitioning);
/// @name Used together with the default constructor
/// @{
void MakeI(int nrows);
void AddAColumnInRow(int r) { I[r]++; }
void AddColumnsInRow(int r, int ncol) { I[r] += ncol; }
void MakeJ();
void AddConnection(int r, int c) { J[I[r]++] = c; }
void AddConnections(int r, const int *c, int nc);
void ShiftUpI();
/// @}
/// Set the size and the number of connections for the table.
void SetSize(int dim, int connections_per_row);
/// @brief Set the rows and the number of all connections for the table.
///
/// Does NOT initialize the whole array I ! (I[0]=0 and I[rows]=nnz only)
void SetDims(int rows, int nnz);
/// Returns the number of TYPE I elements.
inline int Size() const { return size; }
/// @brief Returns the number of connections in the table.
///
/// If Finalize() is not called, it returns the number of possible
/// connections established by the used constructor. Otherwise, it is exactly
/// the number of established connections after calling Finalize(). */
inline int Size_of_connections() const { return J.Size(); }
/// @brief Returns index of the connection between element i of TYPE I and
/// element j of TYPE II.
///
/// If there is no connection between element i and element j established in
/// the table, then the return value is -1.
int operator() (int i, int j) const;
/// Return row i in array row (the Table must be finalized)
void GetRow(int i, Array<int> &row) const;
int RowSize(int i) const { return I[i+1] - I[i]; }
const int *GetRow(int i) const { return J.GetMemory() + I[i]; }
int *GetRow(int i) { return J.GetMemory() + I[i]; }
int *GetI() { return I.GetData(); }
int *GetJ() { return J.GetData(); }
const int *GetI() const { return I.GetData(); }
const int *GetJ() const { return J.GetData(); }
Memory<int> &GetIMemory() { return I.GetMemory(); }
Memory<int> &GetJMemory() { return J.GetMemory(); }
const Memory<int> &GetIMemory() const { return I.GetMemory(); }
const Memory<int> &GetJMemory() const { return J.GetMemory(); }
const int *ReadI(bool on_dev = true) const { return I.Read(on_dev); }
int *WriteI(bool on_dev = true) { return I.Write(on_dev); }
int *ReadWriteI(bool on_dev = true) { return I.ReadWrite(on_dev); }
const int *HostReadI() const { return I.HostRead(); }
int *HostWriteI() { return I.HostWrite(); }
int *HostReadWriteI() { return I.HostReadWrite(); }
const int *ReadJ(bool on_dev = true) const { return J.Read(on_dev); }
int *WriteJ(bool on_dev = true) { return J.Write(on_dev); }
int *ReadWriteJ(bool on_dev = true) { return J.ReadWrite(on_dev); }
const int *HostReadJ() const { return J.HostRead(); }
int *HostWriteJ() { return J.HostWrite(); }
int *HostReadWriteJ() { return J.HostReadWrite(); }
/// Sort the column (TYPE II) indices in each row.
void SortRows();
/// Replace the #I and #J arrays with the given @a newI and @a newJ arrays.
/** If @a newsize < 0, then the size of the Table is not modified. */
void SetIJ(int *newI, int *newJ, int newsize = -1);
/// Establish connection between element i and element j in the table.
/** The return value is the index of the connection. It returns -1 if it
fails to establish the connection. Possibilities are there is not enough
memory on row i to establish connection to j, an attempt to establish new
connection after calling Finalize(). */
int Push( int i, int j );
/// Finalize the table initialization.
/** The function may be called only once, after the table has been
initialized, in order to compress array J (by getting rid of -1's in
array J). Calling this function will "freeze" the table and function Push
will work no more. Note: The table is functional even without calling
Finalize(). */
void Finalize();
/// @brief Create the table from a list of connections {(from, to)}, where
/// 'from' is a TYPE I index and 'to' is a TYPE II index.
///
/// The list is assumed to be sorted and free of duplicities, i.e., you need
/// to call Array::Sort and Array::Unique before calling this method. */
void MakeFromList(int nrows, const Array<Connection> &list);
/// Returns the number of TYPE II elements (after Finalize() is called).
int Width() const;
/// Releases ownership of and null-ifies the data.
void LoseData() { size = -1; I.LoseData(); J.LoseData(); }
/// Prints the table to the stream @a out.
void Print(std::ostream & out = mfem::out, int width = 4) const;
void PrintMatlab(std::ostream & out) const;
void Save(std::ostream &out) const;
void Load(std::istream &in);
void Copy(Table & copy) const;
void Swap(Table & other);
void Clear();
std::size_t MemoryUsage() const;
};
/// Transpose a Table
void Transpose (const Table &A, Table &At, int ncols_A_ = -1);
Table * Transpose (const Table &A);
/// @brief Transpose an Array<int>.
///
/// The array @a A represents a table where each row @a i has exactly one
/// connection to the column (TYPE II) index specified by @a A[i].
///
/// @note The column (TYPE II) indices in each row of @a At will be sorted.
void Transpose(const Array<int> &A, Table &At, int ncols_A_ = -1);
/// C = A * B (as boolean matrices)
void Mult (const Table &A, const Table &B, Table &C);
Table * Mult (const Table &A, const Table &B);
/** Data type STable. STable is similar to Table, but it's for symmetric
connectivity, i.e. TYPE I is equivalent to TYPE II. In the first
dimension we put the elements with smaller index. */
class STable : public Table
{
public:
/// Creates table with fixed number of connections.
STable (int dim, int connections_per_row = 3);
/** Returns index of the connection between element i of TYPE I and
element j of TYPE II. If there is no connection between element i
and element j established in the table, then the return value is -1. */
int operator() (int i, int j) const;
/** Establish connection between element i and element j in the table.
The return value is the index of the connection. It returns -1 if it
fails to establish the connection. Possibilities are there is not
enough memory on row i to establish connection to j, an attempt to
establish new connection after calling Finalize(). */
int Push( int i, int j );
/// Destroys STable.
~STable() {}
};
class DSTable
{
private:
class Node
{
public:
Node *Prev;
int Column, Index;
};
int NumRows, NumEntries;
Node **Rows;
#ifdef MFEM_USE_MEMALLOC
MemAlloc <Node, 1024> NodesMem;
#endif
int Push_(int r, int c);
int Index(int r, int c) const;
public:
DSTable(int nrows);
int NumberOfRows() const { return (NumRows); }
int NumberOfEntries() const { return (NumEntries); }
int Push(int a, int b)
{ return ((a <= b) ? Push_(a, b) : Push_(b, a)); }
int operator()(int a, int b) const
{ return ((a <= b) ? Index(a, b) : Index(b, a)); }
~DSTable();
class RowIterator
{
private:
Node *n;
public:
RowIterator (const DSTable &t, int r) { n = t.Rows[r]; }
int operator!() { return (n != NULL); }
void operator++() { n = n->Prev; }
int Column() { return (n->Column); }
int Index() { return (n->Index); }
void SetIndex(int new_idx) { n->Index = new_idx; }
};
};
}
#endif