Hooks#

This is an updated version of the Oasis wiki’s Hooks, adjusted with the new functionality of OasisMove. The Navier-Stokes solvers NSfracStep, NSCoupled, and NSfracStepMove need to import a mesh, some parameters and a number of solver specific or user defined hooks from the solvers and problems submodules respectively. The problem hooks are called at certain strategic locations in the solvers. All problem hooks have default versions in problems/NSfracStep/__init__.py and problems/NSCoupled/__init__.py .

The default hooks for NSfracStepMove with brief descriptions are:

def body_force(mesh, **NS_namespace):
    """Specify body force"""
    return Constant((0,) * mesh.geometry().dim())


def scalar_source(scalar_components, **NS_namespace):
    """Return a dictionary of scalar sources."""
    return dict((ci, Constant(0)) for ci in scalar_components)


def initialize(**NS_namespace):
    """Initialize solution."""
    pass


def pre_boundary_condition(**NS_namespace):
    """Called after defining the function spaces and functions,
       but before the boundary conditions are created.
    """
    return dict(boundary=None)


def create_bcs(sys_comp, **NS_namespace):
    """Return dictionary of Dirichlet boundary conditions."""
    return dict((ui, []) for ui in sys_comp)


def update_boundary_conditions(**NS_namespace):
    """Called just prior of assembling mesh and/or fluid equations.
    """


def mesh_velocity_hook(**NS_namespace):
    """Called just prior to solving for mesh velocity."""
    pass


def velocity_tentative_hook(**NS_namespace):
    """Called just prior to solving for tentative velocity."""
    pass


def pressure_hook(**NS_namespace):
    """Called prior to pressure solve."""
    pass


def scalar_hook(**NS_namespace):
    """Called prior to scalar solve."""
    pass


def start_timestep_hook(**NS_parameters):
    """Called at start of new timestep"""
    pass


def temporal_hook(**NS_namespace):
    """Called at end of a timestep."""
    pass


def pre_solve_hook(**NS_namespace):
    """Called just prior to entering time-loop. Must return a dictionary."""
    return {}


def theend_hook(**NS_namespace):
    """Called at the very end."""
    pass

All hooks may be overloaded in the problem module because the problem imports from the __init__.py file initially. For information of the classical Oasis hooks, and list of solver hooks we refer to the Oasis wiki.

Understanding NS_namespace#

Throughout the Python scripts you will notice the special keyword argument **NS_namespace. This is actually the entire namespace of the NSfracStepMove solver, where the hooks are called using the following notation:

initialize(**vars())

temporal_hook(**vars())

That is, the solver sends its entire namespace, vars(), to the problem as keyword arguments. In the problem module you may now access anything from the Navier-Stokes solver when initializing. The hook can be used to set the velocity and pressure fields through for example the following code in the problem module, which has been done in the MovingVortex problem:

init_fields = {"u0": 0, "u1": 1, "u2": 2, "p": 0}


def initialize(q_, q_1, q_2, **NS_namespace):
    for ui in q_:
        q_[ui].vector()[:] = init_fields[ui]
    for ui in q_1:
        q_1[ui].vector()[:] = init_fields[ui]
        q_2[ui].vector()[:] = init_fields[ui]

Note that q_ and q_1 are dictionaries declared in the solver that hold the solution at current and previous timesteps. For example, for 2D problems q_ is declared as

V = FunctionSpace(mesh, "CG", velocity_degree)
Q = FunctionSpace(mesh, "CG", pressure_degree)
q_ = {"u0": Function(V), "u1": Function(V), "p": Function(Q)}

and similarily for q_1 and q_2, only that these do not contain any pressures, since the pressure is only stored for one timestep. The dictionaries are accessed through arguments of the initialize hook. Any variable that you want to use is simply written out, the rest is soaked up by **NS_namespace.