Difference between revisions of "Libclasses"

From LIBISIS
Jump to navigation Jump to search
m
 
Line 1: Line 1:
<b>Programming the Classes Library</b>
 
 
_*Memory Management*_
 
Large array objects in types are declared as FORTRAN pointers to arrays; this is done so that we can share memory between matlab and fortran in the fortran interface. If you need to allocate such an array in code that might be called from the matlab interface, you must use the [IXFalloc] function which allocates MATLAB memory and then associates the fortran pointer with this. If you need memory temporarily within a subroutine and will not be returning it to matlab, you can instead use the standard fortran ALLOCATE statement.
 
 
_*Adding a New Type and Operations*_
 
 
The IXMtestclass module is defined in the *'IXMtestclass.f90'* file, which should be consulted concurrently with this page.  The IXTtestclass type is defined in the first section of the module, it contains examples of all the currently used types of variable (real and integer single variables and arrays, character strings, logicals and nested structures; allocatable arrays of structures are a simple extension).  Below the type definition the #define and #include lines direct the preprocessor to include the *'class_header.f90'* file which contains interface statements to all the standard subroutines of the module which are included in the file *'class_base.f90'*.  These are used for generating template code to save on “cut and paste”. The 'contains' line defines the section of the module which holds the module specific subroutines.
 
 
 
 
<pre>
 
<pre>
 +
!-----------------------------------------------------------------------------------------------------------------------------------
 +
! MODULE: IXMtestclass
 +
!-----------------------------------------------------------------------------------------------------------------------------------
 +
!> \file IXMtestclass.f90
 +
!!
 +
!! @author Freddie Akeroyd, ISIS
 +
!! @version $Revision: 1361 $ ($Date: 2008-05-09 17:41:14 +0100 (Fri, 09 May 2008) $)
 +
!!
 +
!! Example of a class module
 +
!!
 +
!! 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
 +
!!
 +
!! All classes must define the module function the implements the IXFoperationRun
 +
!! interface
 +
!!
 +
