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

question about Modules and COMMON blocks

Brian_Murphy
New Contributor II
2,735 Views

I am a novice in the use of Modules, and I've got a question. 

I have a very old code that uses dozens of named common blocks.  I am considering replacing each with a Module of the same name.  Is that a wise thing to do?  I think I will end up with a single .f90 file containing all the named Modules, and replacing all the named COMMON statements and associated variable declarations appearing throughout the code with USE statements with the same names.

Every routine throughout the code has IMPLICIT NONE in it, and I think that will help me along the way.

0 Kudos
31 Replies
Brian_Murphy
New Contributor II
444 Views

It is the former.  As best I can tell, changes are to names only, not to data type or anything else (at least, I haven't spotted any).  There are some places where EQUIVALENCE is used, but not with variables in common blocks.  Most of the time, the common blocks are used without changing the names, but there are still many places where they are changed so they don't conflict with locally declared variables in individual subroutines.

I got most of the way through the conversion without having to change too many variable names, but then I hit whole bunch and decided to stop.

0 Kudos
FortranFan
Honored Contributor II
444 Views

Brian Murphy wrote:

It is the former. ..

@Brian Murphy,

So if I undertand correctly, your situation seems akin to the following where you will notice the COMMON block variables have different names in the two scopes: YSLOPE and YINCPT in the function whereas M and C in the main program.  Is this what you have?

      FUNCTION Y(X)
C Some user function:
      IMPLICIT NONE
C Argument list
      DOUBLE PRECISION X
C Function result
      DOUBLE PRECISION Y
C Global data
      DOUBLE PRECISION YSLOPE
      DOUBLE PRECISION YINCPT
      COMMON / CQUAD / YSLOPE, YINCPT
C Calculations
      Y = YSLOPE*X + YINCPT
C
      END FUNCTION
      PROGRAM QUADRT
C
      IMPLICIT NONE
C Declarations
      DOUBLE PRECISION INTGRT
      EXTERNAL INTGRT
      DOUBLE PRECISION M
      DOUBLE PRECISION C
      COMMON / CQUAD / M, C
      DOUBLE PRECISION Y
      EXTERNAL Y
      DOUBLE PRECISION R, XMIN, XMAX
C Instructions
      XMIN = 0D0
      XMAX = 1D0
      M = 1D0
      C = 1D0
      R = INTGRT(Y, XMIN, XMAX)
C Reporting
      PRINT *, 'Result from INTGRT: ' , R
      PRINT *, '   Expected Result: ' , (M*XMAX**2/2D0+C*XMAX)-
     &                                  (M*XMIN**2/2D0+C*XMIN)
C
      END PROGRAM QUADRT

 

0 Kudos
Brian_Murphy
New Contributor II
444 Views

Yes, but with a slight twist.  For example, a common block most of the time uses variable names RHO and EMU, but in one particular subroutine there are local variables already using these names, so in this subroutine the common block statement was setup using different names, say RHOC and EMUC.  Of course in retrospect this should not have been done, but it was.

In my meager understanding of Modules, I want the Module definition of this Common block to use RHO and EMU because those are the names used in the vast majority of cases, and in the above example that forces me to go through that subroutine and change the pair of local variable names to something that doesn't conflict with the names in the Module.

0 Kudos
FortranFan
Honored Contributor II
444 Views

Brian Murphy wrote:

Yes, but with a slight twist.  For example, a common block most of the time uses variable names RHO and EMU, but in one particular subroutine there are local variables already using these names, so in this subroutine the common block statement was setup using different names, say RHOC and EMUC. .. that forces me to go through that subroutine and change the pair of local variable names to something that doesn't conflict with the names in the Module.

@Brian Murphy,

This is exactly what I suspected when I first read your "name mangling" comment and I was hoping it won't trip you up because the Fortran standard provides a 'renaming' facility for module entities in a given scope.

If you keep the example in Message #15 in mind, then you can imagine an initial modernization of such code might be as shown below.  Now in the main section of the code where all the key setup takes place i.e., under the "Instructions" section on the main program and the integration procedure in the 'math' library and the equation for the user function, you will notice the code can in essence be kept the same as the original; it's all the other 'furniture' around it that is being 'modernized'

Now notice line #5 in the main program below that has constructs such as "M => Slope" (which in your case will be "RHOC => RHO") that setups M as an alias for Slope in that scope.  This can allow you to move forward with your work without requiring you to change a lot of instructions in other sections of your code.

module mykinds_m

   implicit none

   ! Select suitable working precision (WP), here's an example:
   integer, parameter :: WP = selected_real_kind( p=15, r=307 )

end module
module mylib_m

   use mykinds_m, only : WP

   implicit none

   private

   public :: integrate

   interface
      function Ifunc( X ) result( Y )
         import :: WP
         ! Argument list
         real(WP), intent(in) :: X
         ! Function result
         real(WP) :: Y
      end function Ifunc
   end interface

contains

   function integrate(F, Xmin, Xmax) result( r )
   ! Integrate F using trapezoidal rule

      ! Argument list
      procedure(Ifunc)     :: F
      real(WP), intent(in) :: Xmin
      real(WP), intent(in) :: Xmax

      ! Function result
      real(WP) :: r

      ! Local variables
      real(WP) :: Ymin
      real(WP) :: Ymax

      ! Calculations
      Ymin = F( Xmin )
      Ymax = F( Xmax )
      r = (Xmax - Xmin)*(Ymax + Ymin)/2_wp

      return

   end function

end module mylib_m
module myfuncs_m

   use mykinds_m, only : WP

   implicit none

   private

   ! Global data
   real(WP) :: Slope
   real(WP) :: Intercept

   ! Public functions
   public :: myfunc

   ! Public data - only initially
   public :: Slope
   public :: Intercept

contains

   function myfunc( X ) result( Y )
   ! Some user function

      ! Argument list
      real(WP), intent(in) :: X

      ! Function result
      real(WP) :: Y

      ! Calculations
      Y = Slope*X + Intercept

      return

   end function myfunc

end module myfuncs_m
program Quadrature

   use mykinds_m, only : WP
   use mylib_m, only : integrate
   use myfuncs_m, only : M => Slope, C => Intercept, Y => myfunc

   implicit none

   ! Declarationa
   real(WP) :: R
   real(WP) :: XMIN
   real(WP) :: XMAX

   ! Instructions
   XMIN = 0.0_wp
   XMAX = 1.0_wp
   M = 1.0_wp
   C = 1.0_wp
   R = integrate(Y, XMIN, XMAX)

   ! Reporting
   print *, 'Result from INTGRT: ' , R
   print *, '   Expected Result: ' , (M*XMAX**2/2.0_wp + C*XMAX) -                                  &
  &                                  (M*XMIN**2/2.0_wp + C*XMIN)

   stop

end program Quadrature

What all this rearrangement provides you with are the advanatages mentioned earlier, defined floating-point representation, defined interfaces for all your procedures, ability to work with global data in a more controlled manner (private vs public scope), and so forth.  And more benefits can be attained with further refinement as necessary.

Hope this helps,

0 Kudos
Brian_Murphy
New Contributor II
444 Views

Well darn! I didn't know about the renaming feature of USE statements.  The following is from page 151 of Metcalf's book Fortran 90/95 Explained. 

reason for permitting accessed entities to be renamed by the use statement. Renaming
is also available to resolve a name clash between a local entity and an
entity accessed from a module, though our preference is to use a text editor or
other tool to change the local name. With renaming, the use statement has the
form

That would have saved me a lot of trouble.  The best thing for me to do at this point might be to start over, and do the conversion without any renaming.

0 Kudos
Brian_Murphy
New Contributor II
444 Views

I have started over, and so far I have successfully converted all common blocks which contain arrays (20 of over 85 named common blocks!).  The code builds and runs, and the case that crashed no longer crashes.  Yippee!  I'm still using the older version of Intel Fortran.

0 Kudos
jimdempseyatthecove
Honored Contributor III
444 Views

Good luck in the remainder of your conversions.

Bear in mind that named commons are like unions. The same name can appear in different compilation units with potentially different mappings. This can often bite you when you convert to modules.

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor II
444 Views

Brian Murphy wrote:

I have started over, and so far I have successfully converted all common blocks which contain arrays (20 of over 85 named common blocks!).  The code builds and runs, and the case that crashed no longer crashes.  Yippee!  I'm still using the older version of Intel Fortran.

Mega Kudos to you on your effort and perseverance!  Keep up the great work.

0 Kudos
Brian_Murphy
New Contributor II
444 Views

The renaming feature of the Use statement has made this much easier, and that's an understatement.

I am converting one named Common at a time, and I start each by carefully checking the list of variable names in every occurrence of the same named Common statement.  The Visual Studio editor makes doing that pretty easy.  After converting all of one Common, I build and run the code before going on to the next one.

0 Kudos
JohnNichols
Valued Contributor III
444 Views

I have changed a few programs from Common Blocks to modules, the effort it worth it -- but people used to do terrible things in common blocks and it can be a pain to catch all the errors.

It is not that it is not a good idea as noted elsewehre it is more the time -- there is so much to do and such little time

John

0 Kudos
Brian_Murphy
New Contributor II
444 Views

After starting over, it was taking me about 15 to 20 minutes to do one Common block.  So 20 took me about 5 to 6 hours, and my wife wanted to know why I was up so late. :)

Going forward, this particular code will be much easier to work with.  Plus this has been a worthwhile learning experience for me since my fortran knowledge is largely stuck on F77 from the days of paper tapes.

0 Kudos
Reply