Libclasses

From LIBISIS
Jump to navigation Jump to search

Introduction

IXTtestclass is an object which contains all the types of variables which exist in the framework, it also contains implementations of all the standard subroutines which MUST be included by any new module which is added to the framework. They are:


In each of these routines there are particular ways of treating each different type of variable, and the code in IXTtestclass can be used as a model for other modules, containing all the standard code required. This documentation should be read in tandem with the libclasses\IXMtestclass.f90 file

Module Definition

A class should be defined as follows:

  • A derived TYPE starting with the IXT prefix should be created in a module with the same name but prefixed IXM
  • within this module various functions should be defined for class operations e.g. addition and display
  • the class object should be the first argument of any function
  • module functions should make no explicit reference to matlab, they should be passed constructed objects
  • wrapper functions outside the module should be defined to pass data to the module functions

we start by defining the name of the module

module IXMtestclass

include the modules which are relied upon by the module

  use IXMbase
  use IXMspectra
  use IXMdataset_2d

define the type, all component elements of the module are private and encapsulated as a rule, the type is public.

  type IXTtestclass 
     private 
     ! all objects have a base class

every object must contain an IXTbase class, it contains information such as the validity of an object

     type(IXTbase) :: base 

simple real and integer variables, can be defined as fixed length static arrays and initialised

     real(dp) :: val    
     integer(i4b) :: nx 
     real(dp) :: val_static(3)=0.0 
     integer(i4b)::int_static(4)=0.0

variable length arrays are defined as pointers, they are always declared as NULL by default and can be allocated using the IXFalloc subroutines

     real(dp), pointer :: val_array(:) => NULL()  
     integer(i4b),pointer :: int_arr(:,:)=>NULL()

other objects can also be defined in a type

 
     type(IXTspectra):: spectra 

simple strings and logicals are straightforward

     logical :: xhist=.FALSE. 
     character(len=short_len) :: label='x-label' !< character string variable

variable length arrays of strings and objects cannot be defined as pointers and must be defined as allocatable, they can also be allocated using the appropriate IXFalloc subroutines

     character(len=long_len),allocatable :: cell_string(:) 
     type(IXTdataset_2d), allocatable :: d2d(:)
  end type IXTtestclass

include the interfaces required by routines declared in class_base.f90, this is covered in more detail in Preprocessing class_header.f90

#define IXD_TYPE testclass
#include "class_header.f90"

non-standard interfaces are defined, as well as any private functions/subroutines, all subroutines/functions are public unless specified as private

  interface IXFplus
     module procedure IXFplus_testclass
  end interface IXFplus

contains marks the start of the module subroutines, the first section includes the generic subroutines every class requires, this is covered in more detail in Preprocessing class_base.f90

contains
#define IXD_DESCRIPTION	"IXTtestclass class"
#define IXD_TYPE testclass
#define IXD_SQTYPE 'testclass'
#include "class_base.f90"

IXFoperation_run_testclass

All classes must provide this operation; it loops through all members of the class doing the supplied operation, eg matlab_write, display etc..

There are three different types of IXFoperation_run call, with a standard argument call

  1. single value and static array elements and simple nested objects use IXFoperation_run
  2. pointer arrays elements IXFoperation_run_ptr
  3. allocatable type elements IXFoperation_run_alloc

this code should be used as a template with the appropriate IXFoperation_run call made for each member element, substituting appropriately for the name of the class and its member elements


  recursive subroutine IXFoperation_run_testclass(op, field, arg, status)
    implicit none
    type(IXTtestclass) :: arg
    type(IXToperation) :: op
    type(IXTstatus) :: status
    character(len=*) :: field
    logical::cont_op
    call IXFoperationStart(op, 'IXTtestclass', field, status,arg%base,cont_op)
    if(.not. cont_op)return
    ! this order must match the declaration order in matlab as it is
    ! used when parsing argemnts passed in class creation with vargin
    call IXFoperation_run(op, 'base', arg%base, status)
    call IXFoperation_run(op, 'val', arg%val, status)
    call IXFoperation_run(op, 'nx', arg%nx, status)
    call IXFoperation_run(op, 'val_static', arg%val_static, status)
    call IXFoperation_run(op, 'int_static', arg%int_static, status)
    call IXFoperation_run_ptr(op, 'val_array', arg%val_array, status)
    call IXFoperation_run_ptr(op, 'int_array', arg%val_array, status)    
    call IXFoperation_run(op, 'spectra', arg%spectra, status)
    call IXFoperation_run(op, 'xhist', arg%xhist, status)
    call IXFoperation_run(op, 'label', arg%label, status)
    call IXFoperation_run_alloc(op, 'cell_string', arg%cell_string, status)
    call IXFoperation_run_alloc(op, 'd2d', arg%d2d, status)
    call IXFoperationFinish(op, field, status)
  end subroutine IXFoperation_run_testclass
 

