japamat
japamat

Reputation: 720

variable length array in derived type

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?

Upvotes: 4

Views: 1088

Answers (1)

Rodrigo Rodrigues
Rodrigo Rodrigues

Reputation: 8546

Your case is very suitable for using a Fortran feature introduced in the 2003 standard (and adopted much later by compilers) named parameterized derived types. 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:

  • kind parameters 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.
  • len parameters 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.

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

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.

Upvotes: 2

Related Questions