Free plusFORT Licence for Fortran Discourse users

Download and installation instructions for version 7.50 of plusFORT, on Windows, Mac and Linux, can be found at:

https://fortran.uk/currentversion.

Your licence comprises the following 3 lines of text:

fortran-lang.discourse.group - Windows, Mac or Linux
Valid until 31st March 2022
ZDDAAZCJJBJDHMDKFP

This licence is valid until the end of March 2022. To request an extension beyond that date, please get in touch via our website. We ask that you seriously consider purchasing a licence if you use the software meaningfully, particularly in a commercial of government environment. Please let us know how you get on.

Brief Overview of plusFORT

There is much in plusFORT that cannot be found anywhere else. More detail, including video tutorials and an online manual, can be found at www.fortran.uk. plusFORT supports all standards up to Fortran 2003, as well as VAX Fortran, and many other extensions. It can be used from the command line, or using PFFE, our intuitive GUI front-end.

Code Modernization

  • Source code restructuring and spaghetti unscrambler
  • Source form conversion
  • Automatically generates declarations to allow use of IMPLICIT NONE
  • Translates declarations to and from Fortran 2003 form
  • Automatically adds INTENT to argument declarations
  • Over 100 configurable options to customize code transformation

Code Instrumentation

  • Automatically insert probes to analyse CPU usage
  • Dynamic Analysis. Inserts probes to identify illegal use of
    uninitialized data (much more thorough than compile-time checks).
  • Coverage Analysis. Inserts probes to identify execution hotspots, and
    code-blocks which remain untested through a series of runs.

Static Analysis

  • HTML reports (with extensive crosslinks between reports) showing usage
    and problems relating to
    • subprograms
    • subprogram arguments
    • modules and module variables
    • COMMON blocks and variables
    • PARAMETERs
    • local variables
  • Hyperlinked HTML call tree

Modularization

  • Analysis of legacy Fortran code in preparation for conversion to
    modular structure.
    • Internalization - shows subprograms and COMMON blocks that
      could be internal (i.e. CONTAINed within another executable
      subprogram)
    • Cluster Analysis - identifies subprograms and data structures
      which could be combined in a module.

HyperKWIC

  • A complete refresh of the traditional KWIC (KeyWord In Context)
    report.
  • Every occurrence of every user symbol is hyperlinked, to allow users
    to skip back and forth between reports and source code.
16 Likes

This is great, thanks. I have a question regarding what would be considered fair use. There is a lot of public domain FORTRAN code at Netlib and elsewhere that needs updating with something like plusFORT. If someone used plusFORT on such code and put it on GitHub, would you consider that fair use?

A situation where you are paid to modernize a company’s proprietary Fortran code would be different and should entail a purchase of plusFORT.

3 Likes

Yes that would be fine. I’d prefer to issue a separate gratis non-commercial licence specifically for that usage. If you (or anyone else) would like to get in touch via our website, I’ll send you one.

John Appleyard
Polyhedron Solutions

5 Likes

I’ve barely scratched the surface, but initial results with packages like ODEPACK are very encouraging. That’s a very generous offer. I think modernizing some popular OS packages would benefit the Fortran community and give you some useful feedback and visibility in return.

One thing on my wish list that I do not see (yet) is an ability to instrument the codes to generate capture-and-playback unit tests but a lot of the parts seem to be there. A lot of the old codes do not have very complete test suites, which makes additional manual refactoring riskier. Did I miss that or is that possibly a future feature?

Thanks!

3 Likes

Thanks for your feedback! I’m unsure about the answer to your question - does Coverage Analysis come close to what you need? It can accumulate data across a series of test runs with different inputs, to identify linear code blocks that are not exercised.

SPAG is able to insert calls to probes at various points in your code (see below). The probe routines are written in Fortran, and the source is supplied, so it’s quite possible for a user to adapt them to particular needs. As an example, when Y2K was an issue, we adapted the routines to spot things that looked like dates.

Probes can be inserted at various points to monitor control flow

  • At the start of execution, and before a controlled termination
  • on entering or leaving a routine
  • on entering a linear block of code (with no transfers)

The Dynamic analysis facility also inserts probes which change or monitor the values of variables:

  • when variables become formally undefined (e.g. unsaved local variables on entry to a subprogram)
  • when variables are used (to check they are defined)

