A Simple Way to Combine Fortran and Stata in Your workflow

Lin Shao, 2016


Introduction

Our tasks are best done by utilizing the comparative advantage of different tools. As an example, we might want to use Stata for data processing and Fortran for optimization. Yet sometimes, the Stata task and the Fortran task have to be done sequentially. And we do not want to sit still and wait for the Stata task to end and manually begin the Fortran task.

Consider for example the following set of tasks:

  1. Data processing, generate some data moments (Stata)
  2. Optimization using the data moments (Fortran)
  3. More manipulation of data using the optimization results from the previous step (Stata)

Step 2 can not start until step 1 is finished; and step 3 can not start until step 2 is finished. If we do this manually, we would have to run Stata code for step 1, wait for it to end; and start the Fortran code, wait for it to end; and start another Stata code again.

In this notes, I introduce a very simple way to deal with this problem, so that we can free ourselves from the computer.

The basic ideas is that, we ask Stata to send a “go ahead” message to Fortran when step 1 is finished; and ask Fortran to send a “go ahead” message to Stata when step 2 is finished.

More specifically, we maintain two “signal” files (simple text files would do). The first file deals with the sequentiality between step 1 and 2: Stata writes signal into this file and Fortran reads signal from the file. Once Stata finishes step 1, it opens this file and writes a signal into it. At the same time, we tell Fortran to wait while repeatedly read the same file until it receives a signal to move on. The wait process can be implemented by a simple “do while” loop.

Similarly, the second file deals with sequentiality between step 2 and 3. The only difference is now Fortran writes and Stata reads. Stata is asked to wait until the signal says to move ahead. The wait process can be implemented using a simple “while” loop.

Below are examples of Fortran and Stata code. Run the Fortran code first, because it initializes the signal files.


Fortran file

  
  PROGRAM MAIN
    INTEGER :: iwait
    ! sig_ok = 0 means wait; sig_ok = 1 means go ahead
    REAL :: sig_ok                

  !***Initializing the signal files
    sig_ok = 0
    OPEN(UNIT = 1, FILE='sig_ok_1.txt')
      WRITE(1,*) sig_ok
    CLOSE(UNIT=1)
    OPEN(UNIT = 2, FILE='sig_ok_2.txt')
      WRITE(2,*) sig_ok
    CLOSE(UNIT=2)
  !******Wait for the signal from STATA to proceed
    iwait = 0
    sig_ok = 0
    DO WHILE(sig_ok .EQ. 0)
      ! Ask Fortran to read the signal file once every 5 seconds
      CALL SLEEP(5)               
      OPEN(UNIT = 1, FILE='sig_ok_1.txt')
        READ(1,*) sig_ok
      CLOSE(UNIT=1)
      iwait=iwait+1
      PRINT *, "iwait", iwait
    ENDDO
  !******Optimization 
      PRINT *, "Step two finished"
  !******Send a signal to STATA to proceed
      sig_ok = 1
      OPEN(UNIT = 2, FILE='sig_ok_2.txt')
        WRITE(2,*) sig_ok
      CLOSE(UNIT=2)
  END PROGRAM MAIN
  

Stata file

  
    ******Step one
    display "Step one finished"
    
    ******Send the signal to Fortran
    clear
    set obs 1
    generate var1 = 1 in 1
    export delimited var1 using "sig_ok_1.txt", delimiter(tab) novarnames replace
    
    ******Wait for the signal from Fortran 
    local sig_ok = 0
    while `sig_ok' == 0{
        import delimited "sig_ok_2.txt", clear
        local sig_ok = v1[1]
        display `sig_ok'
    }
    ******Step three
    display "Step three finished"