Cleanup of python assemble

Issue #65 duplicate
Martin Sandve Alnæs created an issue

To fix this issue,

https://bitbucket.org/fenics-project/ufl/issue/7/get-rid-of-arguments-to-compute_form_data

the assemble/jit pipeline in dolfin will probably need some cleanup.

This code is really hard to follow and likely contains lot of unknown corner case bugs. There are so many possible code paths that are non-obvious and not tested that it's not feasible to preserve all possible use cases of today. It is therefore likely that some user interface regressions will be necessary.

We need to identify which features need to be preserved in the python assemble interface, so we can design a new, cleaner, and better tested implementation.

Comments (14)

  1. Martin Sandve Alnæs reporter

    What is the use case for passing a ufc::form to assemble? Earlier it was used by sfc integration tests with dolfin. Can we safely ignore it now?

  2. Martin Sandve Alnæs reporter

    What is the use case for passing a dolfin::Form to assemble?

    Is it used for controlled "pre-jitting"? That may be important.

    The code says: "First check if we got a cpp.Form which originates from cpp layer", is that an actual use case?

  3. Johan Hake

    The ufc::form is something we have kept for ages. Going back to when someone (TM) needed to compile pure ufc forms.

    The dolfin::Form is needed so we can assemble a:

    aform = adapt(form)

    The hierarchical interface of dolfin returns an adapted dolfin::Form, which contains everything it needs to be assemble.

  4. Martin Sandve Alnæs reporter

    Use cases for passing mesh to assemble:

    • The integrand has no mesh, e.g.

      Expression("x[0]")*dx().
      

      Possible to handle by attaching mesh to measure? E.g.

      Expression("x[0]")*dx(mesh)
      

      or similar syntax.

    • The form is assembled for multiple meshes. Possible to handle by letting the user create a new or modify an existing dolfin::Form and passing that to assemble.

  5. Martin Sandve Alnæs reporter

    Use cases for passing foo_domains to assemble:

    • The form has no domains attached to its mesh or measures.
    • The form is assembled for multiple sets of foo_domains. Probably rare.

    Both cases above are possible to handle by letting the user create a new or modify an existing dolfin::Form and passing that to assemble.

  6. Johan Hake

    I think dealing with dolfin::Forms should be for advanced use cases. I think reassemble any form on different mesh should be a common use case not demanding fiddling with dolfin::Form.

  7. Martin Sandve Alnæs reporter

    Use cases for passing a dolfin::Form to assemble:

    • Assembling forms from the C++ interface, e.g.: aform = adapt(form)
    • Controlled pre-jitting for optimization.
    • Reusing UFL form with different meshes.
    • Reusing UFL form with different subdomain markers.
    • Reusing UFL form with different coefficients.
    • (Wrapping a UFC form?)

    I see a pattern...

  8. Martin Sandve Alnæs reporter

    Ok, so we want to keep

    assemble(M, mesh=mesh)
    

    which I guess a lot of existing code is doing. It's not possible to make this feature work with

    assemble(M, mesh=mesh1d)
    assemble(M, mesh=mesh2d)
    

    which is currently broken, while simultaneously keeping the memoization of compute_form_data to avoid rebuilding the preprocessed form every time.

    I therefore suggest the following: Passing mesh or foo_domains arguments to assemble:

    assemble(M, mesh=mymesh, cell_domains=mydomains)
    

    behaves like this:

    M2 = reconstruct_form(M, mesh=mymesh, cell_domains=mydomains)
    assemble(M2)
    

    where reconstruct_form is a new utility function that builds a new Form object by reusing the integrands but creating new measures with the given mesh and domains replacing the old. I think an error should be raised if the integrands contain coefficients whose function spaces have conflicting meshes.

    The original form is then untouched, and

    assemble(M, mesh=mesh1d)
    assemble(M, mesh=mesh2d)
    

    will work. The cost is that symbolic preprocessing will always be repeated when mesh or domains are passed to assemble, but jitting will still hit the instant cache.

    Symbolic preprocessing (compute_form_data) can still be cached for all cases where mesh and domains are attached to the measures in the original form. Any code today that does

    assemble(f*dx)
    

    instead of

    M = f*dx
    assemble(M)
    

    is not getting caching of symbolic preprocessing anyway.

    Attaching the mesh to measures is not a current feature, but something we've (I've?) been contemplating for multidomain support. The syntax may need a little bit of thought.

  9. Martin Sandve Alnæs reporter

    I've done some cleaning up of the python assemble function in a branch that I can't merge right away.

  10. Log in to comment