!> @{
 
module IXMtestclass
 
module IXMtestclass
! include the modules which are relied upon by the module
+
! include the modules which are relied upon by the module
  use IXMtype_definitions
 
 
   use IXMbase
 
   use IXMbase
 
   use IXMspectra
 
   use IXMspectra
!declare the type which is public
+
  use IXMdataset_2d
   type IXTtestclass
+
!> IXTtestclass is an object which contains all the types of variables which exist in the framework, it also
     private ! all component elements of the module are private
+
! contains implementations of all the standard subroutines which MUST be included by any new module which is
     type(IXTbase) :: base
+
! added to the framework. In each of these routines there are particular ways of treating each different
     real(dp) :: val    !! real variable
+
! type of variable, and the code in IXTtestclass can be used as a model for other modules, containing all the
     integer(i4b) :: nx !! integer variable
+
! standard code required.
     real(dp), pointer :: val_array(:) => NULL()   !!  variable length 1d real array, always declared as NULL by default
+
   type IXTtestclass  
     real(dp), pointer :: err_array(:) => NULL()    !! variable length 1d real array, always declared as NULL by default
+
     private ! all component elements of the module are private and encapsulated
    real(dp) :: val_stat(3)=0.0 !! static real array
+
    ! all objects have a base class
     integer(i4b),pointer :: int_arr(:,:)=>NULL() !! variable length 2d integer array, always declared as NULL by default
+
     type(IXTbase) :: base !< base class object
     type(IXTspectra):: spectra !! nested object
+
     real(dp) :: val    !< real variable
     logical :: xhist=.FALSE. !! logical variable
+
     integer(i4b) :: nx !< integer variable
     character(len=short_len) :: label='x-label' !! character string variable
+
     real(dp) :: val_static(3)=0.0 !< static real array
 +
    integer(i4b)::int_static(4)=0.0 !< static integer array
 +
     real(dp), pointer :: val_array(:) => NULL()    !< variable length 1d real array, always declared as NULL by default
 +
     integer(i4b),pointer :: int_arr(:,:)=>NULL() !< variable length 2d integer array, always declared as NULL by default              
 +
     type(IXTspectra):: spectra !< nested object
 +
     logical :: xhist=.FALSE. !< logical variable
 +
     character(len=short_len) :: label='x-label' !< character string variable
 +
    character(len=long_len),allocatable :: cell_string(:) !< allocatable array of strings
 +
    type(IXTdataset_2d), allocatable :: d2d(:) !<allocatable array of objects
 
   end type IXTtestclass
 
   end type IXTtestclass
  
!! include the interfaces required by routines declared in class_base.f90
+
 
 +
!> \name pubint
 +
!! Public interfaces
 +
!! @{
 +
! include the interfaces required by routines declared in class_base.f90
 
#define IXD_TYPE testclass
 
#define IXD_TYPE testclass
 
#include "class_header.f90"
 
#include "class_header.f90"
  
 +
!> interface to the IXFplus function
 +
  interface IXFplus
 +
    module procedure IXFplus_testclass
 +
  end interface IXFplus
 +
! finish public interfaces
 +
!> @}
 
contains
 
contains
  
Line 41: Line 74:
 
#define IXD_TYPE testclass
 
#define IXD_TYPE testclass
 
#define IXD_SQTYPE 'testclass'
 
#define IXD_SQTYPE 'testclass'
 
 
#include "class_base.f90"
 
#include "class_base.f90"
</pre>
 
 
 
 
 
To add a new type IXT{your_module_name} you should
 
*Create a module IXM{your_module_name} to define type IXT{your_module_name} in file IXM{your_module_name}.f90
 
*Include “class_header.f90” and “class_base.f90” at the appropriate points with IXD_TYPE set to the name of the type (with the IXT removed) and IXD_DESCRIPTION to the text describing the clas
 
 
Subroutines which must be included in the new module. All descriptions below use IXMtestclass as an example.
 
 
<b>IXFoperation_run_{your_module_name}</b>
 
 
When manipulating class objects you often need to loop through and apply the same operation to all of the data members of that object. The IXFoperation_run interface splits what you want to do (IXFoperation_make) from the looping through of the class members (IXFOperation_run_Testclass). The advantage of this is that you only need to set up the “loop through the class” once and not for everything that you might want to do; also if you were to change the class layout you only have one function to change. We will ignore IXFoperation_make and just show IXFoperation_run_testclass as the “operation run” function is what you must provide for each class.
 
  
<pre>
+
!> All classes must provide this operation; it loops through
!-----------------------------------------------------------------------------------------------------------------------
+
!! all members of the class doing the supplied operation
  ! All classes must provide this operation; it loops through
 
  ! all members of the class doing the supplied operation
 
 
   recursive subroutine IXFoperation_run_testclass(op, field, arg, status)
 
   recursive subroutine IXFoperation_run_testclass(op, field, arg, status)
 
     implicit none
 
     implicit none
Line 68: Line 84:
 
     type(IXTstatus) :: status
 
     type(IXTstatus) :: status
 
     character(len=*) :: field
 
     character(len=*) :: field
     call IXFoperationStart(op, 'IXTtestclass', field, status)
+
    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
 
     ! this order must match the declaration order in matlab as it is
 
     ! used when parsing argemnts passed in class creation with vargin
 
     ! used when parsing argemnts passed in class creation with vargin
Line 74: Line 92:
 
     call IXFoperation_run(op, 'val', arg%val, status)
 
     call IXFoperation_run(op, 'val', arg%val, status)
 
     call IXFoperation_run(op, 'nx', arg%nx, status)
 
     call IXFoperation_run(op, 'nx', arg%nx, status)
     call IXFoperation_run_ptr(op, 'val_array', arg%val_array, status) ! this is a pointer array so the ptr suffix is used
+
     call IXFoperation_run(op, 'val_static', arg%val_static, status)
     call IXFoperation_run_ptr(op, 'err_array', arg%err_array, status) ! this is a pointer array so the ptr suffix is used
+
     call IXFoperation_run(op, 'int_static', arg%int_static, status)
     call IXFoperation_run(op, 'val_stat', arg%val_stat, status)
+
     call IXFoperation_run_ptr(op, 'val_array', arg%val_array, status)!this is a pointer array so the ptr suffix is used
     call IXFoperation_run_ptr(op, 'int_arr', arg%int_arr, status) ! this is a pointer array so the ptr suffix is used
+
     call IXFoperation_run_ptr(op, 'int_array', arg%val_array, status)!this is a pointer array so the ptr suffix is used    
 
     call IXFoperation_run(op, 'spectra', arg%spectra, status)
 
     call IXFoperation_run(op, 'spectra', arg%spectra, status)
 
     call IXFoperation_run(op, 'xhist', arg%xhist, status)
 
     call IXFoperation_run(op, 'xhist', arg%xhist, status)
     call IXFoperation_run(op, 'xlabel', arg%label, 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)
 
     call IXFoperationFinish(op, field, status)
 
   end subroutine IXFoperation_run_testclass
 
   end subroutine IXFoperation_run_testclass
</pre>
+
 
As you see, all we do is re-apply the same IXFoperation_run interface function to each data member. The hierarchy will be descended recursively until a simple type (i.e. integer, real array etc) is reached – then the appropriate final function is called (e.g. runOperationInteger) to do the real work. See the IXMoperation module for details of these final functions. Currently matlab reading, writing and printing to the screen (display) are all handled via this general mechanism – later file access will be added through it.
 
 
 
 
 
<b>IXFcreate_{your_module_name}</b>
 
<pre>
 
 
!-----------------------------------------------------------------------------------------------------------------------
 
!-----------------------------------------------------------------------------------------------------------------------
!!The IXFcreate subroutine STRICTLY takes all the elements required to define a class and creates the resulting
+
!> 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.
+
!! object. If an element of the object is another class then it MUST be initialised.  
   subroutine IXFcreate_testclass(arg, base, val, nx, val_array, err_array,val_stat, int_arr,spectra, xhist,label, status)
+
   subroutine IXFcreate_testclass(arg, val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d, status)
 
     implicit none
 
     implicit none
     type(IXTtestclass) :: arg
+
     type(IXTtestclass),intent(out) :: arg
     type(IXTstatus) :: status
+
     type(IXTstatus),intent(inout) :: status
    type(IXTbase)::base
+
    real(dp),intent(in) :: val    !< real variable
    real(dp),intent(in):: val,val_stat(3)
+
    integer(i4b),intent(in) :: nx !< integer variable
    real(dp),intent(in) :: val_array(:), err_array(:)
+
    real(dp),intent(in) :: val_static(3) !< static real array
    integer(i4b),intent(in):: nx,int_arr(:,:)
+
    integer(i4b),intent(in)::int_static(4) !< static integer array
    type(IXTspectra),intent(in):: spectra
+
    real(dp),intent(in) :: val_array(:)   !<  variable length 1d real array, always declared as NULL by default
    logical,intent(in) :: xhist
+
    integer(i4b),intent(in) :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default             
    character(len=*) :: label
+
    type(IXTspectra),intent(in):: spectra !< nested object
 +
    logical,intent(in) :: xhist !< logical variable
 +
    character(len=short_len),intent(in) :: label !< character string variable
 +
    character(len=long_len),intent(in) :: cell_string(:) !< allocatable array of strings
 +
    type(IXTdataset_2d),intent(in) :: d2d(:) !<allocatable array of objects
 +
 
  
 
     ! nested objects should be tested for initialisation, this shows they have been created properly
 
     ! nested objects should be tested for initialisation, this shows they have been created properly
 
+
 
     if( IXFinitialised(spectra) /= .true.)then
+
     if( IXFvalid(spectra) .neqv. .true.)then
 
             call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
 
             call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
             IXCerr_outofmem, 'IXTspectra failure, all nested objects MUST be initialised (IXFset_testclass)')