JA

1 Like

Thanks for offering a Discourse test license.

I’ve tried to install on Linux, but am hitting an error:

/opt$ sudo wget https://www.fortran.uk/pf/750/plusfort_x64Linux_750.tgz
--2022-01-20 12:47:54--  https://www.fortran.uk/pf/750/plusfort_x64Linux_750.tgz
Resolving www.fortran.uk (www.fortran.uk)... 109.203.102.104
Connecting to www.fortran.uk (www.fortran.uk)|109.203.102.104|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 20868295 (20M) [application/x-tar]
Saving to: ‘plusfort_x64Linux_750.tgz.1’

plusfort_x64Linux_7 100%[===================>]  19,90M  4,34MB/s    in 7,1s    

2022-01-20 12:48:01 (2,81 MB/s) - ‘plusfort_x64Linux_750.tgz.1’ saved [20868295/20868295]
/opt$ sudo tar -xzvf plusfort_x64Linux_750.tgz
tar: This does not look like a tar archive
tar: Skipping to next header
tar: Exiting with failure status due to previous errors
1 Like

Looks like you downloaded it twice. Check whether the one you try to untar is actually the correct one (via the SHA256 checksum which is given on the download page).

You probably have two downloaded files, plusfort_x64Linux_750.tgz and plusfort_x64Linux_750.tgz.1. The first of these is probably a leftover from an earlier failed or aborted download. Delete it, rename the second by removing the “.1” suffix, and then proceed to extract/install.

Thanks for the advice. I think at my first attempt downloading through Firefox I hit the mangling issue, hence I used wget but didn’t notice it went to a new file. The checksum of this one (found with sha256sum plusfort_x64Linux_750.tgz) matched the one on the download page. The archive was now extracted correctly.

The SPAG tool looks interesting. I tried it for a few of my source files, both modern and dusty ones, but I was not really convinced by the result yet.

For example taking toml-f/table.f90 at main · toml-f/toml-f · GitHub results in an invalid Fortran source file in the SPAGged directory. Also, I find the all caps style and the mixed format output somewhat irritating. But maybe I’m using it wrong.

Output of running SPAG on table.f90
!*==tomlf_type_table.f90  processed by SPAG 7.50RH at 19:51 on 20 Jan 2022
! This file is part of toml-f.
! SPDX-Identifier: Apache-2.0 OR MIT
!
! Licensed under either of Apache License, Version 2.0 or MIT license
! at your option; you may not use this file except in compliance with
! the License.
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
 
!> Implementation of the TOML table data type.
!>
!> Every TOML document contains at least one (root) table which holds key-value
!> pairs, arrays and other tables.
      MODULE TOMLF_TYPE_TABLE
      USE TOMLF_CONSTANTS , ONLY:TFC
      USE TOMLF_ERROR , ONLY:TOML_STAT
      USE TOMLF_TYPE_VALUE , ONLY:toml_value , TOML_VISITOR , TOML_KEY
      USE TOMLF_STRUCTURE , ONLY:TOML_STRUCTURE , NEW_STRUCTURE
      IMPLICIT NONE
!*--TOMLF_TYPE_TABLE25
      PRIVATE
 
      PUBLIC  :: TOML_TABLE , new_table , new
 
 
   !> TOML table
      TYPE , EXTENDS :: (toml_value) :: TOML_TABLE
 
      !> Table was implictly created
         LOGICAL  ::  IMPLICIT = .FALSE.
 
      !> Is an inline table and is therefore non-extendable
         LOGICAL  ::  INLINE = .FALSE.
 
      !> Storage unit for TOML values of this table
 
         CONTAINS
      IMPLICIT NONE
!*--AA000144
 
      !> Get the TOML value associated with the respective key
 
      !> Get list of all keys in this table
 
      !> Check if key is already present in this table instance
 
      !> Append value to table (checks automatically for key)
 
      !> Delete TOML value at a given key
 
      !> Release allocation hold by TOML table
 
   END TYPE 
 
 
   !> Create standard constructor
   INTERFACE TOML_TABLE
   MODULE PROCEDURE  :: NEW_TABLE_FUNC
   END INTERFACE
 
 
   !> Overloaded constructor for TOML values
   INTERFACE NEW
   MODULE PROCEDURE  :: NEW_table
   END INTERFACE
 
 
   CONTAINS
 
 
