Hybrid-norm and Fortran 2003: Separating the physics from the solver |
type vec real, allocatable :: vals(:) contains procedure, pass :: add=>add_me end typeThe => keyword indicates that to access the function we should use the name on the left but the name of the procedure is found on the right. The pass keyword will be described later. Within the module that contains the vec definition, we need to define the add_me function:
subroutine add_me(vec1,vec2) class(vec) :: vec1 type(vec) :: vec2 vec2%vals=vec2%vals+vec1%vals end subroutineNote that vec1 is declared using the class keyword rather than the type keyword. The class keyword indicates that anything of type vec or anything that inherits from vec can call this function. We can access the add function through the standard Fortran call keyword:
type(vec) :: vec1,vec2 call vec1%add(vec2)Note how the function definition has two arguments while the call description has a single argument. The pass keyword is the reason for the discrepancy. The pass keyword indicates that the type itself should be passed as the first argument.
An abstract type can also be constructed. An abstract type contains one or more function pointers that haven't been assigned. The abstract type can never be declared. Only types that inherit from it can be declared in a functional unit and only if all function pointers have been assigned. An abstract type is declared with the abstract keyword. The keyword deferred is used for all functions that will be assigned by inherited objects. In addition, you can define the interface of each function using the abstract interface construct. Below is an example of using these features:
type,abstract :: vector contains procedure(add_dec), pass, deferred :: add end type abstract interface subroutine add_dec(v1,v2) class(vector) :: v1,v2 end subroutine end abstract interfaceWe can declare a type that inherits from this vector class using the extends construct:
type, extends(vector) :: vec_real real :: vals(:) contains procedure, pass :: add=>add_real end typeThe function vec_real must have the same interface as the type it extends from with the exception of the first argument which now must be of type vec_real. Note how the abstract interface for the add function defines v2 as a class object. To add two vectors we need them to be the same type. We can check the type of a class object using the select type construct:
subroutine add_real(v1,v2) class(vector) :: v2 class(vector_real) ::v1 select type(v2) type is(vector_real) v1%vals=v1%vals+v2%vals end select end subroutineWithin the type is code block, v2 is assumed to be of type vector_real and all components of vector_real can be accessed.
Fortran 2003 also provides a cleanup feature. The finalize keyword is called when an object is no longer needed (for example, when leaving a subroutine where it has been declared). In the example below, the deleteit function is used remove any memory associated with an object.
type, extends(vector) :: vec_real real :: vals(:) contains procedure, pass :: add=>add_real final :: deleteit end typeFinally, we need the ability to clone an abstract type. The allocate function now takes a keyword argument source. For example, we can create an object v2 of the same type as v1 even though v1 is of an abstract rather than concrete type.
subroutine cloneit(v1,v2) class(vector),pointer :: v1,v2 allocate (v2,source=v1) end subroutineWith these extensions to the Fortran language, it is possible to completely separate operator writing from solver writing.
Hybrid-norm and Fortran 2003: Separating the physics from the solver |