+
             IXCerr_outofmem, 'IXTspectra failure, all nested objects MUST be initialised (IXFcreate_testclass)')
 
     endif
 
     endif
 
+
     if(status == IXCseverity_error)return
!-----------------------------------------------------------------------------------------------------------------------
 
     !should IXTbase be an argument to create....
 
!-----------------------------------------------------------------------------------------------------------------------
 
 
 
 
     ! the set routine can ONLY be called on an initialised object
 
     ! the set routine can ONLY be called on an initialised object
     ! so in this SPECIAL case it is initialised before it is filled
+
     ! so in this *special* case it is initialised before it is filled
     call IXFmark_init(arg)
+
     call IXFmark_valid(arg)
    ! set is called with all the components of the object
+
     
     call IXFset_testclass(arg,status,base,val, nx, val_array, err_array,val_stat, int_arr,spectra, xhist,label)
+
     call IXFset_testclass(arg,status,val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d)  
     !check is called in the set routine
+
      
 
+
   end subroutine IXFcreate_testclass
   end subroutine IXFCreate_testclass
+
 
</pre>
+
 
 
 
<b>IXFdestroy_{your_module_name}</b>
 
<pre>
 
 
!-----------------------------------------------------------------------------------------------------------------------
 
!-----------------------------------------------------------------------------------------------------------------------
  !!IXFdestroy routine deallocates any pointer arrays in the type, and calls the destroy function on any nested
+
!>The IXFdestroy routine deallocates any pointer arrays in the type, and calls the destroy function on any nested
   !!objects, could also be used to set variables back to their default values.
+
   !!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
 
   !!If one of the objects is an array of structures, then each structure will be recursively destroyed by the
 
   !!IXFdealloc function.
 
   !!IXFdealloc function.
Line 135: Line 148:
 
     implicit none
 
     implicit none
 
     type(IXTtestclass),intent(inout) :: arg
 
     type(IXTtestclass),intent(inout) :: arg
     type(IXTstatus) :: status
+
     type(IXTstatus),intent(inout) :: status
 
+
   
     call IXFdealloc(arg%val_array,status)
+
     call IXFdestroy(arg%base,status)
     call IXFdealloc(arg%err_array,status)
+
! destroy pointer arrays
 +
     call IXFdealloc(arg%val_array,status)  
 
     call IXFdealloc(arg%int_arr,status)
 
     call IXFdealloc(arg%int_arr,status)
 +
     
 +
    arg%xhist=.FALSE.
 
     ! for nested objects check it hasn't been destroyed already
 
     ! for nested objects check it hasn't been destroyed already
     ! IXFinitialised is valid for an array of structures, allocatable or static
+
     if(IXFvalid(arg%spectra))call IXFdestroy(arg%spectra,status)
     if(IXFinitialised(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
 
     ! the initialised status is now revoked for the object
 
     ! this statement MUST exist in all destroy routines
 
     ! this statement MUST exist in all destroy routines
     call IXFclear_init(arg)
+
     call IXFclear_valid(arg)
 
+
   
 
   end subroutine IXFdestroy_testclass
 
   end subroutine IXFdestroy_testclass
</pre>
 
  
<b>IXFcheck_{your_module_name}</b>
 
<pre>
 
 
!-----------------------------------------------------------------------------------------------------------------------
 
!-----------------------------------------------------------------------------------------------------------------------
!! IXFcheck will make internal consistency checks in the object, such as array length checking to make
+
!> IXFcheck will make internal consistency checks in the object, such as array length checking to make
 
!! sure the object is properly filled.
 
!! sure the object is properly filled.
   subroutine IXFcheck_testclass(arg, status)
+
   subroutine IXFcheck_Testclass(arg, status)
 
     implicit none
 
     implicit none
     type(IXTtestclass) ::arg
+
     type(IXTtestclass),intent(in) ::arg
     type(IXTstatus) :: status
+
     type(IXTstatus),intent(inout) :: status
     if (size(arg%val_array) /= size(arg%err_array)) then
+
     if (size(arg%val_array) /= size(arg%int_arr,1)) then
 
       call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
 
       call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
             IXCerr_outofmem, 'lengths of value and error arrays do not match(IXFcheck_testclass)')
+
             IXCerr_invparam, 'sizes of value and int arrays do not match(IXFcheck_testclass)')
 
     endif
 
     endif
   end subroutine IXFcheck_testclass
+
   end subroutine IXFcheck_Testclass