IXFcreate_testclass

  • The IXFcreate subroutine STRICTLY takes all the elements required to define a class and creates the resulting object.
  • If an element of the object is another class then it MUST be initialised.
  • pointer array types are defined as assumed-shape arrays.
  • Each element is defined with intent(in), allocatable elements are defined as assumed-shape arrays.
  • Any nested objects need to be tested for validity
  • The object to be created must be marked as valid before the IXFset subroutine is called
   subroutine IXFcreate_testclass(arg, val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d, status)
    implicit none
    type(IXTtestclass),intent(out) :: arg
    type(IXTstatus),intent(inout) :: status
     real(dp),intent(in) :: val    
     integer(i4b),intent(in) :: nx 
     real(dp),intent(in) :: val_static(3) 
     integer(i4b),intent(in)::int_static(4)
     real(dp),intent(in) :: val_array(:)   
     integer(i4b),intent(in) :: int_arr(:,:)               
     type(IXTspectra),intent(in):: spectra 
     logical,intent(in) :: xhist 
     character(len=short_len),intent(in) :: label
     character(len=long_len),intent(in) :: cell_string(:) 
     type(IXTdataset_2d),intent(in) :: d2d(:)


    ! nested objects should be tested for initialisation, this shows they have been created properly
   
    if( IXFvalid(spectra) .neqv. .true.)then
            call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
            IXCerr_outofmem, 'IXTspectra failure, all nested objects MUST be initialised (IXFcreate_testclass)')
    endif
    if(status == IXCseverity_error)return
    ! the set routine can ONLY be called on an initialised object
    ! so in this *special* case it is initialised before it is filled
    call IXFmark_valid(arg)
       
    call IXFset_testclass(arg,status,val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d)    
    
  end subroutine IXFcreate_testclass
  
  

IXFdestroy_testclass

The IXFdestroy routine deallocates any pointer arrays in the type, and calls the destroy function on any nested objects, it can also be used to set variables back to their default values. If one of the objects is an array of structures, then each structure will be recursively destroyed by the IXFdealloc function. The allocatable array of strings is a fortran type and so the IXFdeallocfortran subroutine must be called to deallocate its memory

 
  subroutine IXFdestroy_testclass(arg, status)
    implicit none
    type(IXTtestclass),intent(inout) :: arg
    type(IXTstatus),intent(inout) :: status
    
    call IXFdestroy(arg%base,status)
! destroy pointer arrays
    call IXFdealloc(arg%val_array,status)    
    call IXFdealloc(arg%int_arr,status)
       
    arg%xhist=.FALSE.
    ! for nested objects check it hasn't been destroyed already
    if(IXFvalid(arg%spectra))call IXFdestroy(arg%spectra,status)
    if(allocated(arg%cell_string))call IXFdeallocfortran(arg%cell_string,status)
    if(IXFvalid(arg%d2d))then
       if(allocated(arg%d2d))call IXFdealloc(arg%d2d,status)
    endif
    ! the initialised status is now revoked for the object
    ! this statement MUST exist in all destroy routines
    call IXFclear_valid(arg)
    
  end subroutine IXFdestroy_testclass
 

IXFcheck_testclass

