On Wed 2008-10-22 15:52, John Peterson wrote:
> On Wed, Oct 22, 2008 at 11:52 AM, Jed Brown <[EMAIL PROTECTED]> wrote:
> > trying to keep this on -devel...
> 
> Oops, yes I will try and do the same.
> 
> [snipped]
> 
> > The scary thing here would be the implicit cast (creating a new
> > PetscMatrix which is a MatShell) occurs when a Matrix (which might even
> > be a PetscMatrix) is passed to a Petsc solver.  This is why I think it's
> > cleaner to put the getter for the PETSc Mat in Matrix since this would
> > enable the solvers to take any Matrix.  In this case, we'd have
> 
> This type of cast is indeed scary, since you cannot tell if it
> succeeds by checking the result.  If we make use of polymorphism,
> through the use of a common, correctly implemented PetscMatrixBase
> class, then we can use dynamic_cast to help us, which conveniently
> returns a NULL pointer upon failure.
> 
> I'm not too worried about supporting *completely* arbitrary
> UserShellMatrix objects.  It's easy enough, and I think, reasonable,
> to have folks derive their UserShellMatrices from NumericMatrix (for
> the required interface) and PetscMatrixBase (for the required Mat
> object data).  This approach also does not preclude them from, in
> addition, deriving from (an eventual, correctly implemented)
> EpetraMatrixBase and even other types, to allow use of a
> UserShellMatrix with the other solver types.

So the user implements UserShellMatrix1 through N, then we add FooSolver
and they have to change their code to derive all their matrices from
FooMatrix or FooShellMatrix.  This means that the user sees M:N
inheritance and it's completely unnecessary.

The hypothetical Matrix::foo_matrix() getter can be implemented outside
of Matrix, it's functionally the same as a smart-cast.  An external
implementation would try a dynamic_cast<PetscMatrix>, if it succeeds,
just call .mat(), otherwise create a MatShell and return that.  For
PETSc you don't need a smart-pointer, just use PetscObjectReference(),
for others packages you may need one.  My rationale for putting this in
Matrix is it guarantees that the MatShell wrapper is unique throughout
the application.  Since the wrapper uses a trivial amount of memory and
will always be observationally equivalent, it shouldn't be a problem to
possibly have duplicates around.

In my scheme, libmesh only needs to provide

Matrix <- PetscMatrix
       <- EpetraMatrix
       <- LaspackMatrix

The user derives Matrix to implement their shell matrices.  The solvers
all take a generic Matrix and call a smart-extractor (it could be
namespaced as Foo::Mat libmesh::Foo::mat(Matrix A), it tries a
dynamic_cast<FooMatrix> so it can call FooMatrix::mat(), otherwise it
creates a Foo::Mat using Foo's shell matrix functionality).  Now
FooMatrix only means that the matrix is Foo-native so that
libmesh::Matrix operations are defined in terms of Foo::Mat operations.
Being a FooMatrix has nothing to do with being able to use FooSolver.

With this scheme, it's possible for the user to implement their shell
matrices, compile with shared libs, update libmesh to support new
solvers, and run their program with the new solvers (not even relink).
All matrix types are automatically supported by all solvers and there is
no multiple inheritance.  What is the compelling reason for M:N
inheritance?  It seems strictly less flexible and exposes more
implementation details.

> I'm still just (admittedly stubbornly) against the concept of the
> abstract base having anything to do with derived types.  

In my scheme, being able to provide a PETSc::Mat is not an exclusive
property of the derived type PetscMatrix, we can make a PETSc::Mat from
any Matrix, we just need to know what operations have been implemented.
I don't think it's desirable that PetscSolver needs a PetscMatrix, but
you can wrap the new MatShell in a PetscMatrix if it makes you feel
better (the solver is only ever going to call PetscMatrix::mat(), so the
wrapping is completely gratuitous).

> While there may be no practical problems (or only minor
> inconveniences, for example, the entire petsc_mat() interface must be
> #ifdef'd out when LIBMESH_HAVE_PETSC is 0) with this approach, I just
> think it is bad design.

Why must the interface be #ifdef'd out?  There is no chance you would
end up with a PetscMatrix if it wasn't compiled with PETSc, but there's
no chance you'd have a PetscSolver either so you'd never try to call
Matrix::petsc_mat().  If you did, you'd get not_implemented() since the
implementation would be #ifdef'd out.

Putting one pointer in Matrix per solver package is ugly and avoidable,
but it's simpler, reduces the number of objects to keep track of, and
guarantees that shell objects are created at most once.  It does mean
that the user will have to recompile (but not modify!) their code if new
solver packages are added, but not if existing packages are
enabled/disabled.

Sorry this is long-winded.  I'll be quiet now.

Jed

Attachment: pgpREj391bAcr.pgp
Description: PGP signature

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Libmesh-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libmesh-devel

Reply via email to