</pre>
 
  
<b>IXFset_{your_module_name}</b>
 
<pre>
 
 
!-----------------------------------------------------------------------------------------------------------------------
 
!-----------------------------------------------------------------------------------------------------------------------
!!The IXFset operation can only be performed on a properly filled or initialised object. It takes an optional number of
+
!>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
 
!! 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.
 
!! formed. Error flags are raised if there is any inconsistency. The optional arguments must always be specified by keywords.
!! The order of arguments should match the order of declaration. The 'ref' argument is used to copy the values of a
+
!! The order of arguments should match the order of declaration, except for the IXTbase type which is not declared.
!! reference object to another. In this case the object being modified does not have to be initialised, but the refence object
+
!! The 'ref' argument is used to copy the values of a reference object to another. In this case the object being modified  
!! must be initialised instead.
+
!! does not have to be initialised, but the reference object MUST be initialised.
   recursive subroutine IXFset_testclass(arg, status,base, val,nx, val_array, err_array, val_stat, int_arr, spectra, xhist, label, ref)
+
   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
 
     implicit none
     type(IXTtestclass) :: arg
+
     type(IXTtestclass),intent(inout) :: arg
     type(IXTstatus) :: status
+
     type(IXTstatus),intent(inout) :: status
 
     ! all the supplied variables are declared as optional with intent(in)
 
     ! all the supplied variables are declared as optional with intent(in)
 
     type(IXTtestclass),intent(in),optional :: ref
 
     type(IXTtestclass),intent(in),optional :: ref
    type(IXTbase),intent(in),optional::base
+
    real(dp),optional,intent(in) :: val    !< real variable
    real(dp),intent(in),optional:: val,  val_stat(3)
+
    integer(i4b),optional,intent(in) :: nx !< integer variable
    real(dp),intent(in),optional :: val_array(:), err_array(:)
+
    real(dp),optional,intent(in) :: val_static(3) !< static real array
    integer(i4b),intent(in),optional::int_arr(:,:),nx
+
    integer(i4b),optional,intent(in)::int_static(4) !< static integer array
    type(IXTspectra),intent(in),optional:: spectra
+
    real(dp),optional,intent(in) :: val_array(:) !<  variable length 1d real array, always declared as NULL by default
    logical,intent(in), optional:: xhist
+
    integer(i4b),optional,intent(in) :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default             
 +
    type(IXTspectra),optional,intent(in):: spectra !< nested object
 +
    logical,optional,intent(in) :: xhist !< logical variable
 
     ! input strings are treated as an unknown length, if the supplied string is longer than the declared length
 
     ! 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.
 
     ! 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) :: label !< character string variable
 
+
    character(len=*),optional,intent(in) :: cell_string(:) !< array of strings
 
+
    type(IXTdataset_2d),optional,intent(in) :: d2d(:) !<array of objects
 +
 
 
   ! check that either the reference object is initialised
 
   ! check that either the reference object is initialised
 
   ! or that object to be modified is initialised
 
   ! or that object to be modified is initialised
 
     if(present(ref))then
 
     if(present(ref))then
       if (IXFinitialised(ref) /= .true.)then
+
       if (IXFvalid(ref) .neqv. .true.)then
 
           call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
 
           call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
                 IXCerr_outofmem, 'Reference object MUST be initialised (IXFset_testclass)')
+
                 IXCerr_invparam, 'Reference object MUST be initialised (IXFset_testclass)')
 
       endif
 
       endif
 
       if(status == IXCseverity_error)return
 
       if(status == IXCseverity_error)return
 
       ! now initialise object to be modified, not necessary to check its value
 
       ! now initialise object to be modified, not necessary to check its value
       call IXFinitialize(arg)
+
       call IXFmark_valid(arg)
     else
+
     else  
       if(IXFinitialised(arg) /= .true.) then
+
       if(IXFvalid(arg) .neqv. .true.) then
 
           call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
 
           call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
                 IXCerr_outofmem, 'Set can only be called on an initialised object (IXFset_testclass)')
+
                 IXCerr_invparam, 'Set can only be called on an initialised object (IXFset_testclass)')
 
       endif
 
       endif
 
       if(status == IXCseverity_error)return
 
       if(status == IXCseverity_error)return
 
     endif
 
     endif
 
+
 
 
   ! This command will copy all of the attributes of the reference object to the object being modified
 
   ! This command will copy all of the attributes of the reference object to the object being modified
     if (present(ref))call IXFset_testclass(arg,status,ref%base,ref%val,ref%nx,ref%val_array,ref%err_array, &
+
  ! it calls set with all the components of the reference object
                         ref%val_stat,ref%int_arr,ref%spectra,ref%xhist)
+
     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)
  ! for nested objects the set command of the nested object is called, equally valid is a call to IXFcopy
+
 
  ! call IXFcopy(base,arg%base,status)
 
  ! which in turn will make the same call to IXFset_base
 
    if (present(base))call IXFset_base(arg%base,status,ref=base)
 
 
 
 
     ! single variables are simply overwritten by the supplied variables
 
     ! single variables are simply overwritten by the supplied variables
 
     if (present(val))arg%val=val
 
     if (present(val))arg%val=val
 
     if (present(nx))arg%nx=nx
 
     if (present(nx))arg%nx=nx
    ! 1D variable length arrays