IXFcheck will make internal consistency checks in the object, such as array length checking to make sure the object is properly filled.

  subroutine IXFcheck_Testclass(arg, status)
    implicit none
    type(IXTtestclass),intent(in) ::arg
    type(IXTstatus),intent(inout) :: status
    if (size(arg%val_array) /= size(arg%int_arr,1)) then
       call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
            IXCerr_invparam, 'sizes of value and int arrays do not match(IXFcheck_testclass)')
    endif
  end subroutine IXFcheck_Testclass
 

IXFset_testclass

The IXFset operation can only be performed on a properly filled or initialised object.

  • It takes an optional number of arguments to modify the object contents.
  • A check is made at the end to determine that the edited object is correctly formed.
  • Error flags are raised if there is any inconsistency.
  • The optional arguments MUST always be specified by keywords, and declared as intent(in).
  • The order of arguments should match the order of declaration, except for the IXTbase type which is not declared.
  • The 'ref' argument is used to copy the values of a reference object to another. In this case the object being modified does not have to be initialised, but the reference object MUST be initialised.


  recursive subroutine IXFset_testclass(arg, status, val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d,ref)
    implicit none
    type(IXTtestclass),intent(inout) :: arg
    type(IXTstatus),intent(inout) :: status
    ! all the supplied variables are declared as optional with intent(in)
    type(IXTtestclass),intent(in),optional :: ref
     real(dp),optional,intent(in) :: val    
     integer(i4b),optional,intent(in) :: nx 
     real(dp),optional,intent(in) :: val_static(3)  
     integer(i4b),optional,intent(in)::int_static(4)  
     real(dp),optional,intent(in) :: val_array(:)  
     integer(i4b),optional,intent(in) :: int_arr(:,:)                 
     type(IXTspectra),optional,intent(in):: spectra 
     logical,optional,intent(in) :: xhist 

input strings are treated as an unknown length, if the supplied string is longer than the declared length then it will be truncated. If it is shorter, then the new variable will be padded with spaces.

     character(len=*),optional,intent(in) :: label 
     character(len=*),optional,intent(in) :: cell_string(:) 
     type(IXTdataset_2d),optional,intent(in) :: d2d(:)

check that either the reference object is initialised or that object to be modified is initialised

    if(present(ref))then
       if (IXFvalid(ref) .neqv. .true.)then
           call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
                IXCerr_invparam, 'Reference object MUST be initialised (IXFset_testclass)')
       endif
       if(status == IXCseverity_error)return
       ! now initialise object to be modified, not necessary to check its value
       call IXFmark_valid(arg)
    else    
       if(IXFvalid(arg) .neqv. .true.) then
           call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
                IXCerr_invparam, 'Set can only be called on an initialised object (IXFset_testclass)')
       endif
       if(status == IXCseverity_error)return
    endif
 

This command will copy all of the attributes of the reference object to the object being modified it calls set with all the components of the reference object

   if (present(ref))call IXFset_testclass(arg,status,ref%val,ref%nx,ref%val_static,ref%int_static,ref%val_array, &
                        ref%int_arr,ref%spectra,ref%xhist,ref%label,ref%cell_string,ref%d2d)

single variables are simply overwritten by the supplied variables

    if (present(val))arg%val=val
    if (present(nx))arg%nx=nx

static arrays are simply copied, if the length of val_static is not 3, then the program will break at runtime, a check cannot be made for this

    if (present(val_static)) arg%val_static=val_static
    if (present(int_static)) arg%int_static=int_static

IXFset_real_array and IXFset_integer_array are built in functions which reallocate the memory for the member element and fill with the new data as appropriate, they work on all pointer arrays up to 3d

    call IXFset_real_array(arg%val_array,status,val_array)        
    call IXFset_integer_array(arg%int_arr,status,int_arr)    
    

nested objects are simply copied

    if (present(spectra))call IXFcopy(spectra,arg%spectra,status)
 

logicals and strings are treated in the same way as single variables

    if(present(xhist))arg%xhist=xhist
    if(present(label))arg%label=label

allocatable types must follow this format, including reallocation and population of the element, either by direct assignment or in the case of an object by an IXFcopy call

    
    if (present(cell_string))then
       call IXFreallocFortran(arg%cell_string,size(cell_string),.false.,status)
       arg%cell_string=cell_string
    endif    

    if(present(d2d))then
        call IXFrealloc(arg%d2d,size(d2d),.false.,status)
        call IXFcopy(d2d,arg%d2d,status)
    endif