!> Constructor to create a new TOML table and allocate the internal storage
      SUBROUTINE NEW_TABLE(Self)
      IMPLICIT NONE
!*--NEW_TABLE79
 
   !> Instance of the TOML table
      TYPE (TOML_TABLE) , INTENT(OUT) :: Self
 
      CALL NEW_STRUCTURE(Self%LIST)
 
      END SUBROUTINE NEW_TABLE
 
 
!> Default constructor for TOML table type
      FUNCTION NEW_TABLE_FUNC() RESULT(SELF)
      IMPLICIT NONE
!*--NEW_TABLE_FUNC92
 
   !> Instance of the TOML table
      TYPE (TOML_TABLE) :: SELF
 
      CALL NEW_TABLE(SELF)
 
      END FUNCTION NEW_TABLE_FUNC
 
 
!> Get the TOML value associated with the respective key
      SUBROUTINE GET(SELF,Key,Ptr)
      IMPLICIT NONE
!*--GET105
 
   !> Instance of the TOML table
 
   !> Key to the TOML value
      CHARACTER(KIND=tfc,LEN=*) , INTENT(IN)  ::  Key
 
   !> Pointer to the TOML value
 
      CALL SELF%LIST%FIND(Key,Ptr)
 
      END SUBROUTINE GET
 
 
!> Get list of all keys in this table
      SUBROUTINE GET_KEYS(SELF,List)
      IMPLICIT NONE
!*--GET_KEYS122
 
   !> Instance of the TOML table
 
   !> List of all keys
      TYPE (TOML_KEY) , ALLOCATABLE , INTENT(OUT) :: List(:)
 
      CALL SELF%List%GET_KEYS(List)
 
      END SUBROUTINE GET_KEYS
 
 
!> Check if a key is present in the table
      FUNCTION HAS_KEY(SELF,Key) RESULT(FOUND)
      IMPLICIT NONE
!*--HAS_KEY137
 
   !> Instance of the TOML table
 
   !> Key to the TOML value
      CHARACTER(KIND=tfc,LEN=*) , INTENT(IN)  ::  Key
 
   !> TOML value is present in table
      LOGICAL  ::  FOUND
 
 
      CALL SELF%LIST%FIND(Key,ptr)
 
      FOUND = ASSOCIATED(ptr)
 
      END FUNCTION HAS_KEY
 
 
!> Push back a TOML value to the table
      SUBROUTINE PUSH_BACK(SELF,Val,Stat)
      IMPLICIT NONE
!*--PUSH_BACK158
 
   !> Instance of the TOML table
 
   !> TOML value to append to table
 
   !> Status of operation
      INTEGER , INTENT(OUT)  ::  Stat
 
      IF ( .NOT.ALLOCATED(Val) ) THEN
         Stat = toml_stat%FATAL
         RETURN
      ENDIF
 
      IF ( .NOT.ALLOCATED(Val%KEY) ) THEN
         Stat = toml_stat%FATAL
         RETURN
      ENDIF
 
      IF ( SELF%HAS_KEY(Val%KEY) ) THEN
         Stat = toml_stat%DUPLICATE_KEY
         RETURN
      ENDIF
 
      CALL SELF%LIST%PUSH_BACK(Val)
 
      Stat = toml_stat%SUCCESS
 
      END SUBROUTINE PUSH_BACK
 
 
!> Delete TOML value at a given key
      SUBROUTINE DELETE(SELF,Key)
      IMPLICIT NONE
!*--DELETE192
 
   !> Instance of the TOML table
 
   !> Key to the TOML value
      CHARACTER(KIND=tfc,LEN=*) , INTENT(IN)  ::  Key
 
      CALL SELF%LIST%DELETE(Key)
 
      END SUBROUTINE DELETE
 
 
!> Deconstructor to cleanup allocations (optional)
      SUBROUTINE DESTROY(SELF)
      IMPLICIT NONE
