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.
The example will be the same as in the Howto: a simulation, that in each step calculates the average of its neighbours.
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:
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)
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:
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)
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).
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.
#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_ */
#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:
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)
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 … .