+
 
    ! IXFrealloc is called to make the modified array the same length as the supplied array
 
    ! if the array pointer is not allocated, then allocation is performed
 
    call IXFset_real_array(arg%val_array,status,val_array)
 
    ! as above
 
    call IXFset_real_array(arg%err_array,status,err_array)
 
 
     ! static arrays are simply copied
 
     ! static arrays are simply copied
 
     ! if the length of val_stat is not 3, then the program will break at
 
     ! if the length of val_stat is not 3, then the program will break at
 
     ! runtime, a check cannot be made for this
 
     ! runtime, a check cannot be made for this
     if (present(val_stat)) arg%val_stat=val_stat
+
     if (present(val_static)) arg%val_static=val_static
 
+
     if (present(int_static)) arg%int_static=int_static
    !2D variable length arrays
 
    ! IXfreallocdims is called, this takes the shape of the supplied array. The same checks are made on the status
 
    ! of the array pointer as for 1D arrays
 
    call IXFset_integer_array(arg%int_arr,status,int_arr)
 
 
 
  ! for nested objects the set command of the nested object is called, equally valid is a call to IXFcopy
 
  ! call IXFcopy(spectra,arg%spectra,status)
 
  ! which in turn will make the same call to IXFset_spectra
 
     if (present(spectra))call IXFset_spectra(arg%spectra,status,ref=spectra)
 
  
 +
    call IXFset_real_array(arg%val_array,status,val_array)       
 +
    call IXFset_integer_array(arg%int_arr,status,int_arr)   
 +
   
 +
    if (present(spectra))call IXFcopy(spectra,arg%spectra,status)
 +
   
 
     ! logicals and strings are treated in the same way as single variables
 
     ! logicals and strings are treated in the same way as single variables
 
     if(present(xhist))arg%xhist=xhist
 
     if(present(xhist))arg%xhist=xhist
 
     if(present(label))arg%label=label
 
     if(present(label))arg%label=label
 
+
   
 
+
    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
 
     ! the check routine MUST always be called at the end of the set routine
 
     call IXFcheck(arg,status)
 
     call IXFcheck(arg,status)
 
+
   
 
   end subroutine IXFset_testclass
 
   end subroutine IXFset_testclass
</pre>
 
  
<b>IXFget_{your_module_name}</b>
 
<pre>
 
 
!-----------------------------------------------------------------------------------------------------------------------
 
!-----------------------------------------------------------------------------------------------------------------------
!! The IXFget subroutine will return elements of an object to an optional supplied arrays/variables. The supplied variables
+
!> 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
+
!! 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.
 
!! contents of a whole object to a new one.
   subroutine IXFget_testclass(arg,status,base, val, nx, val_array, err_array,val_stat, int_arr,spectra, xhist,label,wout)
+
   subroutine IXFget_testclass(arg,status, val, nx,val_static,int_static, val_array, int_arr,spectra, xhist,label, cell_string,d2d,wout)
 
     implicit none
 
     implicit none
     type(IXTtestclass) :: arg
+
     type(IXTtestclass),intent(in) :: arg
     type(IXTstatus) :: status
+
    type(IXTtestclass),optional,intent(out)::wout
 +
     type(IXTstatus),intent(inout) :: status
 
     ! all the supplied variables are declared as optional with intent(out)
 
     ! all the supplied variables are declared as optional with intent(out)
    type(IXTtestclass),intent(out),optional::wout
+
    real(dp),optional,intent(out) :: val    !< real variable
    type(IXTbase),intent(out),optional::base
+
    integer(i4b),optional,intent(out) :: nx !< integer variable
    real(dp),intent(out),optional:: val, val_stat(3)
+
    real(dp),optional,intent(out) :: val_static(3) !< static real array
    real(dp),intent(out),optional :: val_array(:), err_array(:)
+
    integer(i4b),optional,intent(out)::int_static(4) !< static integer array
    integer(i4b),intent(out),optional::nx,int_arr(:,:)
+
    real(dp),optional,intent(out) :: val_array(:) !<  variable length 1d real array, always declared as NULL by default
    type(IXTspectra),intent(out),optional:: spectra
+
    integer(i4b),optional,intent(out) :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default             
    logical,intent(out), optional:: xhist
+
    type(IXTspectra),optional,intent(out):: spectra !< nested object
     !output strings are again treated as an unknown length. If they are shorter than the object string then the output
+
    logical,optional,intent(out) :: xhist !< logical variable
     !will be truncated. If they are longer then the output will be padded with spaces.
+
     ! input strings are treated as an unknown length, if the supplied string is longer than the declared length
    character(len=*),optional,intent(out) :: label
+
     ! 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 string variable
 +
    character(len=*),optional,intent(out) :: cell_string(:) !< array of strings
 +
    type(IXTdataset_2d),optional,intent(out) :: d2d(:) !<array of objects
 +
 
  
 
     !  this makes a call to the appropriate set routine. The IXFcopy routine calls
 
     !  this makes a call to the appropriate set routine. The IXFcopy routine calls
 
     !  the same routine underneath.
 
     !  the same routine underneath.
 
     if (present(wout))then
 
     if (present(wout))then
       call IXFset_testclass(wout,status,ref=arg)
+
       call IXFcopy(arg,wout,status)
     endif
+
     endif  
 
+
      
    ! supplied nested objects are filled with an appropriate set routine
 
     if (present(base))call IXFset_base(base,status,ref=arg%base)
 
 
 
 
 
 
     ! single variables are copied into the supplied arrays
 
     ! single variables are copied into the supplied arrays
 
     if (present(val))val=arg%val
 
     if (present(val))val=arg%val
 
     if (present(nx))nx=arg%nx
 
     if (present(nx))nx=arg%nx
 