the check routine MUST always be called at the end of the set routine

    call IXFcheck(arg,status)
    
  end subroutine IXFset_testclass
 

IXFget_testclass

The IXFget subroutine will return elements of an object to an optional supplied arrays/variables.

  • The supplied variables should be referrred to by keyword to avoid errors.
  • The 'wout' variable is special and can be used to copy the contents of a whole object to a new one.
  • all the supplied variables are declared as optional with intent(out)
  • static arrays are expected to be the length of the member element
  subroutine IXFget_testclass(arg,status, val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d,wout)
    implicit none
    type(IXTtestclass),intent(in) :: arg
    type(IXTtestclass),optional,intent(out)::wout
    type(IXTstatus),intent(inout) :: status
     real(dp),optional,intent(out) :: val   
     integer(i4b),optional,intent(out) :: nx 
     real(dp),optional,intent(out) :: val_static(3) 
     integer(i4b),optional,intent(out)::int_static(4)
     real(dp),optional,intent(out) :: val_array(:) 
     integer(i4b),optional,intent(out) :: int_arr(:,:)                
     type(IXTspectra),optional,intent(out):: spectra 
     logical,optional,intent(out) :: xhist 

input strings are treated as an unknown length, if the supplied string is longer than the declared length then it will be truncated. If it is shorter, then the new variable will be padded with spaces.

     character(len=*),optional,intent(out) :: label 
     character(len=*),optional,intent(out) :: cell_string(:)
     type(IXTdataset_2d),optional,intent(out) :: d2d(:)

a simple copy call is made for the wout argument

    if (present(wout))then
      call IXFcopy(arg,wout,status)
    endif    

single variables are copied into the supplied arrays

    if (present(val))val=arg%val
    if (present(nx))nx=arg%nx

for variable length arrays the supplied array must be the same length as the object array. This test is made and the array filled by the built in subroutines IXFget_real_array and IXFget_integer_array.

    call IXFget_real_array(arg%val_array,status,val_array)
    call IXFget_integer_array(arg%int_arr,status,int_arr)

checks are not made on static arrays, since the routine will break at runtime if a supplied array is not the same length as that declared

    if (present(val_static))val_static=arg%val_static
    if (present(int_static))int_static=arg%int_static

supplied nested objects are filled with an appropriate IXFcopy subroutine

    if(present(spectra))call IXFcopy(arg%spectra,spectra,status)

logicals and strings are treated as single variables, with strings being truncated where appropriate

    if(present(xhist))xhist=arg%xhist
    if(present(label))label=arg%label

the arrays which are to contain the allocatable types are assumed to be of an appropriate length, if the length is unknown then the IXFget_alloc subroutine must be called instead

    if (present(cell_string))cell_string=arg%cell_string    
    if(present(d2d))call IXFcopy(arg%d2d,d2d,status)

    
  end subroutine IXFget_testclass
   

IXFget_alloc_testclass

IXFget_alloc can be called with all the same arguments as IXFget, but the pointer array elements/string array/object array elements can be allocatable arrays. the arrays are allocated to the appropriate length and IXFget is called underneath to populate them.

  subroutine IXFget_alloc_testclass(arg,status, val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d,wout)
    implicit none
    type(IXTtestclass),intent(in) :: arg
    type(IXTtestclass),intent(out),optional::wout
     real(dp),optional,intent(out) :: val 
     integer(i4b),optional,intent(out) :: nx 
     real(dp),optional,intent(out) :: val_static(3)
     integer(i4b),optional,intent(out)::int_static(4) 
     !allocatable arrays cannot be defined as intent(out), this tends to make runtime errors for some reason
     real(dp),optional,allocatable :: val_array(:) 
     integer(i4b),optional,allocatable :: int_arr(:,:)       
     type(IXTspectra),optional,intent(out):: spectra 
     logical,optional,intent(out) :: xhist 

