← Back to team overview

dolfin team mailing list archive

Re: Assembly

 

On Wed, Dec 20, 2006 at 09:21:19AM +0100, Johan Hoffman wrote:
> > On Tue, Dec 19, 2006 at 06:43:30PM +0100, Garth N. Wells wrote:
> >>
> >>
> >> Johan Jansson wrote:
> >> > On Tue, Dec 19, 2006 at 04:08:56PM +0100, Anders Logg wrote:
> >> >> Even if FFC does not yet generate code in UFC, it might be time to
> >> >> start thinking about how to organize the assembly when we
> >> >> (re-)implement it for the new interface. Here are some issues:
> >> >>
> >> >> 1. Do we want to keep the interface
> >> >>
> >> >>    FEM::assemble(a, A, mesh);
> >> >>
> >> >> or do we want to change this? Another option would be
> >> >>
> >> >>    Assembler assembler;
> >> >>    assembler.assemble(a, A, mesh);
> >> >>
> >> >> which could allow for reuse of data. We could also add assemble()
> >> >> functions in namespace dolfin that create an Assembler object and
> >> >> calls it which would allow
> >> >>
> >> >>    assemble(a, A, mesh);
> >> >
> >> > I think it would be good if the assembler has some state, then we
> >> > could cache data and reuse if we're assembling again on the same mesh
> >> > for instance.
> >> >
> >>
> >> Agree. There is something to be gained here, with recycling sparse
> >> matrix structures in particular.
> >
> > So do we call it Assembler?
> 
> Sounds good. And I agree with the possible gains of creating an Assembler
> object.

ok.

> > Any other options?
> 
> Should we include "assembling the action" of a form in Assembler? But I
> guess this is already part of the structure; Assembling a(v,u) with u,v
> test functions gives the matrix, and with u a Function we get the action
> of a(.,.) on u, etc.

We could, but it's not necessary (see below).

> >> >> 2. The UFC specification is the same independent of the rank of the
> >> >> global tensor, which might help us remove some of the duplicated
> >> code.
> >> >>
> >> >> 3. Do we want to keep the abstraction BilinearForm, LinearForm,
> >> >> Functional? Or could we throw them away and just use Form? I imagine
> >> >> something like
> >> >>
> >> >>     class Form
> >> >>     {
> >> >>     public:
> >> >>
> >> >>        ...
> >> >>
> >> >>        friend FEM;
> >> >>
> >> >>     private:
> >> >>
> >> >>        ufc::form form;
> >> >>
> >> >>     }
> >> >>
> >> >> Opinions?
> >> >
> >> > I think Form is a good idea. The same thinking could be extended to
> >> > FFC, like we talked about. Where the specification of a form is just a
> >> > (Python) function, which returns a certain type depending on if and
> >> > where the test and trial functions are placed.
> >> >
> >>
> >> Sounds good.
> >
> > Something of a problem here will be that a dolfin::Form and a
> > ufc::form is something fixed. It can be assembled into a tensor of a
> > fixed rank r. You can't say that you want to think of it as an
> > operator (matrix) or action (vector) when you have created it.
> 
> I can think of several situations where it would be great to have one
> dynamic form; to be used in different ways. Typical examples; compute
> discrete residuals (R(U)=a(v,U)-L(v)), postprocessing surface forces
> (N(U)=a(phi,U)-L(phi)), with phi defined on the surface).

This should be ok at least from within Python.

> (Anders: the format of the residual is for you...)

(thanks)

> > On the other hand, in PyDOLFIN the just-in-time compilation of UFC
> > forms makes it possible to be dynamic about these things.
> 
> Maybe, but you'd still need to edit your form?

Only when working with FFC/C++, not in PyDOLFIN/FFC.

One could perhaps have something like

  def poisson(v, u):
      return dot(grad(v), grad(u))*dx

  a = BilinearForm(poisson)
  L = LinearForm(poisson)
  M = Functional(poisson)

or

  a = Operator(poisson)
  L = Action(poisson)
  M = Functional(poisson)

Then the function Operator() could look something like

  def Operator(form):
      v = TestFunction()
      u = TrialFunction()
      a = form(v, u)
      return ffc_just_in_time_compile_or_something(a)

and the function Action() could look something like

  def Action(form):
      v = TestFunction()
      u = Function()
      L = form(v, u)
      return ffc_just_in_time_compile_or_something(L)

How about that?

Then one can define the form once and use it in different ways. The
code (UFC) that gets generated is in each case specific for the type
of usage one makes of the form (Operator, Action, Functional) but
from the users point of view it's dynamic.

I sort of like Operator, Action better than BilinearForm, LinearForm
since in both cases we have a bilinear form that gets used in
different ways.

> > What do we want the interface to be for working with forms? There are
> > two very different use cases:
> >
> >   1. FFC/DOLFIN
> >
> >   Write form in FFC, compile, #include in C++ code.
> >   Here we have two different worlds: FFC Python and DOLFIN C++.
> 
> With a dynamic form, this would be somewhat more flexible.
> 
> >   2. PyDOLFIN
> >
> >   Write form in PyDOLFIN and assemble.
> >   Here it's just one world and Form could mean something different.
> > Any suggestions for how we want the interface to look? If we decide
> > how we want the interface, I'm sure we can make it happen.
> 
> I like the dynamic option, but maybe this means for FFC/DOLFIN that FFC
> has to be used within DOLFIN, not only code generated from FFC?
> 
> I think we should keep the option of using DOLFIN without running FFC, in
> the spirit of efficiency, to precompute as much as possible.
> 
> And then maybe PyDOLFIN provides the dynamic environment for FFC/DOLFIN
> together, which would be enough.
> 
> This way one could think of a situation where one do testing and
> development of e.g. a solver in PyDOLFIN, where it is easy to test things
> out. And then when satisfied, one can precompute as much as possible from
> FFC and write a static pure DOLFIN solver, that would typically be
> somewhat more efficient.

Not necessarily if we allow caching. One can allow the jit-compilation
to reuse forms that have already been compiled.

/Anders


References