+
   
 
     ! 1D variable length arrays
 
     ! 1D variable length arrays
 
     ! The supplied array must be the same length as the object array. this test is made and the array filled
 
     ! The supplied array must be the same length as the object array. this test is made and the array filled
 
       call IXFget_real_array(arg%val_array,status,val_array)
 
       call IXFget_real_array(arg%val_array,status,val_array)
    ! as above
 
      call IXFget_real_array(arg%err_array,status,err_array)
 
 
    !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_stat))val_stat=arg%val_stat
 
 
 
     ! 2D variable length arrays
 
     ! 2D variable length arrays
 
     ! The supplied array must now be the same shape as the object array. this test is made and the object is filled
 
     ! The supplied array must now be the same shape as the object array. this test is made and the object is filled
 
     call IXFget_integer_array(arg%int_arr,status,int_arr)
 
     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 set routine
 
     ! supplied nested objects are filled with an appropriate set routine
     if(present(spectra))call IXFset_spectra(spectra,status,ref=arg%spectra)
+
     if(present(spectra))call IXFcopy(arg%spectra,spectra,status)
  
 
     ! logicals and strings are treated as single variables, with strings being truncated where appropriate
 
     ! logicals and strings are treated as single variables, with strings being truncated where appropriate
Line 312: Line 318:
 
     if(present(label))label=arg%label
 
     if(present(label))label=arg%label
  
 +
    if (present(cell_string))cell_string=arg%cell_string   
 +
    if(present(d2d))call IXFcopy(arg%d2d,d2d,status)
 +
 +
   
 
   end subroutine IXFget_testclass
 
   end subroutine IXFget_testclass
</pre>
+
 
 +
!-----------------------------------------------------------------------------------------------------------------------
 +
!> 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    !< real variable
 +
    integer(i4b),optional,intent(out) :: nx !< integer variable
 +
    real(dp),optional,intent(out) :: val_static(3) !< static real array
 +
    integer(i4b),optional,intent(out)::int_static(4) !< static integer array
 +
    !allocatable arrays cannot be defined as intent(out), this tends to make runtime errors for some reason
 +
    real(dp),optional,allocatable :: val_array(:)  !< variable length 1d real array, always declared as NULL by default
 +
    integer(i4b),optional,allocatable :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default             
 +
    type(IXTspectra),optional,intent(out):: spectra !< nested object
 +
    logical,optional,intent(out) :: xhist !< logical variable
 +
    ! 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 string variable
 +
    !allocatable arrays cannot be defined as intent(out), this tends to make runtime errors for some reason
 +
    character(len=*),allocatable,optional :: cell_string(:) !< allocatable array of strings
 +
    type(IXTdataset_2d),allocatable,optional :: d2d(:) !<allocatable array of objects
 +
    type(IXTstatus),intent(inout)::status
 +
 
 +
 
 +
! 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
  
  
<b>IXFget_ptr_{your_module_name}</b>
 
<pre>
 
 
!-----------------------------------------------------------------------------------------------------------------------
 
!-----------------------------------------------------------------------------------------------------------------------
!! IXFget_ptr will return a pointer to a structure or an array, from an optional argument.
+
!> IXFget_ptr will return a pointer to a structure or an array, from an optional argument.
!! The pointer arguments are generally the same name as the object elements they are pointing to.
+
!! The pointer arguments are the same name as the object elements they are pointing to.
!! Care must be taken since if the pointers are edited, then the data in the structure will also be edited.
+
!! 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,err_array,int_arr,spectra)
+
   subroutine IXFget_ptr_testclass(arg,val_array,int_arr,spectra)
 
     implicit none
 
     implicit none
 
     type(IXTtestclass),intent(in),target :: arg
 
     type(IXTtestclass),intent(in),target :: arg
 
     type(IXTspectra),optional,pointer::spectra
 
     type(IXTspectra),optional,pointer::spectra
     real(dp),optional,pointer::val_array(:),err_array(:)
+
     real(dp),optional,pointer::val_array(:)
 
     integer(i4b),optional,pointer:: int_arr(:,:)
 
     integer(i4b),optional,pointer:: int_arr(:,:)
  
 
     if (present(spectra))spectra=>arg%spectra
 
     if (present(spectra))spectra=>arg%spectra
 
+
   
 
     if (present(val_array))val_array=>arg%val_array
 
     if (present(val_array))val_array=>arg%val_array
    if (present(err_array))err_array=>arg%err_array
 
 
     if (present(int_arr))int_arr=>arg%int_arr
 
     if (present(int_arr))int_arr=>arg%int_arr
  
 
   end subroutine IXFget_ptr_testclass
 
   end subroutine IXFget_ptr_testclass
</pre>
 
  
<b>IXFget_alloc_{your_module_name}</b>
 
<pre>
 
!-----------------------------------------------------------------------------------------------------------------------
 
!! IXFget_alloc will fill optionally supplied allocatable arrays with the data contained in the
 
!! object array elements. The supplied arrays can be either allocated or not. If they are the wrong
 
!! length then they are adjusted accordingly. This is a routine only for internal Fortran use.
 
  subroutine IXFget_alloc_testclass(arg,val_array,err_array,int_arr)
 
    implicit none
 
    type(IXTtestclass),intent(in) :: arg
 
    real(dp),allocatable,optional::val_array(:),err_array(:)
 
    integer(i4b),allocatable,optional:: int_arr(:,:)
 
  