input strings are treated as an unknown length, if the supplied string is longer than the declared length then it will be truncated. If it is shorter, then the new variable will be padded with spaces.

     character(len=*),optional,intent(out) :: label
    !allocatable arrays cannot be defined as intent(out), this tends to make runtime errors for some reason
     character(len=*),allocatable,optional :: cell_string(:)
     type(IXTdataset_2d),allocatable,optional :: d2d(:) 
    type(IXTstatus),intent(inout)::status

    if(present(val_static))then
       call IXFreallocfortran(val_static,3,.false.,status)
    endif
    if(present(int_static))then
       call IXFreallocfortran(int_static,4,.false.,status)
    endif

! allocate the appropriate allocatable arrays then call the standard get function
    if (present(val_array))then
       call IXFreallocdimsFortran(val_array,shape(arg%val_array),.false.,status)
    endif
    
    if (present(int_arr))then
       call IXFreallocdimsFortran(int_arr,shape(arg%int_arr),.false.,status)
    endif

    if (present(cell_string))then
       call IXFreallocdimsFortran(cell_string,(/ size(arg%cell_string) /),.false.,status)
    endif

    if(present(d2d))then
       call IXFrealloc(d2d,size(arg%d2d),.false.,status)
    endif
    
    call IXFget_testclass(arg,status,   val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d,wout)

  end subroutine IXFget_alloc_testclass

 

IXFget_ptr_testclass

!-----------------------------------------------------------------------------------------------------------------------
!> IXFget_ptr will return a pointer to a structure or an array, from an optional argument.
!! The pointer arguments are the same name as the object elements they are pointing to.
!! EXTREME Care must be taken since if the pointers are edited, then the data in the structure will also be edited.
  subroutine IXFget_ptr_testclass(arg,val_array,int_arr,spectra)
    implicit none
    type(IXTtestclass),intent(in),target :: arg
    type(IXTspectra),optional,pointer::spectra
    real(dp),optional,pointer::val_array(:)
    integer(i4b),optional,pointer:: int_arr(:,:)

    if (present(spectra))spectra=>arg%spectra
    
    if (present(val_array))val_array=>arg%val_array
    if (present(int_arr))int_arr=>arg%int_arr

  end subroutine IXFget_ptr_testclass




!!-----------------------------------------------------------------------------------------------------------------------
!> This is an example plus operation for the IXTtestclass object
  subroutine IXFplus_testclass(wres, w1, w2, status)
    ! now add the objects
    type(IXTtestclass) :: w1, w2, wres
    type(IXTstatus) :: status
    
    ! to do this subroutine there should also be a binary operation setup which checks that
    ! the arrays of the objects are the same length etc......
    wres%val = w1%val + w2%val
    wres%nx = w1%nx + w2%nx
    wres%val_static=w1%val_static+w2%val_static
    wres%int_static=w1%int_static+w2%int_static
    
    call IXFalloc(wres%val_array, size(w1%val_array), status)
    call IXFallocdims(wres%int_arr, shape(w1%int_arr) , status)
 
    wres%val_array = w1%val_array + w2%val_array
    wres%int_arr= w1%int_arr + w2%int_arr    
    call IXFcopy(w1%spectra,wres%spectra,status)
    wres%xhist=.true.
    
    wres%label='new label'
    
    if(size(w1%cell_string) /= size(w2%cell_string))then
        call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
            IXCerr_outofmem, 'cell_string failure in operation (IXFplus_testclass)')
        return
    else    
        call IXFallocfortran(wres%cell_string,size(w1%cell_string),status)
        wres%cell_string=w1%cell_string
    endif

    if(size(w1%d2d) /= size(w2%d2d))then
        call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
            IXCerr_outofmem, 'd2d failure in operation (IXFplus_testclass)')
        return
    else    
        call IXFalloc(wres%d2d,size(w1%d2d),status)
        do i=1,size(w1%d2d)
            call IXFplus(wres%d2d(i),w1%d2d(i),w2%d2d(i),status)
        enddo
    endif
    
  end subroutine IXFplus_testclass
    
end module IXMtestclass

Preprocessing class_header.f90

before the contains statement we

Preprocessing class_base.f90

we include many subroutines automatically