Table of Contents

Fortran 90 / C Interface

Introduction

Many simulations are still written in Fortran, but even the “object orientation” of Fortran 2003 is incompatible to C++. Because of that Steereo provides a wrapper for the most important library routines for Fortran 90/95/03/08. The wrapper function can also be used for simulations written and compiled with C (don't forget the underscore after the function name). An overview of all wrapper functions can be seen at the steereowrapper page.

What changes

Simulation example

The example will be the same as in the Howto: a simulation, that in each step calculates the average of its neighbours.

The Simulation

averaging.f90
MODULE SimulationFunctions
 
contains
 
SUBROUTINE initArray (arr)
  IMPLICIT NONE
  real(kind=4), intent (inout) :: arr(:)
  integer :: j
  real, parameter :: M_PI = 3.141593
 
  DO j = lbound(arr,1), ubound(arr, 1), 1
     arr(j) = sin (M_PI * real(j-1)/ size(arr)) * cos(real(j-1))
  END DO
END SUBROUTINE initArray
 
SUBROUTINE simulationStep (arr, attenuation)
  IMPLICIT NONE
  real(kind=4), intent (inout) :: arr(:)
  real(kind=4), intent (in) :: attenuation
  integer :: j
 
  DO j = lbound(arr,1)+1, ubound(arr,1)-1, 1
     arr(j) = attenuation * (arr(j-1) + arr(j+1)) / 2
  END DO
  call sleep (1)
END SUBROUTINE simulationStep
 
SUBROUTINE printArray (arr)
  IMPLICIT NONE
  real(kind=4), intent (in) :: arr(:)
  integer :: j
  PRINT*, "-----------------------------"
  DO j = lbound(arr,1), ubound(arr,1), 1
     PRINT*, j, arr(j)
  END DO
  PRINT*, ""
END SUBROUTINE printArray
 
SUBROUTINE incrementStep (stepNr)
  IMPLICIT NONE
  integer, intent (inout) :: stepNr
  stepNr = stepNr + 1
END SUBROUTINE incrementStep
 
END MODULE SimulationFunctions
 
 
PROGRAM AVERAGING
  USE SimulationFunctions
  IMPLICIT NONE
 
  real, allocatable :: A(:)
  integer, parameter :: size = 10
  integer, parameter :: maxStep = 10000
  integer :: step
  real :: att
  real :: testReal
 
  allocate (A(size))
 
  att = 1
  step = 1
 
  call initArray(A)
  DO WHILE (step <= maxStep)
     PRINT*, 'step ', step
     call simulationStep (A, att)
     PRINT*, 'attenuation ', att
     call printArray (A)
     call incrementStep (step)
  END DO
 
END PROGRAM AVERAGING

The used Makefile:

Makefile
F90FLAGS=
F90 = gfortran
 
STEEREO_INC=/home/hpcdjenz/workspace/Steereo/src
STEEREO_LIB=/home/hpcdjenz/workspace/Steereo/src
 
LIBS= -lpthread -lSteereo -L$(STEEREO_LIB) -lc -lstdc++
INCLUDE= -I$(STEEREO_INC)
EXECUTABLE= averaging
 
all: $(EXECUTABLE) $(MODULES)
 
%.o: %.f90
	$(F90) -c $(F90FLAGS) $(INCLUDE) -o $@ $^
 
$(EXECUTABLE): averaging.o
	$(F90) $(F90FLAGS) $(LIBS) -o $(EXECUTABLE) $^
 
clean:
	rm *.o $(EXECUTABLE) $(MODULES)

Enable Steering

As with the C++ simulations, we want to steer the parameters attenuation and A and be able to retrieve the mean value with an self written command. The general proceeding is the mostly the same as for C++ simulations:

  1. Create an instance of SimSteering
  2. Create an instance of a class derived from SteereoCommunicator (e.g. SteereoSocketCommunicator) and assign it to the SimSteering instance
  3. start listening to requests from clients (with a method of the SimSteering object)
  4. process the queue with all the requests the Simulation got (a good place is at the end of your simulation queue)
  5. destroy your SimSteering and SteereoCommunicator instances (you don't need to, but you should do it)

First you have to add these function stubs and variable declarations:

integer simsteer_new
integer socketcommunicator_new
 
integer simSteer, socketComm, port

Then insert these lines before the main loop:

! 1.
simSteer = simsteer_new ()
port = 44445
! 2.
socketComm = socketcommunicator_new (port)
call simsteer_setcommunicator (simSteer, socketComm)
! 3.
call simsteer_startlistening (simSteer)

this line into the loop:

! 4.
call simsteer_processqueue (simSteer)

and last but not least at the end of your code:

! 5.
call simsteer_delete (simSteer)
call socketcommunicator_delete (socketComm)

Parameter Steering

Enabling the parameter steering functionality is also not very difficult, but alas the registering of variables is not quite as comfortable as with C++. We want to steer the parameters att and A. To do this just first define some helper variables:

character :: parameterName*128
real(kind=4) :: minValue, maxValue
integer :: stringSize

Register the two parameters now:

parameterName= 'attenuation'
stringSize=LEN_TRIM(parameterName)
call register_scalar_float_parameter (TRIM(parameterName), stringSize, att, minValue, maxValue) 
parameterName= 'dataArray'
stringSize=LEN_TRIM(parameterName)
call register_array_float_parameter (TRIM(parameterName), stringSize, A, size)

After these enhancements the two parameters can already be steered by the appropriate client (e.g. the client from the Howto or the Fortran Client, that will be described some paragraphs further).

Using Commands

The Commands still have to be written in C++, and the command for the Fortran Simulation is very similar to the command in the Howto (the use of SimulationData has been removed). One important addition is the last line in the code file, COMMAND_AUTOREG(MeanValueCommand), which is enables you to load the command in the simulation. The name you give your command in the constructor also gains more importance, as this will be the name, clients have to use to request the command.

meanValueCommand.h
#ifndef MEANVALUECOMMAND_H_
#define MEANVALUECOMMAND_H_
#include <steereoCommand.h>
 
class MeanValueCommand : public SteereoCommand
{
public:
	MeanValueCommand();
	virtual ~MeanValueCommand();
	ReturnType execute ();
	void setParameters (std::list<std::string> params);
	static SteereoCommand* generateNewInstance ();
 
private:
	int startIndex, endIndex;
};
#endif /* MEANVALUECOMMAND_H_ */
meanValueCommand.cpp
#include "meanValueCommand.h"
 
MeanValueCommand::MeanValueCommand() : SteereoCommand (false, "meanValueCommand")
{
}
 
MeanValueCommand::~MeanValueCommand()
{
}
 
 
ReturnType MeanValueCommand::execute ()
{
  double* data = (double*) (this->getData());
  double sum = 0;
  int valueNumber = endIndex - startIndex + 1;
  for (int i = startIndex; i <= endIndex; i++)
  {
    sum += data[i];
  }
  if (valueNumber > 0)
  {
    sum /= valueNumber;
  }
  this->getCommunicator()->sendDouble(this->getConnection()->getDataConnection(), sum);
  return EXECUTED;
}
 
void MeanValueCommand::setParameters (std::list<std::string> params)
{
  startIndex = 0;
  endIndex = this->getDataSize() - 1;
  int paramNum = params.size();
  if (paramNum >= 1)
  {
    startIndex = atoi (params.front().c_str());
    params.pop_front();
  }
  if (paramNum >= 2)
  {
    endIndex = atoi (params.front().c_str());
    params.pop_front();
  }
  std::cout << "** Parameters set are " << startIndex << " and " << endIndex << std::endl;
}
 
SteereoCommand* MeanValueCommand::generateNewInstance ()
{
  return new MeanValueCommand;
}
 
COMMAND_AUTOREG(MeanValueCommand)

The following Makefile will now also compile the Command as a dynamic library:

Makefile
CPP= g++
CC = gcc
F90FLAGS=
F90 = gfortran
 
STEEREO_INC=<<<PATH_TO_YOUR_STEEREO_SRC_DIR>>>
STEEREO_LIB=<<<PATH_TO_YOUR_STEEREO_SRC_DIR>>>
 
LIBS= -lpthread -lSteereo -L$(STEEREO_LIB) -lc -lstdc++
INCLUDE= -I$(STEEREO_INC)
EXECUTABLE= averaging
MODULES= meanValueCommand.so 
 
all: $(EXECUTABLE) $(MODULES)
 
%.o: %.f90
	$(F90) -c $(F90FLAGS) $(INCLUDE) -o $@ $^
 
%.so: %.cpp
	$(CPP) $(CPPFLAGS) $(INCLUDE) -fPIC -shared -Wl,-soname,$@ -o $@ $^
 
$(EXECUTABLE): averaging.o
	$(F90) $(F90FLAGS) $(LIBS) -o $(EXECUTABLE) $^
 
clean:
	rm *.o $(EXECUTABLE) $(MODULES)

Client example

It is also possible to write a steering client, though it is not recommended. But maybe you want to integrate it in an existing Fortran 90/95 program … .