! 1D arrays
+
!!-----------------------------------------------------------------------------------------------------------------------
    if (present(val_array))then
+
!!! IXFcreate_special_testclass is a customised constructor subroutine which takes three source components of the object
      call IXFreallocFortran(val_array,shape(arg%val_array),.false.,status)
+
!!! and fills the rest of the object with customised variables and calls the IXFset subroutine.
      val_array=arg%val_array
+
!  subroutine IXFcreate_special_testclass(arg,val_array,err_array,spectra,status)
     endif
+
!    implicit none
 
+
!    type(IXTtestclass)::arg
     if (present(err_array))then
+
!    real(dp),intent(in)::val_array(:),err_array(:)
      call IXFreallocFortran(err_array,shape(arg%err_array),.false.,status)
+
!    type(IXTspectra),intent(in):: spectra
      err_array=arg%err_array
+
!    real(dp)::val_stat(3),val
 +
!    type(IXTstatus)::status
 +
!    integer(i4b)::int_arr(2,2),nx
 +
!    logical :: xhist
 +
!    character(len=short_len) :: label
 +
!
 +
!    !set customised variables
 +
!    xhist=.true.
 +
!    nx=33
 +
!    val=666.66
 +
!    label='customised object'
 +
!    int_arr(1,:)=2
 +
!    int_arr(2,:)=445
 +
!    call random_number(val_stat)
 +
!   
 +
!    !conceivably the IXFcreate subroutine could now be called here
 +
!   
 +
!    !input arguments are now checked
 +
!   
 +
!    !make the check on nested objects
 +
!    if( IXFvalid(spectra) .neqv. .true.)then
 +
!            call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
 +
!            IXCerr_outofmem, 'IXTspectra failure, all nested objects MUST be initialised (IXFset_testclass)')
 +
!    endif
 +
!
 +
!   
 +
!    ! 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_array, err_array,val_stat, int_arr,spectra, xhist,label)   
 +
!   
 +
!  end subroutine IXFcreate_special_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
 
     endif
  
 
+
     if(size(w1%d2d) /= size(w2%d2d))then
! for the 2D array we have to check if the shape is exactly the same using the sum
+
        call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
! of the absolute of the difference of the shape output
+
            IXCerr_outofmem, 'd2d failure in operation (IXFplus_testclass)')
     if (present(int_arr))then
+
        return
      call IXFreallocFortran(int_arr,shape(arg%int_arr),.false.,status)
+
    else   
      int_arr=arg%int_arr
+
        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
 
     endif
 
+
   
end subroutine IXFget_alloc_testclass
+
  end subroutine IXFplus_testclass
</pre>
+
   
 +
end module IXMtestclass
 +
<\pre>

Revision as of 10:10, 12 May 2008

!-----------------------------------------------------------------------------------------------------------------------------------
! MODULE: IXMtestclass
!-----------------------------------------------------------------------------------------------------------------------------------
!> \file IXMtestclass.f90
!!
!! @author Freddie Akeroyd, ISIS
!! @version $Revision: 1361 $ ($Date: 2008-05-09 17:41:14 +0100 (Fri, 09 May 2008) $)
!!
!! Example of a class module
!!
!! 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
!! 
!! All classes must define the module function the implements the IXFoperationRun
!! interface
!!
!> @{
module IXMtestclass
! include the modules which are relied upon by the module  
  use IXMbase
  use IXMspectra
  use IXMdataset_2d
!> 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. 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.
  type IXTtestclass 
     private ! all component elements of the module are private and encapsulated
     ! all objects have a base class
     type(IXTbase) :: base !< base class object
     real(dp) :: val    !< real variable
     integer(i4b) :: nx !< integer variable
     real(dp) :: val_static(3)=0.0 !< static real array
     integer(i4b)::int_static(4)=0.0 !< static integer array
     real(dp), pointer :: val_array(:) => NULL()    !<  variable length 1d real array, always declared as NULL by default
     integer(i4b),pointer :: int_arr(:,:)=>NULL() !< variable length 2d integer array, always declared as NULL by default               
     type(IXTspectra):: spectra !< nested object
     logical :: xhist=.FALSE. !< logical variable
     character(len=short_len) :: label='x-label' !< character string variable
     character(len=long_len),allocatable :: cell_string(:) !< allocatable array of strings
     type(IXTdataset_2d), allocatable :: d2d(:) !<allocatable array of objects
  end type IXTtestclass


!> \name pubint
!! Public interfaces
!! @{
! include the interfaces required by routines declared in class_base.f90
#define IXD_TYPE testclass
#include "class_header.f90"

!> interface to the IXFplus function
  interface IXFplus
     module procedure IXFplus_testclass
  end interface IXFplus
! finish public interfaces
!> @}
contains

!! include the generic subroutines every class requires

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

!>  All classes must provide this operation; it loops through
!! all members of the class doing the supplied operation
  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)!this is a pointer array so the ptr suffix is used
    call IXFoperation_run_ptr(op, 'int_array', arg%val_array, status)!this is a pointer array so the ptr suffix is used      
    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
  