!*--DESTROY207
 
   !> Instance of the TOML table
 
      IF ( ALLOCATED(SELF%KEY) ) DEALLOCATE (SELF%KEY)
 
      IF ( ALLOCATED(SELF%LIST) ) THEN
         CALL SELF%LIST%DESTROY
         DEALLOCATE (SELF%LIST)
      ENDIF
 
      END SUBROUTINE DESTROY
 
 
      END PROGRAM AA0001

My apologies - the original post was misleading. SPAG supports Fortran 95, but not Fortran 2003 - my unaccountable brain-fog. As a result, SPAG produces quite a few errors when processing your code.

16 occurrences of Statement not recognised
 4 occurrences of Local variable used but not set
 8 occurrences of MODULE not found in PFMODULE.KEY

The use of capitals is very configurable, and I think it likely you could configure it to your taste. The default uses case to distinguish different types of object - for example Fortran keywords and user names

   Case of Variables etc
       (-1=leave,0=lower
         n=n caps then lower
         9=upper)

100=0 case of local variables
101=3 case of COMMON variables
102=1 case of dummy arguments
103=9 case of PARAMETERS
104=9 case of other symbols

   Case of Other Elements

106=1 case of logical operators
(0=lower,1=upper)
107=1 case of FORTRAN keywords
(0=lower,1=upper)
108=0 case of character strings
(0=leave,-1=lower,1=upper)
109=0 case of comments
(0=leave,-1=lower,1=upper)

1 Like

Brian Smith, a former J3 denizen, had a Fortran test harness. I had a CD for it, but I lost it when I retired.

1 Like

Version 7.51, which includes an important bug-fix, has been released.

See www.fortran.uk/currentversion

1 Like

I tried Plusfort, but had no time to properly check out the options it gave me. I recently came across a very nasty bug in an Fortran program I maintain. The bug is specific to fixed-form, and something that in my opinion should not be allowed, but it passes and compiles with GFortran just fine. Then I thought: would plusfort discover such a bug?

I therefore created a small reproducer code with the bug:

      SUBROUTINE multiplication(c, a, b)
          IMPLICIT NONE

          REAL, INTENT(out) :: c
          REAL, INTENT(in) :: a, b

          c = a *
     &        * b
      END SUBROUTINE multiplication

In real life, the code was significantly more complicated than this, of course. This code compiles with Gfortran 9.3. If I rewrite to free-form it does not compile any more.

When I run the above mentioned code through spag it does not complain significantly about anything and re-write the code into:

!*==multiplication.f90  processed by SPAG 7.50RH at 08:56 on  4 Feb 2022
      SUBROUTINE MULTIPLICATION(C,A,B)
      IMPLICIT NONE
!*--MULTIPLICATION4
 
      REAL , INTENT(OUT)  ::  C
      REAL , INTENT(IN)  ::  A , B
 
      C = A**B
      END SUBROUTINE MULTIPLICATION

In my opinion there should have been a warning on this…

2 Likes

I guess it is not a bug. For fixed source form, the standard allows blanks everywhere:

6.3.3 Fixed form
6.3.3.1 General
2 Except in a character context, blanks are insignificant and may be used freely throughout the program.

This is not true for free source form, where blanks are not allowed within lexical tokens (except in character context) - 6.3.2.2.1

It is indeed completely allowed and that is one of the reasons fixed form should be avoided in my opinion. A classical example is:

      do 100 i = 1.100
          write(*,*) i
  100 continue

Can you spot the problem?

2 Likes

It is interpreted as

      do100i = 1.100   ! do100i being a variable name
      write(*,*) i
100   continue         ! unused label
1 Like

Yes, and that is due to the decimal point instead of a comma.

Welcome to the forum, and thanks for the example. At least translating to free source form clarifies what the code does, which the programmer must compare to his intention.

1 Like

@hakostra, consider the following version of your fixed form code:

      SUBROUTINE multiplication(c, a, b)
          IMPLICIT NONE

          REAL, INTENT(out) :: c
          REAL, INTENT(in) :: a, b

          c = a *
     *        * b
      END SUBROUTINE multiplication

It differs only in that the continuation character (in col.6) is ‘*’ instead of ‘&’. What do you expect to be the semantics of this version? What do you expect a Fortran compiler or SPAG to do with it?

1 Like