I am mostly doing scientific programming in Python and do not have a whole lot of Fortran (90/95) experience. For one of my projects I want to define a derived type and overload a bunch of operators for that type. Critically, I'd like one of the variables of the derived type to an array of variable length; at least, I need two different lengths in different parts of the code. How can I best achieve this efficiently and avoiding code duplication?
My first approach was to use an allocatable array but that involved several allocate statements throughout the code including the overloaded operators. It also led to difficulties when using the code in an MPI application.
My current approach is two define a type of the same name in two separate modules and use one or the other in different parts of the code. The overloaded operators can be shared using a header file (
mytype_operators.h in the example below).
module mod1 type mytype real :: x1 real,dimension(1) :: x2 end type mytype #include "mytype_operators.h" end module mod1 module mod2 type mytype real :: x1 real,dimension(10) :: x2 end type mytype #include "mytype_operators.h" end module mod2
Unfortunately, there is one module in the code with subroutines that I would like to use for both types. Currently I have two copies of that code; one with "
use mod1", the other with "
use mod2". Can I improve this and avoid the code duplication?
Your case is very suitable for using a Fortran feature introduced in the 2003 standard (and adopted much later by compilers) named <a href="https://software.intel.com/en-us/node/692037" rel="nofollow">parameterized derived types</a>. First of all, you should check the compliance status of your compiler to know if it's fully supported.
This feature allows you to pass custom parameters when declaring or constructing a derived-type, so internal functionality will be adjusted accordingly. They are good for having different behaviour alternatives grouped in a single type name, possibly with considerable coding or storage differences. There are 2 types of parameters:<ul><li>
kindparameters behave much like the kind specifier of intrinsic types. Kind parameters of all variables must be known at compile time and are treated practically as constant values. The convenience is that you could change it easily (in code time) by altering just a value in the declaration or construction. This is commonly used for specializing the kind of components of intrinsic type.</li> <li>
lenparameters behave much like the len specifier of intrinsic character type. You can define len parameters at compile time or runtime, and a len parameter of a variable cannot change unless you declared it allocatable. Moreover, you can have arguments with assumed len-parameters in procedures, and avoid code verbosity. This is commonly used as a "length" or "dimension" parameter for a derived type, because you can use them in the declaration of array bounds and character length.</li> </ul>
In general, type parameters are used to mimic the functionality of intrinsic types in derived types, but you could also get "creative" and use it for other things, like the dimension-space of a transformation-matrix type; for a custom "union type" (like an enumeration); as the nature of a physical quantity in a "units of measurement" data-type (a real value annotated with "mass" unity is not directly compatible with a "temperature" one, but they can be handled pretty much the same way in the code); the "arity" of a tuple type...
module mod1 type :: mytype(n) integer, len :: n real :: x1 real, dimension(n) :: x2 end type mytype contains ! your operations here... end module mod1
And use it like this:
program test_pdt use mod1 implicit none type(mytype(10)) :: mt10 type(mytype(1)) :: mt1 integer :: i mt10%x1 = 40.0 mt10%x2 = [(0.5 * i, i = 1, 10)] mt1 = mytype(1)(20.0, [30.0]) call sub(mt10) call sub1(mt1) contains subroutine sub(m) ! accepts values with any "n" parameter type(mytype(*)) :: m ! you can also use them in declarations integer, dimension(m%n + 1) :: y type(mytype(m%n)) :: w print *, m%n, w%n, size(y) end subroutine sub1(m) type(mytype(1)) :: m ! only accepts values with n=1 print *, m%x1, m%x2, m%n end end<hr />
Warning: This is feature, despite of having been announced many years ago, was just recently added to most compilers, and you should be aware that there are still some bugs in implementation. You should probably be fine with regular use, but I often face false syntax errors in some corner cases, and even ICE sometimes.