!-----------------------------------------------------------------------------------------------------------------------
!> 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. 
  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    !< real variable
     integer(i4b),intent(in) :: nx !< integer variable
     real(dp),intent(in) :: val_static(3) !< static real array
     integer(i4b),intent(in)::int_static(4) !< static integer array
     real(dp),intent(in) :: val_array(:)    !<  variable length 1d real array, always declared as NULL by default
     integer(i4b),intent(in) :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default               
     type(IXTspectra),intent(in):: spectra !< nested object
     logical,intent(in) :: xhist !< logical variable
     character(len=short_len),intent(in) :: label !< character string variable
     character(len=long_len),intent(in) :: cell_string(:) !< allocatable array of strings
     type(IXTdataset_2d),intent(in) :: d2d(:) !<allocatable array of objects


    ! 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
  
  
!-----------------------------------------------------------------------------------------------------------------------
!>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.
  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 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

!-----------------------------------------------------------------------------------------------------------------------
!>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.
!! 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    !< real variable
     integer(i4b),optional,intent(in) :: nx !< integer variable
     real(dp),optional,intent(in) :: val_static(3) !< static real array
     integer(i4b),optional,intent(in)::int_static(4) !< static integer array
     real(dp),optional,intent(in) :: val_array(:)  !<  variable length 1d real array, always declared as NULL by default
     integer(i4b),optional,intent(in) :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default               
     type(IXTspectra),optional,intent(in):: spectra !< nested object
     logical,optional,intent(in) :: xhist !< logical variable
    ! 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 string variable
     character(len=*),optional,intent(in) :: cell_string(:) !< array of strings
     type(IXTdataset_2d),optional,intent(in) :: d2d(:) !<array of objects
  
   ! 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_stat 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

    call IXFset_real_array(arg%val_array,status,val_array)        
    call IXFset_integer_array(arg%int_arr,status,int_arr)    
    
    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
    
    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

!-----------------------------------------------------------------------------------------------------------------------
!> 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.
  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
    ! all the supplied variables are declared as optional with intent(out)
     real(dp),optional,intent(out) :: val    !< real variable
     integer(i4b),optional,intent(out) :: nx !< integer variable
     real(dp),optional,intent(out) :: val_static(3) !< static real array
     integer(i4b),optional,intent(out)::int_static(4) !< static integer array
     real(dp),optional,intent(out) :: val_array(:)  !<  variable length 1d real array, always declared as NULL by default
     integer(i4b),optional,intent(out) :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default               
     type(IXTspectra),optional,intent(out):: spectra !< nested object
     logical,optional,intent(out) :: xhist !< logical variable
    ! 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 string variable
     character(len=*),optional,intent(out) :: cell_string(:) !< array of strings
     type(IXTdataset_2d),optional,intent(out) :: d2d(:) !<array of objects


    !  this makes a call to the appropriate set routine. The IXFcopy routine calls
    !  the same routine underneath.
    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
    
    ! 1D variable length arrays
    ! The supplied array must be the same length as the object array. this test is made and the array filled
      call IXFget_real_array(arg%val_array,status,val_array)
    ! 2D variable length arrays
    ! The supplied array must now be the same shape as the object array. this test is made and the object is filled
    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 set routine
    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

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

    
  end subroutine IXFget_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    !< real variable
     integer(i4b),optional,intent(out) :: nx !< integer variable
     real(dp),optional,intent(out) :: val_static(3) !< static real array
     integer(i4b),optional,intent(out)::int_static(4) !< static integer array
     !allocatable arrays cannot be defined as intent(out), this tends to make runtime errors for some reason
     real(dp),optional,allocatable :: val_array(:)  !<  variable length 1d real array, always declared as NULL by default
     integer(i4b),optional,allocatable :: int_arr(:,:) !< variable length 2d integer array, always declared as NULL by default               
     type(IXTspectra),optional,intent(out):: spectra !< nested object
     logical,optional,intent(out) :: xhist !< logical variable
    ! 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 string variable
     !allocatable arrays cannot be defined as intent(out), this tends to make runtime errors for some reason
     character(len=*),allocatable,optional :: cell_string(:) !< allocatable array of strings
     type(IXTdataset_2d),allocatable,optional :: d2d(:) !<allocatable array of objects
    type(IXTstatus),intent(inout)::status


! 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 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


!!-----------------------------------------------------------------------------------------------------------------------
!!! IXFcreate_special_testclass is a customised constructor subroutine which takes three source components of the object
!!! and fills the rest of the object with customised variables and calls the IXFset subroutine. 
!  subroutine IXFcreate_special_testclass(arg,val_array,err_array,spectra,status)
!    implicit none
!    type(IXTtestclass)::arg
!    real(dp),intent(in)::val_array(:),err_array(:)
!    type(IXTspectra),intent(in):: spectra 
!    real(dp)::val_stat(3),val
!    type(IXTstatus)::status
!    integer(i4b)::int_arr(2,2),nx
!    logical :: xhist
!    character(len=short_len) :: label
!
!    !set customised variables
!    xhist=.true.
!    nx=33
!    val=666.66
!    label='customised object'
!    int_arr(1,:)=2
!    int_arr(2,:)=445
!    call random_number(val_stat)
!    
!    !conceivably the IXFcreate subroutine could now be called here
!    
!    !input arguments are now checked 
!    
!    !make the check on nested objects
!    if( IXFvalid(spectra) .neqv. .true.)then
!            call IXFadd_status(status, IXCfacility_libisis, IXCseverity_error, &
!            IXCerr_outofmem, 'IXTspectra failure, all nested objects MUST be initialised (IXFset_testclass)')
!    endif
!
!    
!    ! 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_array, err_array,val_stat, int_arr,spectra, xhist,label)    
!    
!  end subroutine IXFcreate_special_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
<\pre>