[petsc-dev] PETSc factories 101 and 102
Matthew Knepley
knepley at gmail.com
Sat Sep 8 07:19:42 CDT 2012
On Sat, Sep 8, 2012 at 5:39 AM, Barry Smith <bsmith at mcs.anl.gov> wrote:
>
> Some discussion came up today about the use of "factories" in C++ (and
> other OO languages) code and I thought it would be useful to related it to
> how PETSc handles the same issue since some numerical libraries (the next
> generation of Trilinos's ML and Chombo) are using factories extensively and
> recklessly (and will, I am confident, alienate a lot of users. I love it
> when our competitors make the mistake of chasing every new idea down the
> wrong road).
>
> "In object-oriented computer programming, a factory is an object for
> creating other objects ", "it deals with the problem of creating objects
> (products) without specifying the exact class of object that will be
> created." http://en.wikipedia.org/wiki/Factory_method_pattern
> http://en.wikipedia.org/wiki/Factory_(software_concept)
>
> So for example if in my code I need a KSP solver object I could do
> something like
>
> void myroutine( …..) {
> KSPObject *kspobject = new KSPGMRESObject
>
> where KSPObject is my abstract class and KSPGMRESObject is a specific
> implementation some one has written. Now in the code I can go off and use
> the KSPObject to solve something and the rest of my code does not need to
> "know" that the KSPObject is actually a KSPGMRESObject. But since when I
> create the object I cannot create an abstract class and can only create a
> specific implementation my code has now hardwired the KSPGMRESObject.
> Factories are a way of "avoiding" this hardwiring. I can introduce a new
> class KSPObjectFactory that has a method newKSPObject() and then reorganize
> my code as
>
> void myroutine(KSPObjectFactory *factory, ….) {
> KSPObject *kspobject = factor->newKSPObject();
>
> and I've removed the explicit use of a particular implementation
> constructor from my routine. The actual decision of what type of KSPObject
> to create is "pushed up higher in the code" and involves the factory. So
> for example I could have a KSPObjectFactory() that produces a particular
> implementation of a KSPObject by setting a string name into the factory.
> So if BarrysKSPObjectFactory is a particular implementation of
> KSPObjectFactory then I could write "higher up in my code"
>
> BarrysKSPObjectFactory *kspobjectfactory = new
> BarrysKSPObjectFactory;
> kspobjectfactory->setimplementationbyname("gmres");
> myroutine(kspobjectfactory);
>
> one could also consider a method on BarrysKSPObjectFactory
> setimplementationbyCommandLineArgs(argsc,args); now I can "push up" the
> decision of what KSPObject to actually use to runtime as a command option.
>
> A drawback to factories is that it can easily double the number of
> different classes that users have to deal with and many people (at least
> Mark and I) find it cumbersome.
>
> PETSc factories 101
> ----------------------------
>
> In PETSc because all objects are essentially delegator objects ("the
> delegation pattern is a design pattern in object-oriented programming where
> an object, instead of performing one of its stated tasks, delegates that
> task to an associated helper object"
> http://en.wikipedia.org/wiki/Delegation_pattern) when we "create" a
> PETSc object we have not yet actually created the delegated object and thus
> do not need traditional factories for the purpose listed above. For example
>
> KSP ksp;
> KSPCreate(comm,&ksp);
>
> gives me a KSP solver object that I can pass around to other code,
> keep references to, and even set options on but it does not have a specific
> implementation of a solver wired to it yet. When I call
>
> KSPSetType(ksp,"gmres"); or KSPSetFromOptions(ksp);
>
> what happens is the KSP object looks for the "gmres factory" that has
> been registered with KSPRegister("gmres",KSPCreate_GMRES,..) and calls
> KSPCreate_GMRES() to generate the delegate that will actually do the
> solving.
>
> Since the delegate is completely encapsulated inside the ksp object
> I can change the delegate at a later time in the code to have a different
> implementation by just calling
>
> KSPSetType(ksp,"cg")
>
> The old delegate is freed and the new solver implementation is put
> in place. And all references to the ksp object continue to work (just using
> the new solver).
>
> So you see the design of PETSc allows "pushing up" the specific
> choice of implementations of classes in essentially the same way as factory
> objects do but without the need for users to explicitly create and
> manipulate the factories.
>
> PETSc factories 102
> ---------------------------
>
> The other place PETSc uses factories is to allow "mesh information"
> to determine algebraic objects that are created within algebraic solvers.
> This is done with the DM abstract class which you can think of as a factory
> for Vecs and Mats (though it does other things as well).
>
> Consider the nonlinear solver SNES in PETSc. I can use
>
> SNESSetJacobian(snes,J,J,func,ctx)
>
> to provide the matrix that Newton's method is going to use. But these
> means that my code that creates the SNES object and sets is various
> parameters has to explicit know how to create the J Mat object. If I am
> using a complicated meshing package that generated some grid and is going
> to use finite elements to compute the Jacobian J I'd like the figuring out
> of the size and sparsity pattern of the J to be handled by the meshing
> package. Thus I would make an implementation of the DM abstract class (say
> MattsDM) that does all this yucky figuring out. Then I could call
>
> DMCreateMatrix(dm,&J) /* now my solver code sees nothing of the
> yuckyness of particular mesh details */
>
> and then pass the J to
>
> SNESSetJacobian(snes,J,J,func,ctx)
>
> Similarly I can do the same thing to create vectors.
>
> We can take this one step further. So far I've been assuming that the
> application programmer is explicitly creating the algebraic objects (Mats
> and Vecs) and passing them to the solver object.
>
> Once solver objects start getting complicated; for example with multigrid
> methods it is painful for the user to create ALL the vectors and matrices
> needed for the multiple levels and provide them all to PCMG (though it is
> possible and we provide interfaces for doing that).
>
> Instead we can create DMs that can generate appropriate sized vectors and
> matrices and give those DMs to the solver object and the solver then calls
> the methods to get new vectors and matrices wherever it needs them inside
> the solver. PCMG would still need several DMs, one for each level. But
> rather than requiring the user to create these several DMs the user can
> create a single DM and the DM objects have methods in them that generate
> coarser or finer DMs that can be used to generate the vectors and matrices
> on the other levels. This is why a simple call of SNESSetDM(snes,dm)
> allows the nonlinear and linear solver objects (including all the levels of
> multigrid) to create all their various needed vectors and matrices.
>
> When composing complicated solvers this approach can be extremely
> powerful, one can envision DMs being able to generate sub DMs that
> represent just pieces of the physics and using those DMs to generate the
> vectors and matrices for the solver associated with the sub physics (in
> PCFieldSplit). Thus we can generate all the algebraic pieces for very
> complicated nested solvers with multigrid inside fieldsplit and fieldsplit
> into multigrid etc for any number of levels with one simple paradigm.
>
>
> In conclusion, you can think of PETSc as having one important visible
> factory class the DM and then one factory completely transparent to the
> user for each abstract PETSc object: IS, Vec,Mat,KSP,SNES, and yes even DM.
> IMHO factories are a powerful and useful tool for large libraries but they
> should be used sparingly and most of them though thoughtful design need
> never be seen the users (because if seen by the users that steepens the
> learning curve a great deal)
>
> Questions, clarifications?
>
I have written up a discussion of 101 for the Encyclopedia of Programming
Languages (Hans Petter is editing), which is attached. We
should certainly write up 102 since it is more interesting.
I think DMCreateSubDM() is fine for field pieces, but we need some way of
doing domain pieces, and so far I don't like any of the suggestions.
Matt
> Barry
>
> We actually have another factory in PETSc, MatGetVecs() that returns for a
> given matrix appropriately sized vectors. I don't have some grand
> philosophical reason for it to be around, but it is a very useful utility
> since often when you have a matrix you need vectors to perform operations
> with it and it is cumbersome to have to pass some vector through several
> layers of routines to get it to where it is needed to interact with the
> matrix.
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
--
What most experimenters take for granted before they begin their
experiments is infinitely more interesting than any results to which their
experiments lead.
-- Norbert Wiener
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mcs.anl.gov/pipermail/petsc-dev/attachments/20120908/ca19db14/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ProgrammingLanguagesForScientificComputing_Full.pdf
Type: application/pdf
Size: 207634 bytes
Desc: not available
URL: <http://lists.mcs.anl.gov/pipermail/petsc-dev/attachments/20120908/ca19db14/attachment.pdf>
More information about the petsc-dev
mailing list