Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Fortran equivalent to Property Let?

NotThatItMatters
Beginner
376 Views

I have a large set of code that has "global" arrays in it.  These arrays are arguments to many different SUBROUTINEs in the code.  I would like to create a module (or two or three) which make these global arrays PRIVATE but provide accessors to these arrays.  As right-hand side (RHS) arguments it is simple to create something in the module like

MODULE GLB
INTEGER, PARAMETER :: F = SELECTED_REAL_KIND(12, 100)
REAL (KIND = F), DIMENSION(:, :, :), ALLOCATABLE, PRIVATE :: A_LOCAL

CONTAINS
REAL (KIND = F) FUNCTION A(I, J, K)
    INTEGER, INTENT(IN) :: I, J, K
    IF (ALLOCATED(A_LOCAL)) THEN
        A = A_LOCAL(I, J, K)
    ELSE
        A = 0.0_F
    END IF
END FUNCTION A
END MODULE GLB

so that all existing RHS references to the A array in the code would be replaced by

USE GLB, ONLY : A

without changing any code lines.  Might there be a way to replace left-hand side (LHS) uses of the array A, as in

A(I, J, K) = 42.0_F

with some construct, sort of like the VB Property Let construct with multiple input arguments?

The answer here can easily be no, in which case I will need to track down all LHS uses and replace them with calls to a SUBROUTINE within MODULE GLB like

SUBROUTINE SET_A(I, J, K, A_VALUE)
    INTEGER, INTENT(IN) ::: I, J, K
    REAL (KIND = F), INTENT(IN) :: A_VALUE
    IF (.NOT. ALLOCATED(A_LOCAL)) THEN
        ALLOCATE(A_LOCAL(I_SIZE, J_SIZE, K_SIZE)
        A_LOCAL = 0.0_F
    END IF
    A_LOCAL(I, J, K) = A_VALUE
END SUBROUTINE

Thanks in advance.

0 Kudos
1 Solution
IanH
Honored Contributor II
376 Views

The answer to the question, which asks about "Fortran", is "Yes".  But, there's a "but".

Fortran 2008 permits a function reference with a data pointer result to appear on the left hand side of an assignment statement.  For example:

MODULE m
  IMPLICIT NONE
  INTEGER, TARGET :: local_a(4)
  PUBLIC :: A
  PUBLIC :: B
CONTAINS
  FUNCTION A(i)
    INTEGER, INTENT(IN) :: i
    INTEGER, POINTER :: A
    ! Remap the index just for giggles.
    A => local_a(MOD(i,4)+1)
  END FUNCTION A
  
  SUBROUTINE B
    PRINT "(*(I0,:,','))", local_a
  END SUBROUTINE B
END MODULE m

PROGRAM ptr_left_hand_side
  USE m
  IMPLICIT NONE
  INTEGER :: i
  
  DO i = 1, 4
    A(i) = i
  END DO
  
  CALL B
END PROGRAM ptr_left_hand_side

The above would be expected to print 4,1,2,3.

The "but" is that ifort current release and current beta don't support this Fortran 2008 feature.

:(

View solution in original post

0 Kudos
5 Replies
jimdempseyatthecove
Honored Contributor III
376 Views

If you intend on making the array private, then shouldn't you require an GET_A function?

I know you do not want to rewrite the rhs (or lhs).

What is the reasoning why you do not want A public?

Is it to protect against index out of range? (and return 0.0 when so). Note that your SET_A subroutine does not check/protect for index out of range.

Is it to auto allocate? Allocation can be done in a once only init routine.

Or, at the top of each of your routines that access A (and other such routines)

CALL YourINIT(A, iMAX, jMAX, kMAX)

When A is not allocated, it is allocated to those sizes and initialized.
When A is allocated AND any extents are less than those specified, A gets reshaped.
When A is allocated AND all extents are .GE. the maxes do nothing.

This will permit the lhs and rhs in source to remain the same, yet provide some protection (assuming you know the max values to be subsequently used).

Jim Dempsey

0 Kudos
NotThatItMatters
Beginner
376 Views

The reasoning behind this effort lies in several factors:

1) There are at least one hundred allocatable arrays currently allocated / deallocated in a driver routine which are then passed down the line to other routines  Yes, the idea was to use an initialize routine for these arrays and index them appropriately without bounds checking.  This is done currently by making most access DO loop driven with the indices being between bounds.  As an example, one called routine currently has 400+ arguments.

2) The need for some arrays extends beyond routines which currently have them as arguments.  Rather than tacking more arguments onto these routines, the use of accessors could satisfy the need.  Again, there is considerable legacy in using the code, but code development has become unwieldy with the current structure.

3) The use of a few, but not all, of these arrays is "update".  Often the update of these arrays in the design is part of other code segments.  For example, there is a "model geometry" set of arrays.  Many of these, once initialized, never change.  Others, although they have the characteristics of model geometry, will change at every time step due to certain criteria.  It is this second set of data that is the concern here. The idea of code maintenance and development was based on keeping the arrays PRIVATE and manipulating using accessors, much like a traditional class structure.  That way I can be assured that data is changing only when I want it to change (specific calls).  This data integrity is currently managed with INTENT clauses in the routines, but the largest routines mostly have INTENT(INOUT) which gives me little help in determining where data is changing.

4) Much of the use of the arrays is encased presently in fixed source form.  Prefixing each array name with "GET_" will most likely cause a great deal more continuation lines (there are a good deal already), making the code even more unreadable.

I am assuming the LHS use of the arrays will require building SET_A routines.  I think this will be a small price to pay in the refactoring and general cleanup of the code.  I will of course need to be very specific in the documentation of "A" and its relatives.

0 Kudos
IanH
Honored Contributor II
377 Views

The answer to the question, which asks about "Fortran", is "Yes".  But, there's a "but".

Fortran 2008 permits a function reference with a data pointer result to appear on the left hand side of an assignment statement.  For example:

MODULE m
  IMPLICIT NONE
  INTEGER, TARGET :: local_a(4)
  PUBLIC :: A
  PUBLIC :: B
CONTAINS
  FUNCTION A(i)
    INTEGER, INTENT(IN) :: i
    INTEGER, POINTER :: A
    ! Remap the index just for giggles.
    A => local_a(MOD(i,4)+1)
  END FUNCTION A
  
  SUBROUTINE B
    PRINT "(*(I0,:,','))", local_a
  END SUBROUTINE B
END MODULE m

PROGRAM ptr_left_hand_side
  USE m
  IMPLICIT NONE
  INTEGER :: i
  
  DO i = 1, 4
    A(i) = i
  END DO
  
  CALL B
END PROGRAM ptr_left_hand_side

The above would be expected to print 4,1,2,3.

The "but" is that ifort current release and current beta don't support this Fortran 2008 feature.

:(

0 Kudos
NotThatItMatters
Beginner
376 Views

Thanks, IanH.

I had thought there might be a way, but I was focused on "defined assignment" which was not yielding anything.  It is unfortunate that this feature has yet to be implemented, but I will make use of the CALL SET_A syntax.  At present I am only maneuvering through a small subset of the arrays which should make this development effort approachable.

0 Kudos
Simon_Geard
New Contributor I
376 Views

If you can arrange for 'A' always to have been initialized when it is used you could declare it as 'public, protected'. That will give you read-only access without having to create 'get' accessors. Of course that doesn't help with 'set'. Just a thought.

0 Kudos
Reply