Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce wrappers and separate transport from time discretisations #382

Merged
merged 22 commits into from
Jul 24, 2023

Conversation

tommbendall
Copy link
Contributor

@tommbendall tommbendall commented Jun 23, 2023

This PR:

  • introduces the Wrapper object for time discretisations
  • introduces a SpatialMethod object to describe specific spatial discretisations, similar to physics schemes.
  • reorganises code relating to these things

This allows a bunch of specific code to be pulled out from the time discretisations, and gives us new ways of combining and specifying our different options.

Breakdown of changes

  1. A new Wrapper object is introduced in wrappers.py, which has pre- and post- apply methods. There are three applications of this:
  • EmbeddedDGWrapper
  • RecoveryWrapper
  • SUPGWrapper (which does nothing in pre- and post- apply methods, but does introduce a different test function to be used)
    I have tried to keep all of this code the same, but we should instantly gain the functionality to apply these wrappers to physics and diffusion terms.
  1. TimeDiscretisation objects can now set up a Wrapper object when they are passed wrapper options. All of the specific wrapper code is now completely pulled out of time_discretisation.py.
  2. A new SpatialMethod object is defined in spatial_methods.py, which is the base object for specifying a spatial discretisation of some term. Why bother to introduce this? It allows us to separate the generic term in an equation [e.g. test*dot(u,grad(q))*dx], from its discretisation [e.g. an upwind discretisation]. When the stepper sets up all of the methods/schemes, it will replace the term in the equation with the discretisation in the SpatialMethod.
  3. Two general child classes of SpatialMethod have been introduced: TransportMethod in transport_methods.py and DiffusionMethod in diffusion_methods.py. These are very simple and pretty much just call the methods of SpatialMethod but with the specific transport/diffusion labels. However the TransportMethod requires more care as it needs to ensure that the transporting_velocity is replaced correctly.
  4. diffusion.py has been removed, and code for the InteriorPenaltyDiffusion method has been moved to diffusion_methods.py
  5. Two types of TransportMethod have been implemented: DGUpwind (which is basically our work-horse at the moment) and DefaultTransport (which calls the basic form originally set up in the equation -- I'm open to better names for this!). The forms used in DGUpwind are stored in transport_methods.py
  6. NB: SUPG transport much of the same code as DGUpwind, so this has been implemented as a Wrapper (to ensure that the test function in the time discretisation gets replaced). The appropriate IntegrateByParts option should be passed to the DGUpwind object.
  7. transport_forms.py has been renamed as common_forms.py, and the intention is that this should contain forms that are used by PrognosticEquations and not SpatialMethods. The basic transport and diffusion forms have been added to this.
  8. I haven't been able to remove the linearisations of the transport forms, which was my hope. I think this will require some special care with the transporting_velocity label. I have however fixed the linear_advection_form (issue Linear transport forms need fixing #332).
  9. I have made some changes to the circulation form of the vector transport equation. I was a bit confused by what was happening before, and hopefully this clears things up:
  • "vector_invariant": (u.∇)u = (∇×u)×u + (1/2)∇(u.u), implemented as a single transport term, with separate transported and transporting velocities
  • "circulation": (u.∇)u = (∇×u)×u + (1/2)∇(u.u), implemented as two terms, a transport term (∇×u)×u and a K.E. term (1/2)∇(u.u) [which would now be evaluated as a forcing rather than as a transport term]
  1. All integration-tests and examples with transport or diffusion terms now need some extra code to specify which transport method is needed. This pretty much means just adding transport_methods = [DGUpwind(eqn, variable_name), ...] to all of these, and ensuring that it is passed to the time stepper.

@tommbendall tommbendall changed the title Introduce model wrappers and separate transport from time discretisations Introduce wrappers and separate transport from time discretisations Jun 23, 2023
@tommbendall tommbendall marked this pull request as ready for review July 13, 2023 08:34
(u.∇)q = (∇×q)×u + (1/2)∇(u.q)

The form returned by this function corresponds to the (∇×q)×u circulation
term. An an upwind discretisation is used when integrating by parts.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to remove part about upwind discretisation.

ubar (:class:`ufl.Expr`): the transporting velocity.

Raises:
NotImplementedError: the specified integration by parts is not 'once'.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer raises error

Args:
equation (:class:`PrognosticEquation`): the equation, which includes
the original type of this term.
variable (str): name of the variable to set the transport scheme for
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not just transport

self.field = equation.X
self.test = equation.test

# Find the original transport term to be used, which we use to extract
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not just transport

residual = equation.residual.label_map(
lambda t: t.has_label(term_label), map_if_false=drop
)
active_variables = [t.get(prognostic) for t in residual.terms]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is "active" the right word here?

Copy link
Contributor

@jshipton jshipton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic - thanks @tommbendall

@jshipton jshipton merged commit cb0c056 into main Jul 24, 2023
1 check passed
@jshipton jshipton deleted the wrappers branch July 24, 2023 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants