Index: Source/Fortran/Makefile.CRAY
===================================================================
--- Source/Fortran/Makefile.CRAY	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/Makefile.CRAY	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Top level Makefile for ECMWFDATA7.0 software
+#
+# Last modified:  December 1, 2015
+#
+###############################################################################
+
+
+.SUFFIXES: .o .c .c~ .f .f~ .F90 .f90 .f90~ .f95 .f95~ .F .F~ .y .y~ .l .l~ \
+           .s .s~ .sh .sh~ .h .h~ .C .C~ .a
+
+OPT	= 
+DEBUG	= -g
+LIB     =  $(GRIB_API_LIBS) $(EMOSLIB)
+
+FC=ftn  $(F90FLAGS)
+F90C=ftn    $(F90FLAGS)
+
+FFLAGS =  $(OPT) -I. -r8 -I$(GRIB_API_INCLUDE_DIR)
+F90FLAGS =  $(OPT) -I. -r8 -I$(GRIB_API_INCLUDE_DIR)
+
+LDFLAGS =  $(OPT)
+
+BINDIR  =      .
+
+EXE	=      CONVERT2
+
+
+.f.o:
+	$(F90C) -c $(F90FLAGS) $(DEBUG) $*.f
+.f90.o:
+	$(F90C) -c $(F90FLAGS) $(DEBUG) $*.f90
+
+all:	${EXE}
+
+clean:
+	rm *.o *.mod ${EXE}
+
+phgrreal.o: phgrreal.f
+	$(F90C) -c -g -O3 phgrreal.f
+
+grphreal.o: grphreal.f
+	$(F90C) -c -g -O3 grphreal.f
+
+ftrafo.o: ftrafo.f
+	$(F90C) -c -g -O3 ftrafo.f
+
+$(BINDIR)/${EXE}:	phgrreal.o grphreal.o ftrafo.o rwGRIB2.o  posnam.o preconvert.o
+	$(F90C) $(DEBUG) $(OPT) -o $(BINDIR)/${EXE} ftrafo.o phgrreal.o grphreal.o rwGRIB2.o posnam.o preconvert.o ${LIB}
+
+
+###############################################################################
+#
+# End of the Makefile
+#
+###############################################################################
Index: Source/Fortran/Makefile.gfortran
===================================================================
--- Source/Fortran/Makefile.gfortran	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/Makefile.gfortran	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Top level Makefile for ECMWFDATA7.0 software
+#
+# Last modified:  December 1, 2015
+#
+###############################################################################
+
+
+.SUFFIXES: .o .c .c~ .f .f~ .F90 .f90 .f90~ .f95 .f95~ .F .F~ .y .y~ .l .l~ \
+           .s .s~ .sh .sh~ .h .h~ .C .C~ .a
+
+OPT	= -O3
+DEBUG	= -g
+LIB     =  $(ECCODES_LIB) $(EMOSLIB)   
+
+FC=gfortran   -m64 -fdefault-real-8 -fcray-pointer -fno-second-underscore  -ffixed-line-length-132 -fopenmp  -fconvert=big-endian  
+F90C=gfortran   -m64 -fdefault-real-8 -fcray-pointer -fno-second-underscore  -ffixed-line-length-132 -fopenmp  -fconvert=big-endian 
+
+FFLAGS =  $(OPT) -I. -I$(ECCODES_INCLUDE_DIR) 
+
+F90FLAGS =  $(OPT) -I. -I$(ECCODES_INCLUDE_DIR)
+
+LDFLAGS =  $(OPT)
+
+BINDIR  =      .
+
+EXE	=      CONVERT2
+
+
+.f.o:
+	$(F90C) -c $(F90FLAGS) $(DEBUG) $*.f
+.f90.o:
+	$(F90C) -c $(F90FLAGS) $(DEBUG) $*.f90
+
+all:	${EXE}
+
+clean:
+	rm *.o *.mod ${EXE}
+
+phgrreal.o: phgrreal.f
+	$(F90C) -c -g -O3 -fopenmp phgrreal.f
+
+grphreal.o: grphreal.f
+	$(F90C) -c -g -O3 -fopenmp grphreal.f
+
+ftrafo.o: ftrafo.f
+	$(F90C) -c -g -O3 -fopenmp ftrafo.f
+
+$(BINDIR)/${EXE}:	phgrreal.o grphreal.o ftrafo.o rwGRIB2.o  posnam.o preconvert.o
+	$(F90C) $(DEBUG) $(OPT) -o $(BINDIR)/${EXE} ftrafo.o phgrreal.o grphreal.o rwGRIB2.o posnam.o preconvert.o ${LIB}
+
+
+###############################################################################
+#
+# End of the Makefile
+#
+###############################################################################
Index: Source/Fortran/Makefile.local.gfortran
===================================================================
--- Source/Fortran/Makefile.local.gfortran	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/Makefile.local.gfortran	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Top level Makefile for ECMWFDATA7.0 software
+#
+# Last modified:  December 1, 2015
+#
+###############################################################################
+
+
+.SUFFIXES: .o .c .c~ .f .f~ .F90 .f90 .f90~ .f95 .f95~ .F .F~ .y .y~ .l .l~ \
+           .s .s~ .sh .sh~ .h .h~ .C .C~ .a
+
+
+GRIB_API_INCLUDE_DIR=/usr/local/gcc-4.9.3/grib_api-1.14.3/include
+GRIB_API_LIB= -L/usr/local/gcc-4.9.3/grib_api-1.14.3/lib -Bstatic  -lgrib_api_f77 -lgrib_api_f90 -lgrib_api -Bdynamic  -lm  -ljasper
+
+EMOSLIB=-lemosR64
+
+OPT	= -O3 -fopenmp
+DEBUG	= -O3
+LIB     =  $(GRIB_API_LIB) $(EMOSLIB)
+
+FC=gfortran   -m64 -fdefault-real-8 -fcray-pointer -fno-second-underscore  -ffixed-line-length-132 -fopenmp  -fconvert=big-endian  
+F90C=gfortran   -m64 -fdefault-real-8 -fcray-pointer -fno-second-underscore  -ffixed-line-length-132 -fopenmp  -fconvert=big-endian 
+
+FFLAGS =  $(OPT) -I. -I$(GRIB_API_INCLUDE_DIR)
+F90FLAGS =  $(OPT) -I. -I$(GRIB_API_INCLUDE_DIR)
+
+LDFLAGS =  $(OPT)
+
+BINDIR  =      .
+
+EXE	=      CONVERT2
+
+
+.f.o:
+	$(F90C) -c $(F90FLAGS) $(DEBUG) $*.f
+.f90.o:
+	$(F90C) -c $(F90FLAGS) $(DEBUG) $*.f90
+
+all:	${EXE}
+
+clean:
+	rm *.o *.mod ${EXE}
+
+phgrreal.o: phgrreal.f
+	$(F90C) -c -g -O3 -fopenmp phgrreal.f
+
+grphreal.o: grphreal.f
+	$(F90C) -c -g -O3 -fopenmp grphreal.f
+
+ftrafo.o: ftrafo.f
+	$(F90C) -c -g -O3 -fopenmp ftrafo.f
+
+$(BINDIR)/${EXE}:	phgrreal.o grphreal.o ftrafo.o rwGRIB2.o  posnam.o preconvert.o
+	$(F90C) $(DEBUG) $(OPT) -o $(BINDIR)/${EXE} ftrafo.o phgrreal.o grphreal.o rwGRIB2.o posnam.o preconvert.o ${LIB}
+
+
+###############################################################################
+#
+# End of the Makefile
+#
+###############################################################################
Index: Source/Fortran/Makefile.local.ifort
===================================================================
--- Source/Fortran/Makefile.local.ifort	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/Makefile.local.ifort	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Top level Makefile for ECMWFDATA7.0 software
+#
+# Last modified:  December 1, 2015
+#
+###############################################################################
+
+
+.SUFFIXES: .o .c .c~ .f .f~ .F90 .f90 .f90~ .f95 .f95~ .F .F~ .y .y~ .l .l~ \
+           .s .s~ .sh .sh~ .h .h~ .C .C~ .a
+
+GRIB_API_INCLUDE_DIR=/usr/local/include
+GRIB_API_LIB= -L/usr/local/lib -leccodes_f90  -leccodes -lm  -ljasper
+
+
+OPT	= -g -O3 -mcmodel=medium -unroll -inline -heap-arrays 32 
+DEBUG	= ''
+LIB     =  $(GRIB_API_LIB) -lemosR64 -lgfortran
+
+FC=/opt/intel/composerxe/bin/ifort -132  -traceback -r8
+F90C=/opt/intel/composerxe/bin/ifort -132  -traceback -r8
+
+FFLAGS =  $(OPT) -I. -I$(GRIB_API_INCLUDE_DIR)
+F90FLAGS =  $(OPT) -I. -I$(GRIB_API_INCLUDE_DIR)
+
+LDFLAGS =  $(OPT)
+
+BINDIR  =      .
+
+EXE	=      CONVERT2.s8.ifort
+
+
+.f.o:
+	$(F90C) -c $(F90FLAGS) -132 $(DEBUG) $*.f
+.f90.o:
+	$(F90C) -c $(F90FLAGS) -132 $(DEBUG) $*.f90
+
+all:	${EXE}
+
+clean:
+	rm *.o *.mod ${EXE}
+
+phgrreal.o: phgrreal.f
+	$(F90C) -c -g -O3 phgrreal.f
+
+grphreal.o: grphreal.f
+	$(F90C) -c -g -O3 grphreal.f
+
+ftrafo.o: ftrafo.f
+	$(F90C) -c -g -O3 ftrafo.f
+
+$(BINDIR)/${EXE}:	phgrreal.o grphreal.o ftrafo.o rwGRIB2.o  posnam.o preconvert.o
+	$(F90C) $(DEBUG) $(OPT) -o $(BINDIR)/${EXE} ftrafo.o phgrreal.o grphreal.o rwGRIB2.o posnam.o preconvert.o ${LIB}
+
+
+###############################################################################
+#
+# End of the Makefile
+#
+###############################################################################
Index: Source/Fortran/ftrafo.f
===================================================================
--- Source/Fortran/ftrafo.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/ftrafo.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,504 @@
+	MODULE FTRAFO
+
+	CONTAINS
+
+
+
+C
+C	Implementierung der spektralen Transformationsmethode unter Verwendung
+C	des reduzierten Gauss'schen Gitters
+C	
+C Berechnung der scale winds aus Vorticity und Divergenz
+C uebergibt man in XMN die Divergenz, so wird der divergente Anteil des
+C Windes (XPHI=Ud,XPHI=Vd) zurueckgegeben, uebergibt man die Vorticity, so 
+C erhaelt man den rotationellen Wind (XLAM=Vrot,XPHI=-Urot).
+C Summiert man beide, erhaelt man den gesamten Scale wind
+C	GWSAVE ist ein Hilfsfeld fuer die FFT
+C	P enthaelt die assoziierten Legendrepolynome, H deren Ableitung
+C	MLAT enthaelt die Anzahl der Gitterpunkte pro Breitenkreis
+C	MNAUF gibt die spektrale Aufloesung an,
+C	NI = Anzahl der Gauss'schen Gitterpunkte pro Flaeche
+C	NJ = Anzahl der Gauss'schen Breiten,
+C	NK = Anzahl der Niveaus
+ 
+	SUBROUTINE VDTOUV(XMN,XLAM,XPHI,GWSAVE,IFAX,P,MLAT,MNAUF,NI,NJ,NK)
+
+
+	USE PHTOGR
+
+	IMPLICIT NONE
+	INTEGER   J,N,NI,NJ,NK,MNAUF,GGIND(NJ/2)
+	INTEGER		MLAT(NJ),IFAX(10,NJ)
+	REAL			XMN(0:(MNAUF+1)*(MNAUF+2)-1,NK)
+	REAL		P(0:(MNAUF+3)*(MNAUF+4)/2,NJ/2)
+	REAL		H(0:(MNAUF+2)*(MNAUF+3)/2)
+	REAL			XLAM(NI,NK),XPHI(NI,NK)
+	REAL			GWSAVE(8*NJ+15,NJ/2)
+	REAL 		SCR,SCI,ACR,ACI,MUSCR,MUSCI,MUACR,MUACI
+	REAL	  RT,IT
+
+	GGIND(1)=0
+	DO 4 J = 2,NJ/2	   	   
+	  GGIND(J)=GGIND(J-1)+MLAT(J-1)
+4	CONTINUE
+!$OMP PARALLEL DO SCHEDULE(DYNAMIC)
+	DO 5 J = 1,NJ/2
+	   CALL VDUVSUB(J,XMN,XLAM,XPHI,GWSAVE,IFAX,P,GGIND(J),
+     *MLAT,MNAUF,NI,NJ,NK)   
+ 5	CONTINUE
+!$OMP END PARALLEL DO
+	RETURN
+	END SUBROUTINE VDTOUV
+
+	SUBROUTINE VDUVSUB(J,XMN,XLAM,XPHI,GWSAVE,IFAX,P,
+     *GGIND,MLAT,MNAUF,NI,NJ,NK) 
+
+	USE PHTOGR
+
+	IMPLICIT NONE
+	INTEGER   J,K,M,N,NI,NJ,NK,MNAUF,GGIND,LL,LLP,LLH,LLS,LLPS,LLHS
+	INTEGER		MLAT(NJ),IFAX(10,NJ)
+	REAL     	UFOUC(0:MAXAUF),MUFOUC(0:MAXAUF)
+	REAL 			VFOUC(0:MAXAUF),MVFOUC(0:MAXAUF)
+	REAL			XMN(0:(MNAUF+1)*(MNAUF+2)-1,NK)
+	REAL		P(0:(MNAUF+3)*(MNAUF+4)/2,NJ/2)
+	REAL		H(0:(MNAUF+2)*(MNAUF+3)/2)
+	REAL			XLAM(NI,NK),XPHI(NI,NK)
+	REAL			GWSAVE(8*NJ+15,NJ/2)
+	REAL 		ERAD,SCR,SCI,ACR,ACI,MUSCR,MUSCI,MUACR,MUACI
+	REAL	  FAC(0:MNAUF),RT,IT
+  
+	
+	ERAD = 6367470.D0
+	
+	FAC(0)=0.D0
+	DO 12 N=1,MNAUF
+	  FAC(N)=-ERAD/DBLE(N)/DBLE(N+1)
+12	CONTINUE
+
+	 CALL DPLGND(MNAUF,P(0,J),H)
+	 DO 3 K = 1,NK
+	  LL=0
+	  LLP=0
+	  LLH=0
+	  DO 2 M = 0,MNAUF
+	   SCR=0.D0
+	   SCI=0.D0
+	   ACR=0.D0
+	   ACI=0.D0
+	   MUSCR=0.D0
+	   MUSCI=0.D0
+	   MUACR=0.D0
+	   MUACI=0.D0
+	   LLS=LL
+	   LLPS=LLP
+	   LLHS=LLH
+	   IF(2*M+1.LT.MLAT(J)) THEN
+	      DO 1 N = M,MNAUF,2
+	       RT=XMN(2*LL,K)*FAC(N)
+	       IT=XMN(2*LL+1,K)*FAC(N)
+	       SCR =SCR+ RT*P(LLP,J)
+	       SCI =SCI+ IT*P(LLP,J)
+	       MUACR =MUACR+ RT*H(LLH)
+	       MUACI =MUACI+ IT*H(LLH)
+	       LL=LL+2
+	       LLP=LLP+2
+	       LLH=LLH+2
+ 1	      CONTINUE
+	      LL=LLS+1
+	      LLP=LLPS+1
+	      LLH=LLHS+1
+	      DO 11 N = M+1,MNAUF,2
+	       RT=XMN(2*LL,K)*FAC(N)
+	       IT=XMN(2*LL+1,K)*FAC(N)
+	       ACR =ACR+ RT*P(LLP,J)
+	       ACI =ACI+ IT*P(LLP,J)
+	       MUSCR =MUSCR+ RT*H(LLH)
+	       MUSCI =MUSCI+ IT*H(LLH)
+	       LL=LL+2
+	       LLP=LLP+2
+	       LLH=LLH+2
+ 11	      CONTINUE
+	   ENDIF
+	   LL=LLS+(MNAUF-M+1)
+	   LLP=LLPS+(MNAUF-M+3)
+	   LLH=LLHS+(MNAUF-M+2)
+
+	   UFOUC(2*M)=-M*(SCI-ACI)
+	   UFOUC(2*M+1)=M*(SCR-ACR)
+	   VFOUC(2*M)=-M*(SCI+ACI)
+	   VFOUC(2*M+1)=M*(SCR+ACR)
+	   
+	   MUFOUC(2*M)=-(MUSCR-MUACR)
+	   MUFOUC(2*M+1)=-(MUSCI-MUACI)
+	   MVFOUC(2*M)=-(MUSCR+MUACR)
+	   MVFOUC(2*M+1)=-(MUSCI+MUACI)
+ 2	  CONTINUE
+			
+	 CALL RFOURTR(VFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XLAM(GGIND+1:GGIND+MLAT(J),K)=VFOUC(0:MLAT(J)-1)
+	 CALL RFOURTR(UFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XLAM(NI-GGIND-MLAT(J)+1:NI-GGIND,K)=UFOUC(0:MLAT(J)-1)
+			
+	 CALL RFOURTR(MVFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XPHI(GGIND+1:GGIND+MLAT(J),K)=MVFOUC(0:MLAT(J)-1)
+	 CALL RFOURTR(MUFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XPHI(NI-GGIND-MLAT(J)+1:NI-GGIND,K)=MUFOUC(0:MLAT(J)-1)
+
+3		CONTINUE
+
+      RETURN
+      END SUBROUTINE VDUVSUB
+
+C Berechnung des Gradienten eines Skalars aus dem Feld des
+C	Skalars XMN im Phasenraum. Zurueckgegeben werden die Felder der
+C	Komponenten des horizontalen Gradienten XLAM,XPHI auf dem Gauss'schen Gitter.
+C	GWSAVE ist ein Hilfsfeld fuer die FFT
+C	P enthaelt die assoziierten Legendrepolynome, H deren Ableitung
+C	MLAT enthaelt die Anzahl der Gitterpunkte pro Breitenkreis
+C	MNAUF gibt die spektrale Aufloesung an, 
+C	NI = Anzahl der Gauss'schen Gitterpunkte,
+C	NJ = Anzahl der Gauss'schen Breiten,
+C	NK = Anzahl der Niveaus
+ 
+	SUBROUTINE PHGRAD(XMN,XLAM,XPHI,GWSAVE,IFAX,P,H,MLAT,
+     *MNAUF,NI,NJ,NK)
+
+	USE PHTOGR
+	IMPLICIT NONE
+	INTEGER   J,K,M,N,NI,NJ,NK,MNAUF,GGIND,LL,LLP,LLH,LLS,LLPS,LLHS
+	INTEGER		MLAT(NJ),IFAX(10,NJ)
+	REAL     	UFOUC(0:MAXAUF),MUFOUC(0:MAXAUF)
+	REAL 			VFOUC(0:MAXAUF),MVFOUC(0:MAXAUF)
+	REAL			XMN(0:(MNAUF+1)*(MNAUF+2)-1,NK)
+	REAL		P(0:(MNAUF+3)*(MNAUF+4)/2,NJ/2)
+	REAL		H(0:(MNAUF+2)*(MNAUF+3)/2)
+	REAL			XLAM(NI,NK),XPHI(NI,NK)
+	REAL			GWSAVE(8*NJ+15,NJ/2)
+	REAL      ERAD
+	REAL 		SCR,SCI,ACR,ACI,MUSCR,MUSCI,MUACR,MUACI,RT,IT
+	
+	ERAD = 6367470.0
+	
+	GGIND=0
+	DO 4 J = 1,NJ/2
+	 CALL DPLGND(MNAUF,P(0,J),H)
+	 DO 3 K = 1,NK
+	  LL=0
+	  LLP=0
+	  LLH=0
+	  DO 2 M = 0,MNAUF
+	   SCR=0.D0
+	   SCI=0.D0
+	   ACR=0.D0
+	   ACI=0.D0
+	   MUSCR=0.D0
+	   MUSCI=0.D0
+	   MUACR=0.D0
+	   MUACI=0.D0
+	   LLS=LL
+	   LLPS=LLP
+	   LLHS=LLH
+	   IF(2*M+1.LT.MLAT(J)) THEN
+	      DO 1 N = M,MNAUF,2
+	       RT=XMN(2*LL,K)
+	       IT=XMN(2*LL+1,K)
+	       SCR =SCR+ RT*P(LLP,J)
+	       SCI =SCI+ IT*P(LLP,J)
+	       MUACR =MUACR+RT*H(LLH)
+	       MUACI =MUACI+ IT*H(LLH)
+	       LL=LL+2
+	       LLP=LLP+2
+	       LLH=LLH+2
+ 1	      CONTINUE
+	      LL=LLS+1
+	      LLP=LLPS+1
+	      LLH=LLHS+1
+	      DO 11 N = M+1,MNAUF,2
+	       RT=XMN(2*LL,K)
+	       IT=XMN(2*LL+1,K)
+	       ACR =ACR+ RT*P(LLP,J)
+	       ACI =ACI+ IT*P(LLP,J)
+	       MUSCR =MUSCR+ RT*H(LLH)
+	       MUSCI =MUSCI+ IT*H(LLH)
+	       LL=LL+2
+	       LLP=LLP+2
+	       LLH=LLH+2
+ 11	      CONTINUE
+	   ENDIF
+          LL=LLS+(MNAUF-M+1)
+          LLP=LLPS+(MNAUF-M+3)
+          LLH=LLHS+(MNAUF-M+2)
+
+		UFOUC(2*M)=-M*(SCI-ACI)/ERAD
+		UFOUC(2*M+1)=M*(SCR-ACR)/ERAD
+		VFOUC(2*M)=-M*(SCI+ACI)/ERAD
+		VFOUC(2*M+1)=M*(SCR+ACR)/ERAD
+
+		MUFOUC(2*M)=-(MUSCR-MUACR)/ERAD
+		MUFOUC(2*M+1)=-(MUSCI-MUACI)/ERAD
+		MVFOUC(2*M)=-(MUSCR+MUACR)/ERAD
+		MVFOUC(2*M+1)=-(MUSCI+MUACI)/ERAD
+2	CONTINUE
+
+	 CALL RFOURTR(VFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XLAM(GGIND+1:GGIND+MLAT(J),K)=VFOUC(0:MLAT(J)-1)
+	 CALL RFOURTR(UFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XLAM(NI-GGIND-MLAT(J)+1:NI-GGIND,K)=UFOUC(0:MLAT(J)-1)
+			
+	 CALL RFOURTR(MVFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XPHI(GGIND+1:GGIND+MLAT(J),K)=MVFOUC(0:MLAT(J)-1)
+	 CALL RFOURTR(MUFOUC,
+     *GWSAVE(:,J),IFAX(:,J),MNAUF,MLAT(J),1)
+         XPHI(NI-GGIND-MLAT(J)+1:NI-GGIND,K)=MUFOUC(0:MLAT(J)-1)
+			
+3		CONTINUE
+		GGIND=GGIND+MLAT(J)
+4	CONTINUE
+
+
+	RETURN
+	END SUBROUTINE PHGRAD
+
+C Berechnung des Gradienten eines Skalars aus dem Feld des
+C	Skalars XMN im Phasenraum. Zurueckgegeben werden die Felder der
+C	Komponenten des horizontalen Gradienten XLAM,XPHI auf dem Gauss'schen Gitter.
+C	GWSAVE ist ein Hilfsfeld fuer die FFT
+C	P enthaelt die assoziierten Legendrepolynome, H deren Ableitung
+C	MLAT enthaelt die Anzahl der Gitterpunkte pro Breitenkreis
+C	MNAUF gibt die spektrale Aufloesung an, 
+C	NI = Anzahl der Gauss'schen Gitterpunkte,
+C	NJ = Anzahl der Gauss'schen Breiten,
+C	NK = Anzahl der Niveaus
+ 
+	SUBROUTINE PHGRACUT(XMN,XLAM,XPHI,GWSAVE,IFAX,P,H,MAUF,
+     *MNAUF,NI,NJ,MANF,NK)
+
+	USE PHTOGR
+	IMPLICIT NONE
+	INTEGER   J,K,M,N,NI,NJ,NK,MNAUF,GGIND,LL,LLP,LLH,LLS,LLPS,LLHS
+	INTEGER		MAUF,MANF,I,IFAX(10)
+	REAL     	UFOUC(0:MAXAUF),MUFOUC(0:MAXAUF)
+	REAL 			VFOUC(0:MAXAUF),MVFOUC(0:MAXAUF)
+	REAL			XMN(0:(MNAUF+1)*(MNAUF+2)-1,NK)
+	REAL		P(0:(MNAUF+3)*(MNAUF+4)/2,NJ)
+	REAL		H(0:(MNAUF+2)*(MNAUF+3)/2)
+	REAL			XLAM(NI,NJ,NK),XPHI(NI,NJ,NK)
+	REAL			HLAM(MAXAUF,2),HPHI(MAXAUF,2)
+	REAL			GWSAVE(4*MAUF+15)
+	REAL      ERAD
+	REAL 		SCR,SCI,ACR,ACI,MUSCR,MUSCI,MUACR,MUACI,RT,IT
+	
+	ERAD = 6367470.0
+	
+	GGIND=0
+	DO 4 J = 1,NJ
+	 CALL DPLGND(MNAUF,P(0,J),H)
+	 DO 3 K = 1,NK
+	  LL=0
+	  LLP=0
+	  LLH=0
+	  DO 2 M = 0,MNAUF
+	   SCR=0.D0
+	   SCI=0.D0
+	   ACR=0.D0
+	   ACI=0.D0
+	   MUSCR=0.D0
+	   MUSCI=0.D0
+	   MUACR=0.D0
+	   MUACI=0.D0
+	   LLS=LL
+	   LLPS=LLP
+	   LLHS=LLH
+	   IF(2*M+1.LT.MAUF) THEN
+	      DO 1 N = M,MNAUF,2
+	       RT=XMN(2*LL,K)
+	       IT=XMN(2*LL+1,K)
+	       SCR =SCR+ RT*P(LLP,J)
+	       SCI =SCI+ IT*P(LLP,J)
+	       MUACR =MUACR+RT*H(LLH)
+	       MUACI =MUACI+ IT*H(LLH)
+	       LL=LL+2
+	       LLP=LLP+2
+	       LLH=LLH+2
+ 1	      CONTINUE
+	      LL=LLS+1
+	      LLP=LLPS+1
+	      LLH=LLHS+1
+	      DO 11 N = M+1,MNAUF,2
+	       RT=XMN(2*LL,K)
+	       IT=XMN(2*LL+1,K)
+	       ACR =ACR+ RT*P(LLP,J)
+	       ACI =ACI+ IT*P(LLP,J)
+	       MUSCR =MUSCR+ RT*H(LLH)
+	       MUSCI =MUSCI+ IT*H(LLH)
+	       LL=LL+2
+	       LLP=LLP+2
+	       LLH=LLH+2
+ 11	      CONTINUE
+	   ENDIF
+          LL=LLS+(MNAUF-M+1)
+          LLP=LLPS+(MNAUF-M+3)
+          LLH=LLHS+(MNAUF-M+2)
+
+		UFOUC(2*M)=-M*(SCI-ACI)/ERAD
+		UFOUC(2*M+1)=M*(SCR-ACR)/ERAD
+		VFOUC(2*M)=-M*(SCI+ACI)/ERAD
+		VFOUC(2*M+1)=M*(SCR+ACR)/ERAD
+
+		MUFOUC(2*M)=-(MUSCR-MUACR)/ERAD
+		MUFOUC(2*M+1)=-(MUSCI-MUACI)/ERAD
+		MVFOUC(2*M)=-(MUSCR+MUACR)/ERAD
+		MVFOUC(2*M+1)=-(MUSCI+MUACI)/ERAD
+2			CONTINUE
+			
+		CALL RFOURTR(VFOUC,
+     *GWSAVE,IFAX,MNAUF,MAUF,1)
+			
+		CALL RFOURTR(MVFOUC,
+     *GWSAVE,IFAX,MNAUF,MAUF,1)
+			
+            DO 6 I=0,NI-1
+                  IF(MANF+I.LE. MAUF) THEN
+                    XLAM(I+1,J,K)=VFOUC(MANF+I-1)
+                    XPHI(I+1,J,K)=MVFOUC(MANF+I-1)
+                  ELSE
+                    XLAM(I+1,J,K)=VFOUC(MANF-MAUF+I-1)
+                    XPHI(I+1,J,K)=MVFOUC(MANF-MAUF+I-1)
+                  ENDIF
+    6   CONTINUE
+3		CONTINUE
+		GGIND=GGIND+MAUF
+4	CONTINUE
+
+	RETURN
+	END SUBROUTINE PHGRACUT
+
+C Berechnung der Divergenz aus dem Windfeld (U,V)
+C	im Phasenraum. Zurueckgegeben werden die Felder der
+C	Komponenten des horizontalen Gradienten XLAM,XPHI auf dem Gauss'schen Gitter.
+C	GWSAVE ist ein Hilfsfeld fuer die FFT
+C	P enthaelt die assoziierten Legendrepolynome, H deren Ableitung
+C	MLAT enthaelt die Anzahl der Gitterpunkte pro Breitenkreis
+C	MNAUF gibt die spektrale Aufloesung an, 
+C	NI = Anzahl der Gauss'schen Gitterpunkte,
+C	NJ = Anzahl der Gauss'schen Breiten,
+C	NK = Anzahl der Niveaus
+C Beachte, dass das Windfeld eine um 1 erhoehte Aufloesung in mu-Richtung hat.
+ 
+	SUBROUTINE CONTGL(PS,DPSDL,DPSDM,DIV,U,V,BREITE,ETA,
+     *MLAT,A,B,NI,NJ,NK)
+				
+	IMPLICIT NONE
+	
+	INTEGER NI,NJ,NK,I,J,K,MLAT(NJ),L
+	
+	REAL A(NK+1),B(NK+1)
+	REAL PS(NI),DPSDL(NI),DPSDM(NI)
+	REAL DIV(NI,NK),U(NI,NK),V(NI,NK),ETA(NI,NK)
+	REAL BREITE(NJ)
+	
+	REAL DIVT1,DIVT2,POB,PUN,DPSDT,COSB
+	
+        L=0
+	DO 4 J=1,NJ
+		COSB=(1.0-BREITE(J)*BREITE(J))
+		DO 3 I=1,MLAT(J)
+                        L=L+1
+			DIVT1=0.0
+			DIVT2=0.0
+			DO 1 K=1,NK
+				POB=A(K)+B(K)*PS(L)
+				PUN=A(K+1)+B(K+1)*PS(L)
+				
+				DIVT1=DIVT1+DIV(L,K)*(PUN-POB)
+				if(cosb .gt. 0.) then
+                                  DIVT2=DIVT2+(B(K+1)-B(K))*PS(L)*
+     *(U(L,K)*DPSDL(L)+V(L,K)*DPSDM(L))/COSB
+                                endif
+     
+     		ETA(L,K)=-DIVT1-DIVT2
+1			CONTINUE
+
+			DPSDT=(-DIVT1-DIVT2)/PS(L)
+			
+			DO 2 K=1,NK
+				ETA(L,K)=ETA(L,K)-DPSDT*B(K+1)*PS(L)
+2			CONTINUE
+			PS(L)=DPSDT*PS(L)
+3		CONTINUE
+4	CONTINUE
+	RETURN
+	END SUBROUTINE CONTGL
+	
+C	OMEGA berechnet omega im Hybridkoordinatensystem
+C	PS ist der Bodendruck,
+C	DPSDL,DPSDM sind die Komponenten des Gradienten des Logarithmus des
+C	Bodendrucks
+C	DIV,U,V sind die horizontale Divergenz und das horizontale Windfeld
+C	BREITE ist das Feld der Gauss'schen Breiten
+C	E ist omega,
+
+	SUBROUTINE OMEGA(PS,DPSDL,DPSDM,DIV,U,V,BREITE,E,MLAT,A,B,NGI
+     *	 ,NGJ,MKK) 
+				
+	IMPLICIT NONE
+		
+	INTEGER I,J,K,L,NGI,NGJ,MKK,MLAT(NGJ)
+	
+	REAL PS(NGI),DPSDL(NGI),DPSDM(NGI),A(MKK+1),B(MKK+1)
+	REAL DIV(NGI,MKK),U(NGI,MKK),V(NGI,MKK),E(NGI,MKK)
+	REAL BREITE(NGJ)
+	
+	REAL DIVT1,DIVT2,POB,PUN,DP,X,Y,COSB
+	REAL DIVT3(MKK+2)
+	
+	L=0
+	DO 4 J=1,NGJ
+		COSB=(1.0-BREITE(J)*BREITE(J))
+		DO 3 I=1,MLAT(J)
+			L=L+1
+			DIVT1=0.0
+			DIVT2=0.0
+			DIVT3(1)=0.0
+			DO 1 K=1,MKK
+				POB=A(K)+B(K)*PS(L)
+				PUN=A(K+1)+B(K+1)*PS(L)
+				DP=PUN-POB
+				
+		Y=PS(L)*(U(L,K)*DPSDL(L)+V(L,K)*DPSDM(L))/COSB
+     		IF(K.LT.3) THEN
+     		X=0.0
+     		ELSE
+				X=(B(K+1)-B(K))*Y
+				ENDIF
+
+				DIVT1=DIVT1+DIV(L,K)*DP			
+				DIVT2=DIVT2+X
+     
+     		DIVT3(K+1)=-DIVT1-DIVT2
+     		
+     		IF(K.GT.1) THEN
+				E(L,K) = 0.5*(POB+PUN)/DP*Y*
+     *((B(K+1)-B(K))+(A(K+1)*B(K)-A(K)*B(K+1))/
+     *DP*LOG(PUN/POB))
+				ELSE
+					E(L,K) = 0.0
+				ENDIF
+								
+			E(L,K) = E(L,K)+0.5*(DIVT3(K)+DIVT3(K+1))
+
+1			CONTINUE
+3		CONTINUE
+4	CONTINUE
+	RETURN
+	END SUBROUTINE OMEGA
+	
+	END MODULE FTRAFO
Index: Source/Fortran/grphreal.f
===================================================================
--- Source/Fortran/grphreal.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/grphreal.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,188 @@
+      MODULE GRTOPH
+
+      USE PHTOGR
+
+      CONTAINS
+C
+      SUBROUTINE GRPH213(CXMN,FELD,WSAVE,IFAX,Z,W,MLAT,
+     *MNAUF,MAXL,MAXB,MLEVEL)
+
+C     DIE ROUTINE F]HRT EINE TRANSFORMATION EINER
+C     FELDVARIABLEN VOM PHASENRAUM IN  DEN PHYSIKALISCHEN
+C     RAUM AUF KUGELKOORDINATEN DURCH
+C
+C     CXMN   = SPEKTRALKOEFFIZIENTEN IN DER REIHENFOLGE
+C              CX00,CX01,CX11,CX02,....CXMNAUFMNAUF
+C			CXM		 = FOURIERKOEFFIZIENTEN - nur ein Hilfsfeld
+C     FELD   = FELD DER METEOROLOGISCHEN VARIABLEN
+C			WSAVE  = Working Array fuer Fouriertransformation
+C     Z 	 = LEGENDREFUNKTIONSWERTE
+C
+C     MNAUF    ANZAHL DER FOURIERKOEFFIZIENTEN
+C     MAXL     ANZAHL DER FUER DAS GITTER BENUTZTEN LAENGEN
+C     MAXB     ANZAHL DER FUER DAS GITTER BENOETIGTEN BREITEN
+C     MLEVEL   ANZAHL DER LEVELS, DIE TRANSFORMIERT WERDEN
+C
+      IMPLICIT REAL (A-H,O-Z)
+
+
+C			Anzahl der Gitterpunkte pro Breitenkreis des reduzierten 
+C			Gauss'schen Gitters
+	INTEGER MLAT(MAXB),ISIZE,IFAX(10,MAXB)
+			
+C    FELD DER LEGENDREPOLYNOME FUER EINE BREITE
+      REAL*8 Z(MAXB/2,0:((MNAUF+3)*(MNAUF+4))/2)
+      
+C      LOGICAL*1 USED(((216*217)/2+1)*160)
+
+      DIMENSION CXMN(0:(MNAUF+1)*(MNAUF+2)-1,MLEVEL)
+      REAL FELD(MAXL,MLEVEL)
+      DIMENSION WSAVE(8*MAXB+15,MAXB/2)
+      REAL*8 W(MAXB) 
+      DIMENSION IND(MAXB)
+			
+			
+      	IND(1)=0
+      DO 6 J=2,MAXB/2
+	 IND(j)=IND(J-1)+MLAT(J-1)
+ 6    CONTINUE
+!$OMP PARALLEL DO SCHEDULE(DYNAMIC)
+      DO 16 L=1,MLEVEL
+        CALL GRPHSUB(L,IND,CXMN,FELD,WSAVE,IFAX,Z,W,MLAT,
+     *MNAUF,MAXL,MAXB,MLEVEL)
+16    CONTINUE
+!$omp end parallel do
+
+
+      RETURN
+      END SUBROUTINE GRPH213
+C
+      SUBROUTINE GRPHSUB(L,IND,CXMN,FELD,WSAVE,IFAX,Z,W,MLAT,
+     *MNAUF,MAXL,MAXB,MLEVEL)
+
+C     DIE ROUTINE F]HRT EINE TRANSFORMATION EINER
+C     FELDVARIABLEN VOM PHASENRAUM IN  DEN PHYSIKALISCHEN
+C     RAUM AUF KUGELKOORDINATEN DURCH
+C
+C     CXMN   = SPEKTRALKOEFFIZIENTEN IN DER REIHENFOLGE
+C              CX00,CX01,CX11,CX02,....CXMNAUFMNAUF
+C			CXM		 = FOURIERKOEFFIZIENTEN - nur ein Hilfsfeld
+C     FELD   = FELD DER METEOROLOGISCHEN VARIABLEN
+C			WSAVE  = Working Array fuer Fouriertransformation
+C     Z 	 = LEGENDREFUNKTIONSWERTE
+C
+C     MNAUF    ANZAHL DER FOURIERKOEFFIZIENTEN
+C     MAXL     ANZAHL DER FUER DAS GITTER BENUTZTEN LAENGEN
+C     MAXB     ANZAHL DER FUER DAS GITTER BENOETIGTEN BREITEN
+C     MLEVEL   ANZAHL DER LEVELS, DIE TRANSFORMIERT WERDEN
+C
+      IMPLICIT REAL (A-H,O-Z)
+
+C    FELD DER FOURIERKOEFFIZIENTEN
+      REAL CXMS(4*(MNAUF+1))
+      REAL CXMA(4*(MNAUF+1))
+      REAL,ALLOCATABLE :: CXM(:,:)
+
+C			Anzahl der Gitterpunkte pro Breitenkreis des reduzierten 
+C			Gauss'schen Gitters
+			INTEGER MLAT(MAXB),ISIZE
+			
+C    FELD DER LEGENDREPOLYNOME FUER EINE BREITE
+      REAL Z(MAXB/2,0:((MNAUF+3)*(MNAUF+4))/2)
+      
+C      LOGICAL*1 USED(((216*217)/2+1)*160)
+
+      REAL CXMN(0:(MNAUF+1)*(MNAUF+2)-1,MLEVEL)
+      REAL FELD(MAXL,MLEVEL)
+      REAL WSAVE(8*MAXB+15,MAXB/2)
+      INTEGER IFAX(10,MAXB)
+      REAL W(MAXB)
+      INTEGER IND(MAXB)
+			
+      ALLOCATE(CXM( 4*MAXB,MAXB))
+	  DO 5 J=1,MAXB/2
+            CXMS(1:MLAT(J))=FELD(IND(J)+1:IND(J)+MLAT(J),L)
+	    CALL RFOUFTR(CXMS,WSAVE(1,J),IFAX(:,J),MNAUF,MLAT(J),1)
+            CXMA(1:MLAT(J))=FELD(MAXL-IND(J)-MLAT(J)+1:MAXL-IND(J),L)
+	    CALL RFOUFTR(CXMA,
+     *WSAVE(1,J),IFAX(:,J),MNAUF,MLAT(J),1)
+	   	DO 4 I=1,2*(MNAUF+1)
+	    	CXM(I,J)=CXMS(I)+CXMA(I)
+	    	CXM(I,MAXB+1-J)=CXMS(I)-CXMA(I)
+4			CONTINUE
+    5 	CONTINUE
+        CALL LGTR213(CXMN(0,L),CXM,Z,W,MLAT,MNAUF,MAXB)
+
+      DEALLOCATE(CXM)
+
+      RETURN
+      END SUBROUTINE GRPHSUB
+C
+        SUBROUTINE LGTR213(CXMN,CXM,Z,W,MLAT,MNAUF,MAXB)
+        IMPLICIT REAL (A-H,O-Z)
+        INTEGER MLAT(MAXB)
+        DIMENSION CXM(0:4*MAXB-1,MAXB)
+        DIMENSION CXMN(0:2*(((MNAUF+1)*MNAUF)/2+MNAUF)+1)
+        REAL*8 Z(MAXB/2,0:((MNAUF+3)*(MNAUF+4))/2)
+        REAL*8 W(MAXB),CR,CI,HILF
+        LOGICAL EVEN
+C
+C     DIESE ROUTINE BERECHNET DIE KFFKs CXMN
+C
+			LL=0
+			LLP=0
+      DO 1 I=0,MNAUF
+        KM=0
+    9   KM=KM+1
+        IF(MLAT(KM).LE.2*I) THEN
+           GOTO 9
+        ENDIF
+        DO 2 J=I,MNAUF
+          CR=0
+          CI=0
+          EVEN=MOD(I+J,2).EQ.0
+          IF(EVEN) THEN
+            DO 3 K=KM,MAXB/2
+        			  HILF=W(K)*Z(K,LLP)
+        	      CR=CR+CXM(2*I,K)*HILF
+        	      CI=CI+CXM(2*I+1,K)*HILF
+    3     	CONTINUE
+          ELSE
+            DO 4 K=KM,MAXB/2
+        			  HILF=W(K)*Z(K,LLP)
+        	      CR=CR+CXM(2*I,MAXB+1-K)*HILF
+        	      CI=CI+CXM(2*I+1,MAXB+1-K)*HILF
+    4     	CONTINUE
+          ENDIF
+    5     CXMN(2*LL)=CR
+          CXMN(2*LL+1)=CI
+          LL=LL+1
+          LLP=LLP+1
+    2   CONTINUE
+        LLP=LLP+2
+    1 CONTINUE
+        RETURN
+        END SUBROUTINE LGTR213
+C
+
+C
+      SUBROUTINE RFOUFTR(CXM,TRIGS,IFAX,MNAUF,MAXL,ISIGN)
+C     BERECHNET DIE FOURIERSUMME MIT EINEM FFT-ALGORITHMUS
+      IMPLICIT REAL (A-H,O-Z)
+      DIMENSION CXM(0:2*MAXL-1)
+      DIMENSION FELD(MAXL),TRIGS(2*MAXL)
+      DIMENSION WSAVE(MAXAUF)
+      INTEGER IFAX(10)
+
+
+C NORMIERUNG...
+      WSAVE(1)=CXM(MAXL-1)
+
+      CXM(1:MAXL)=CXM(0:MAXL-1)/2
+      CXM(0)=WSAVE(1)/2
+!      CALL CFFTF(MAXL,CXM,WSAVE)
+      CALL FFT99(CXM,WSAVE,TRIGS,IFAX,1,1,MAXL,1,-1)
+      RETURN
+      END SUBROUTINE RFOUFTR
+
+      END MODULE GRTOPH
Index: Source/Fortran/jparams.h
===================================================================
--- Source/Fortran/jparams.h	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/jparams.h	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,34 @@
+C
+C     Parameters
+C
+      INTEGER JP32, JPLONO, J2NFFT, JPFFT, JPLOOK, JPMAX, JPMAXITER
+      INTEGER JPMXTRY, JPTRNC, JPK, JPTRP1
+      PARAMETER ( JP32 = 32 )
+C
+C     The following value for JPLONO (2560) will handle regular grids 
+C     from N1 to N720 derived from spectral truncations from T1 to
+C     T639.
+C
+Cjdc  PARAMETER ( JPLONO = 2560 , J2NFFT = 2 + JPLONO, JPFFT = 12000)
+      PARAMETER ( JPLONO = 6000 , J2NFFT = 2 + JPLONO, JPFFT = 12000)
+      PARAMETER ( JPLOOK = 50)
+      PARAMETER ( JPMAX = 2048 )
+      PARAMETER ( JPMAXITER = 10)
+      PARAMETER ( JPMXTRY = 3 )
+      PARAMETER ( JPTRNC = 2047, JPK = (JPTRNC + 1)*(JPTRNC + 4) )
+      PARAMETER ( JPTRP1 = (JPTRNC + 1) )
+C
+      REAL PPEPSA, PPQUART, PPHALF, PPTWO, PP90
+      PARAMETER ( PPEPSA = 1.0E-6)
+      PARAMETER ( PPQUART = 0.25E0)
+      PARAMETER ( PPHALF = 0.5E0)
+      PARAMETER ( PPTWO = 2.0E0)
+      PARAMETER ( PP90 = 90.0E0)
+C
+      REAL PPI
+      PARAMETER ( PPI = 3.14159265358979 )
+C
+C     Debug parameters
+C
+      INTEGER NDBG, NDBGLP
+      COMMON /JDCNDBG/ NDBG, NDBGLP
Index: Source/Fortran/phgrreal.f
===================================================================
--- Source/Fortran/phgrreal.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/phgrreal.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,553 @@
+      MODULE PHTOGR
+
+      INTEGER, PARAMETER :: MAXAUF=36000
+
+      CONTAINS
+
+      SUBROUTINE PHGR213(CXMN,FELD,WSAVE,IFAX,Z,MLAT,MNAUF,
+     *MAXL,MAXB,MLEVEL)
+
+C     DIE ROUTINE F]HRT EINE TRANSFORMATION EINER
+C     FELDVARIABLEN VOM PHASENRAUM IN  DEN PHYSIKALISCHEN
+C     RAUM AUF DAS REDUZIERTE GAUSS'SCHE GITTER DURCH
+C
+C     CXMN   = SPEKTRALKOEFFIZIENTEN IN DER REIHENFOLGE
+C              CX00,CX01,CX11,CX02,....CXMNAUFMNAUF
+C     FELD   = FELD DER METEOROLOGISCHEN VARIABLEN
+C	WSAVE  = Working Array fuer Fouriertransformation
+C     Z 	 = LEGENDREFUNKTIONSWERTE
+C
+C     MNAUF    ANZAHL DER FOURIERKOEFFIZIENTEN
+C     MAXL     ANZAHL DER FUER DAS GITTER BENUTZTEN LAENGEN
+C     MAXB     ANZAHL DER FUER DAS GITTER BENOETIGTEN BREITEN
+C     MLEVEL   ANZAHL DER LEVELS, DIE TRANSFORMIERT WERDEN
+C
+      IMPLICIT NONE
+
+C			Anzahl der Gitterpunkte auf jedem Breitenkreis
+      INTEGER MLAT(MAXB/2)
+      INTEGER K,MAXL,MAXB,MLEVEL,MNAUF
+      INTEGER IND(MAXB)
+      
+			
+C    FELD DER LEGENDREPOLYNOME FUER EINE BREITE
+      REAL Z(0:((MNAUF+3)*(MNAUF+4))/2,MAXB/2)
+
+      REAL CXMN(0:(MNAUF+1)*(MNAUF+2)-1,MLEVEL)
+      REAL FELD(MAXL,MLEVEL)
+      REAL WSAVE(8*MAXB+15,MAXB/2)
+      INTEGER :: IFAX(10,MAXB)
+      
+      IND(1)=0
+     	DO 7 K=2,MAXB/2
+       	IND(K)=IND(K-1)+MLAT(K-1)
+7 		CONTINUE
+
+!$OMP PARALLEL DO SCHEDULE(DYNAMIC)
+     	DO 17 K=1,MAXB/2
+        CALL PHSYM(K,IND,CXMN,FELD,Z,WSAVE,IFAX,MLAT,
+     *MNAUF,MAXL,MAXB,MLEVEL)
+
+17 		CONTINUE
+!$OMP END PARALLEL DO
+
+        RETURN
+        END SUBROUTINE PHGR213
+C
+C
+      SUBROUTINE PHSYM(K,IND,CXMN,FELD,Z,WSAVE,IFAX,MLAT,
+     *MNAUF,MAXL,MAXB,MLEVEL)
+
+      IMPLICIT NONE
+
+      INTEGER MLAT(MAXB/2)
+      INTEGER K,L,I,J,LLS,LLPS,LL,LLP,MAXL,MAXB,MLEVEL,MNAUF
+      INTEGER IND(MAXB)
+      INTEGER :: IFAX(10,MAXB)
+      
+			
+C    FELD DER FOURIERKOEFFIZIENTEN
+      REAL :: CXMS(0:MAXAUF-1),CXMA(0:MAXAUF-1)
+
+C    FELD DER LEGENDREPOLYNOME FUER EINE BREITE
+      REAL Z(0:((MNAUF+3)*(MNAUF+4))/2,MAXB/2)
+      REAL ACR,ACI,SCR,SCI
+
+      REAL CXMN(0:(MNAUF+1)*(MNAUF+2)-1,MLEVEL)
+      REAL FELD(MAXL,MLEVEL)
+      REAL WSAVE(8*MAXB+15,MAXB/2)
+
+      	DO 6 L=1,MLEVEL
+      	 LL=0
+      	 LLP=0
+         DO 1 I=0,MNAUF
+          SCR=0.D0
+          SCI=0.D0
+          ACR=0.D0
+          ACI=0.D0
+          LLS=LL
+          LLPS=LLP
+          IF(2*I+1.LT.MLAT(K)) THEN
+C	Innerste Schleife aufgespalten um if-Abfrage zu sparen
+            DO 18 J=I,MNAUF,2
+              SCR=SCR+Z(LLP,K)*CXMN(2*LL,L)
+              SCI=SCI+Z(LLP,K)*CXMN(2*LL+1,L)
+              LL=LL+2            
+              LLP=LLP+2            
+18          CONTINUE
+            LL=LLS+1
+            LLP=LLPS+1
+            DO 19 J=I+1,MNAUF,2
+              ACR=ACR+Z(LLP,K)*CXMN(2*LL,L)
+              ACI=ACI+Z(LLP,K)*CXMN(2*LL+1,L)
+              LL=LL+2            
+              LLP=LLP+2            
+19          CONTINUE
+          ENDIF
+          LL=LLS+(MNAUF-I+1)
+          LLP=LLPS+(MNAUF-I+3)
+          CXMS(2*I)=SCR+ACR
+          CXMS(2*I+1)=SCI+ACI
+          CXMA(2*I)=SCR-ACR
+          CXMA(2*I+1)=SCI-ACI
+    1    CONTINUE
+C         CALL FOURTR(CXMS,FELD(IND(k)+1,L),WSAVE(:,K),MNAUF,
+C     *MLAT(K),1)
+C         CALL FOURTR(CXMA,FELD(MAXL-IND(k)-MLAT(K)+1,L),
+C     *WSAVE(:,K),MNAUF,MLAT(K),1)
+         CALL RFOURTR(CXMS,WSAVE(:,K),IFAX(:,K),MNAUF,
+     *MLAT(K),1)
+          FELD(IND(k)+1:IND(K)+MLAT(K),L)=CXMS(0:MLAT(K)-1)
+         CALL RFOURTR(CXMA,
+     *WSAVE(:,K),IFAX(:,K),MNAUF,MLAT(K),1)
+         FELD(MAXL-IND(k)-MLAT(K)+1:MAXL-IND(k),L)=CXMA(0:MLAT(K)-1)
+C         WRITE(*,*) IND+1,FELD(IND+1,L)
+6 			CONTINUE
+
+      END SUBROUTINE PHSYM
+
+      SUBROUTINE PHGCUT(CXMN,FELD,WSAVE,IFAX,Z,
+     *                  MNAUF,MMAX,MAUF,MANF,MAXL,MAXB,MLEVEL)
+
+C     DIE ROUTINE FUEHRT EINE TRANSFORMATION EINER
+C     FELDVARIABLEN VOM PHASENRAUM IN  DEN PHYSIKALISCHEN
+C     RAUM AUF KUGELKOORDINATEN DURCH. Es kann ein Teilausschnitt
+C			Der Erde angegeben werden. Diese Routine ist langsamer als
+C			phgrph
+C
+C     CXMN   = SPEKTRALKOEFFIZIENTEN IN DER REIHENFOLGE
+C              CX00,CX01,CX11,CX02,....CXMNAUFMNAUF
+C     FELD   = FELD DER METEOROLOGISCHEN VARIABLEN
+C     BREITE = SINUS DER GEOGRAFISCHEN BREITEN
+C
+C     MNAUF    ANZAHL DER FOURIERKOEFFIZIENTEN
+C     MAUF     ANZAHL DER LAENGEN UND DER FOURIERKOEFFIZIENTEN
+C     MANF     ANFANG DES LAENGENBEREICHS FUER DAS GITTER,
+C              AUF DAS INTERPOLIERT WERDEN SOLL
+C     MAXL     ANZAHL DER FUER DAS GITTER BENUTZTEN LAENGEN
+C     MAXB     ANZAHL DER FUER DAS GITTER BENOETIGTEN BREITEN
+C     MLEVEL   ANZAHL DER LEVELS, DIE TRANSFORMIERT WERDEN
+C
+        IMPLICIT REAL (A-H,O-Z)
+
+C    FELD DER FOURIERKOEFFIZIENTEN
+
+C    FELD DER LEGENDREPOLYNOME FUER EINE BREITE
+        REAL Z(0:((MMAX+3)*(MMAX+4))/2,MAXB)
+
+        DIMENSION CXMN(0:(MMAX+1)*(MMAX+2)-1,MLEVEL)
+        REAL FELD(MAXL,MAXB,MLEVEL)
+      DIMENSION WSAVE(4*MAUF+15)
+      INTEGER:: IFAX(10)
+      
+      LOGICAL SYM
+
+C
+C      write(*,*)mauf,mnauf,manf,maxl
+
+     
+        IF(MAUF.LE.MNAUF) WRITE(*,*) 'TOO COARSE LONGITUDE RESOLUTION'
+          IF((MANF.LT.1).OR.(MAXL.LT.1).OR.
+     *       (MANF.GT.MAUF).OR.(MAXL.GT.MAUF)) THEN
+            WRITE(*,*) 'WRONG LONGITUDE RANGE',MANF,MAXL
+            STOP
+          ENDIF
+
+C Pruefe, ob Ausgabegitter symmetrisch zum Aequator ist
+C Wenn ja soll Symmetrie der Legendrepolynome ausgenutzt werden
+      IF(MAXB .GT. 4) THEN
+          SYM=.TRUE.
+	    DO 11 J=5,5
+	      IF(ABS(ABS(Z(100,J))-ABS(Z(100,MAXB+1-J))).GT.1E-11)
+     *    SYM=.FALSE.
+C	      WRITE(*,*) ABS(Z(100,J)),ABS(Z(100,MAXB+1-J))
+11	  CONTINUE
+      WRITE(*,*) 'Symmetrisch: ',SYM
+      ELSE
+        SYM=.FALSE.
+      ENDIF
+
+	
+		IF(SYM) THEN
+!$OMP PARALLEL DO 
+      	DO J=1,(MAXB+1)/2
+          CALL PHSYMCUT(J,CXMN,FELD,Z,WSAVE,IFAX,
+     *MAUF,MNAUF,MAXL,MAXB,MLEVEL,MANF)
+
+        ENDDO
+!$OMP END PARALLEL DO
+		ELSE
+!$OMP PARALLEL DO 
+        DO J=1,MAXB
+            CALL PHGPNS(CXMN,FELD,Z,WSAVE,IFAX,
+     *J,MNAUF,MAUF,MANF,MAXL,MAXB,MLEVEL)
+        ENDDO
+!$OMP END PARALLEL DO
+
+      ENDIF
+
+
+        RETURN
+        END SUBROUTINE PHGCUT
+
+        SUBROUTINE PHSYMCUT(J,CXMN,FELD,Z,WSAVE,IFAX,
+     *MAUF,MNAUF,MAXL,MAXB,MLEVEL,MANF)
+
+        IMPLICIT REAL (A-H,O-Z)
+
+C    FELD DER FOURIERKOEFFIZIENTEN
+
+        REAL :: CXM(0:MAXAUF-1),CXMA(0:MAXAUF-1)
+
+
+C    FELD DER LEGENDREPOLYNOME FUER EINE BREITE
+        REAL Z(0:((MNAUF+3)*(MNAUF+4))/2,MAXB)
+        REAL SCR,SCI,ACR,ACI
+
+        DIMENSION CXMN(0:(MNAUF+1)*(MNAUF+2)-1,MLEVEL)
+        REAL FELD(MAXL,MAXB,MLEVEL)
+      DIMENSION WSAVE(4*MAUF+15)
+        INTEGER :: IFAX(10)
+
+        DO 16 L=1,MLEVEL
+      	 LL=0
+      	 LLP=0
+         DO 17 I=0,MNAUF
+          SCR=0.D0
+          SCI=0.D0
+          ACR=0.D0
+          ACI=0.D0
+          LLS=LL
+          LLPS=LLP
+C	Innerste Schleife aufgespalten um if-Abfrage zu sparen
+          DO 18 K=I,MNAUF,2
+            SCR=SCR+Z(LLP,J)*CXMN(2*LL,L)
+            SCI=SCI+Z(LLP,J)*CXMN(2*LL+1,L)
+            LL=LL+2            
+            LLP=LLP+2            
+18        CONTINUE
+          LL=LLS+1
+          LLP=LLPS+1
+          DO 19 K=I+1,MNAUF,2
+            ACR=ACR+Z(LLP,J)*CXMN(2*LL,L)
+            ACI=ACI +Z(LLP,J)*CXMN(2*LL+1,L)
+            LL=LL+2            
+            LLP=LLP+2            
+19        CONTINUE
+          LL=LLS+MNAUF-I+1
+          LLP=LLPS+MNAUF-I+3
+          CXM(2*I)=SCR+ACR
+          CXM(2*I+1)=SCI+ACI
+          CXMA(2*I)=SCR-ACR
+          CXMA(2*I+1)=SCI-ACI
+17       CONTINUE
+         
+         CALL RFOURTR(CXM,WSAVE,IFAX,MNAUF,MAUF,1)
+         DO 26 I=0,MAXL-1
+           IF(MANF+I.LE.MAUF) THEN
+             FELD(I+1,J,L)=CXM(MANF+I-1)
+           ELSE
+             FELD(I+1,J,L)=CXM(MANF-MAUF+I-1)
+           ENDIF
+26       CONTINUE
+         CALL RFOURTR(CXMA,WSAVE,IFAX,MNAUF,MAUF,1)
+         DO 36 I=0,MAXL-1
+           IF(MANF+I.LE.MAUF) THEN
+             FELD(I+1,MAXB+1-J,L)=CXMA(MANF+I-1)
+           ELSE
+             FELD(I+1,MAXB+1-J,L)=CXMA(MANF-MAUF+I-1)
+           ENDIF
+36       CONTINUE
+16 			CONTINUE
+
+      END SUBROUTINE PHSYMCUT
+
+      SUBROUTINE PHGPNS(CXMN,FELD,Z,WSAVE,IFAX,
+     *J,MNAUF,MAUF,MANF,MAXL,MAXB,MLEVEL)
+
+        IMPLICIT NONE
+        INTEGER,intent(in) :: MNAUF,MAUF,MANF,J,MAXL,MAXB,MLEVEL
+        REAL :: CXM(0:MAXAUF-1)
+        REAL,intent(in) :: Z(0:((MNAUF+3)*(MNAUF+4))/2,MAXB)
+
+        REAL,intent(in) :: CXMN(0:(MNAUF+1)*(MNAUF+2)-1,MLEVEL)
+
+        REAL,intent(in) :: WSAVE(4*MAUF+15)
+
+        REAL :: FELD(MAXL,MAXB,MLEVEL)
+        INTEGER :: IFAX(10)
+
+        INTEGER I,L
+
+          DO L=1,MLEVEL
+            CALL LEGTR(CXMN(:,L),CXM,Z(:,J),MNAUF,MAUF)
+            CALL RFOURTR(CXM,WSAVE,IFAX,MNAUF,MAUF,1)
+           
+            DO I=0,MAXL-1
+                  IF(MANF+I.LE.MAUF) THEN
+                    FELD(I+1,J,L)=CXM(MANF+I-1)
+                  ELSE
+                    FELD(I+1,J,L)=CXM(MANF-MAUF+I-1)
+                  ENDIF
+            ENDDO
+          ENDDO
+      END SUBROUTINE PHGPNS
+C
+        SUBROUTINE LEGTR(CXMN,CXM,Z,MNAUF,MAUF)
+        IMPLICIT NONE
+        INTEGER MNAUF,MAUF,LL,LLP,I,J
+        REAL CXM(0:MAXAUF-1)
+        REAL CXMN(0:(MNAUF+1)*(MNAUF+2)-1)
+        REAL Z(0:((MNAUF+3)*(MNAUF+4))/2)
+        REAL CI,CR
+C
+C     DIESE ROUTINE BERECHNET DIE FOURIERKOEFFIZIENTEN CXM
+C
+        LL=0
+        LLP=0
+        DO 1 I=0,MNAUF
+            CR=0.D0
+            CI=0.D0
+            DO 2 J=I,MNAUF
+              CR=CR+Z(LLP)*CXMN(2*LL)
+              CI=CI+Z(LLP)*CXMN(2*LL+1)
+              LL=LL+1
+              LLP=LLP+1
+    2     CONTINUE
+          LLP=LLP+2
+          CXM(2*I)=CR
+          CXM(2*I+1)=CI
+    1 CONTINUE
+        RETURN
+        END SUBROUTINE LEGTR
+C
+C     
+C
+      SUBROUTINE RFOURTR(CXM,TRIGS,IFAX,MNAUF,MAXL,ISIGN)
+C     BERECHNET DIE FOURIERSUMME MIT EINEM FFT-ALGORITHMUS
+      IMPLICIT REAL (A-H,O-Z)
+      DIMENSION CXM(0:MAXAUF-1)
+      REAL :: WSAVE(2*MAXL),TRIGS(2*MAXL)
+      INTEGER IFAX(10)
+
+      DO I=MNAUF+1,MAXL-1
+          CXM(2*I)=0.0
+          CXM(2*I+1)=0.0
+      ENDDO
+      CALL FFT99(CXM,WSAVE,TRIGS,IFAX,1,1,MAXL,1,1)
+      DO I=0,MAXL-1
+        CXM(I)=CXM(I+1)
+      ENDDO
+
+      RETURN
+      END SUBROUTINE RFOURTR
+C     
+C     
+      SUBROUTINE GAULEG(X1,X2,X,W,N)
+C     BERECHNET DIE GAUSS+SCHEN BREITEN
+        IMPLICIT REAL (A-H,O-Z)
+        DIMENSION X(N),W(N)
+      PARAMETER (EPS=3.D-14)
+      M=(N+1)/2
+      XM=0.5D0*(X2+X1)
+      XL=0.5D0*(X2-X1)
+      DO 12 I=1,M
+        Z=DCOS(3.141592654D0*(I-.25D0)/(N+.5D0))
+1       CONTINUE
+          P1=1.D0
+          P2=0.D0
+          DO 11 J=1,N
+            P3=P2
+            P2=P1
+            P1=((2.D0*J-1.D0)*Z*P2-(J-1.D0)*P3)/J
+11        CONTINUE
+          PP=N*(Z*P1-P2)/(Z*Z-1.D0)
+          Z1=Z
+          Z=Z1-P1/PP
+        IF(ABS(Z-Z1).GT.EPS)GO TO 1
+        X(I)=XM-XL*Z
+        X(N+1-I)=XM+XL*Z
+        W(I)=2.D0*XL/((1.D0-Z*Z)*PP*PP)
+        W(N+1-I)=W(I)
+12    CONTINUE
+      RETURN
+      END SUBROUTINE GAULEG
+C
+C
+        SUBROUTINE PLGNFA(LL,X,Z)
+C
+C PLGNDN BERECHNET ALLE NORMIERTEN ASSOZIIERTEN
+C LEGENDREFUNKTIONEN VON P00(X) BIS PLL(X)
+C UND SCHREIBT SIE IN DAS FELD Z
+C Die Polynome sind wie im ECMWF indiziert, d.h.
+C P00,P10,P11,P20,P21,P22,...
+C	Ansonsten ist die Routine analog zu PLGNDN
+C X IST DER COSINUS DES ZENITWINKELS ODER
+C       DER SINUS DER GEOGRAFISCHEN BREITE
+C
+        IMPLICIT REAL (A-H,O-Z)
+        DIMENSION Z(0:((LL+3)*(LL+4))/2)
+C
+      L=LL+2
+      I=1
+      Z(0)=1.D0
+      FACT=1.D0
+      POT=1.D0
+      SOMX2=DSQRT(1.D0-X*X)
+        DO 14 J=0,L
+          DJ=DBLE(J)
+        IF(J.GT.0) THEN
+          FACT=FACT*(2.D0*DJ-1.D0)/(2.D0*DJ)
+            POT=POT*SOMX2
+          Z(I)=DSQRT((2.D0*DJ+1.D0)*FACT)*POT
+          I=I+1
+        ENDIF
+          IF(J.LT.L) THEN
+            Z(I)=X*
+     *DSQRT((4.D0*DJ*DJ+8.D0*DJ+3.D0)/(2.D0*DJ+1.D0))*Z(I-1)
+            I=I+1
+          ENDIF
+          DK=DJ+2.D0
+        DO 14 K=J+2,L
+          DDK=(DK*DK-DJ*DJ)
+          Z(I)=X*DSQRT((4.D0*DK*DK-1.D0)/DDK)*Z(I-1)-
+     *    DSQRT(((2.D0*DK+1.D0)*(DK-DJ-1.D0)*(DK+DJ-1.D0))/
+     *    ((2.D0*DK-3.D0)*DDK))*Z(I-2)
+          DK=DK+1.D0
+          I=I+1
+14    CONTINUE
+      RETURN
+      END SUBROUTINE PLGNFA
+
+
+      SUBROUTINE DPLGND(MNAUF,Z,DZ)
+C
+C DPLGND BERECHNET DIE ABLEITUNG DER NORMIERTEN ASSOZIIERTEN
+C LEGENDREFUNKTIONEN VON P00(X) BIS PLL(X)
+C UND SCHREIBT SIE IN DAS FELD DZ
+C DIE REIHENFOLGE IST
+C P00(X),P01(X),P11(X),P02(X),P12(X),P22(X),..PLL(X)
+C
+        IMPLICIT REAL (A-H,O-Z)
+        DIMENSION Z(0:((MNAUF+3)*(MNAUF+4))/2)
+        DIMENSION DZ(0:((MNAUF+2)*(MNAUF+3))/2)
+C
+				IF(Z(0).NE.1.D0) THEN
+			WRITE(*,*) 'DPLGND: Z(0) must be 1.0'
+					STOP
+				ENDIF
+				
+				LLP=0
+				LLH=0
+        DO 1 I=0,MNAUF+1
+            DO 2 J=I,MNAUF+1
+              IF(I.EQ.J) THEN
+              	WURZELA=
+     *DSQRT(DBLE((J+1)*(J+1)-I*I)/DBLE(4*(J+1)*(J+1)-1))
+                DZ(LLH)=DBLE(J)*WURZELA*Z(LLP+1)
+              ELSE
+              		WURZELB=
+     *DSQRT(DBLE((J+1)*(J+1)-I*I)/DBLE(4*(J+1)*(J+1)-1))
+                DZ(LLH)=
+     *DBLE(J)*WURZELB*Z(LLP+1)-DBLE(J+1)*WURZELA*Z(LLP-1)
+     						WURZELA=WURZELB
+	        ENDIF
+	        LLH=LLH+1              
+          LLP=LLP+1
+2	      CONTINUE
+        LLP=LLP+1
+1	CONTINUE
+	RETURN
+	END SUBROUTINE DPLGND
+	
+
+* Spectral Filter of Sardeshmukh and Hoskins (1984, MWR)
+* MM=Spectral truncation of field
+* MMAX= Spectral truncation of filter
+*
+      SUBROUTINE SPFILTER(FELDMN,MM,MMAX)
+
+      IMPLICIT NONE
+
+      INTEGER MM,MMAX,I,J,K,L
+      REAL FELDMN(0:(MM+1)*(MM+2)-1)
+      REAL KMAX,SMAX,FAK
+
+      SMAX=0.1
+      KMAX=-ALOG(SMAX)
+      KMAX=KMAX/(float(MMAX)*float(MMAX+1))**2
+c      WRITE(*,*)'alogsmax',alog(smax),'KMAX:',KMAX
+        l=0
+        do i=0,MM
+          do j=i,MM
+c          write(*,*) i,j,feld(k),feld(k)*exp(-KMAX*(j*(j+1))**2)
+            if(j .le. MMAX) then
+c               fak=exp(-KMAX*(j*(j+1))**2)
+               fak=1.0
+               feldmn(2*l)=feldmn(2*l)*fak
+               feldmn(2*l+1)=feldmn(2*l+1)*fak
+            else
+               feldmn(2*l)=0.
+               feldmn(2*l+1)=0.
+            endif
+            l=l+1
+          enddo
+        enddo
+      END SUBROUTINE SPFILTER
+
+      END MODULE PHTOGR
+    
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: Source/Fortran/posnam.f
===================================================================
--- Source/Fortran/posnam.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/posnam.f	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,25 @@
+      SUBROUTINE POSNAM(KULNAM,CDNAML)
+!-------------------------------------
+
+!--- position in namelist file. 
+!    author:  Mats Hamrud, ECMWF
+
+      INTEGER, INTENT(IN)       :: KULNAM
+      CHARACTER*(*), INTENT(IN) :: CDNAML
+      CHARACTER*120 CLINE
+      CHARACTER*1 CLTEST
+      REWIND(KULNAM)
+      ILEN=LEN(CDNAML)
+ 102  CONTINUE
+      CLINE=' '
+      READ(KULNAM,'(A)') CLINE
+      IND1=INDEX(CLINE,'&'//CDNAML)
+      IF(IND1.EQ.0) GO TO 102
+      CLTEST=CLINE(IND1+ILEN+1:IND1+ILEN+1)
+      IF((LGE(CLTEST,'0').AND.LLE(CLTEST,'9')).OR.
+     &   (LGE(CLTEST,'A').AND.LLE(CLTEST,'Z'))) GO TO 102
+      BACKSPACE(KULNAM)
+
+      RETURN
+      END SUBROUTINE POSNAM
+
Index: Source/Fortran/preconvert.f90
===================================================================
--- Source/Fortran/preconvert.f90	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/preconvert.f90	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,807 @@
+      PROGRAM PRECONVERT
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                                                                 !
+! PROGRAM PRECONVERT - PREPARES INPUT DATA FOR POP MODEL METEOR-  !
+!                      OLOGICAL PREPROCESSOR                      !
+!                                                                 !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                                                                 !
+! CALCULATION OF ETAPOINT ON A REGULAR LAMDA/PHI GRID AND WRITING !
+! U,V,ETAPOINT,T,PS,Q,SD,MSL,TCC,10U, 10V, 2T,2D,LSP,CP,SSHF,SSR, !
+! EWSS,NSSS TO AN OUTPUT FILE (GRIB 1 or 2 FORMAT).               ! 
+!                                                                 !
+! AUTHORS: L. HAIMBERGER, G. WOTAWA, 1994-04                      !
+!                     adapted: A. BECK                            !
+!                     2003-05-11                                  !
+!          L. Haimberger 2006-12    V2.0                          !
+!                    modified to handle arbitrary regular grids   !
+!                    and T799 resolution data                     !
+!          L. Haimberger 2010-03    V4.0                          !
+!                    modified to grib edition 2 fields            !
+!                    and T1279 resolution data                    !
+!                                                                 !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                                                                 !
+! DESCRIPTION OF NEEDED INPUT:                                    !
+!                                                                 !
+! UNIT  FILE      PARAMETER(S)    DATA REPRESENTATION             !
+!                                                                 !
+! 11    fort.11   T,U,V           regular lamda phi grid          !
+! 12    fort.12   D               regular lamda phi grid          !
+! 13    fort.13   LNSP          fort.13  spherical harmonics             !
+! 14    fort.14   SD,MSL,TCC,10U,                                 !
+!                 10V,2T,2D       regular lamda phi grid          !
+! 16    fort.16   LSP,CP,SSHF,                                    !
+!                 SSR,EWSS,NSSS   regular lamda phi grid          !
+! 17    fort.17   Q               regular lamda phi grid          !
+!                                                                 !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                                                                 !
+! DESCRIPTION OF OUTPUT:                                          !
+!                                                                 !
+! UNIT  FILE      PARAMETER(S)    DATA REPRESENTATION             !
+!                                                                 !
+! 15    fort.15   U,V,ETA,T,PS,                                   !
+!                 Q,SD,MSL,TCC,                                   !
+!                 10U,10V,2T,2D,  regular lamda phi grid          !
+!                 LSP,CP,SSHF,                                    !
+!                 SSR,EWSS,NSSS                                   !
+!                                                                 !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!
+
+      USE PHTOGR
+      USE GRTOPH
+      USE FTRAFO
+      USE RWGRIB2
+      USE GRIB_API
+
+      IMPLICIT NONE
+
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: LNPS
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: Z
+      REAL, ALLOCATABLE, DIMENSION (:,:,:) :: T, UV , UV2
+      REAL, ALLOCATABLE, DIMENSION (:,:,:) :: QA,OM,OMR
+      REAL, ALLOCATABLE, DIMENSION (:,:,:) :: DIV, ETA,ETAR
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: DPSDL, DPSDM
+      REAL, ALLOCATABLE, DIMENSION (:,:,:) :: PS,DPSDT
+      REAL, ALLOCATABLE, DIMENSION (:,:,:) :: SURF,FLUX,OROLSM
+      REAL, ALLOCATABLE, DIMENSION (:) :: WSAVE,H,SINL,COSL,WSAVE2
+      REAL, ALLOCATABLE, DIMENSION (:) :: BREITE, GBREITE,AK, BK,pv
+
+! Arrays for Gaussian grid calculations
+
+      REAL  :: X1,X2,RMS,MW,SIG,LAM
+      REAL,ALLOCATABLE :: CUA(:,:,:),CVA(:,:,:)
+
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: P,PP !,P2
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: XMN,HILFUV
+      REAL, ALLOCATABLE, DIMENSION (:) :: LNPMN,LNPMN2,LNPMN3
+      REAL, ALLOCATABLE, DIMENSION (:) :: WEIGHT
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: UGVG
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: DG, ETAG
+      REAL, ALLOCATABLE, DIMENSION (:,:) :: GWSAVE
+      REAL, ALLOCATABLE, DIMENSION (:) :: PSG,HILF
+
+! end arrays for Gaussian grid calculations
+
+      INTEGER, ALLOCATABLE, DIMENSION (:) :: MLAT,MPSURF,MPFLUX,MPORO,MPAR
+      INTEGER, ALLOCATABLE :: GIFAX(:,:)
+
+      REAL PI,COSB,DAK,DBK,P00
+      REAL URLAR8,JMIN1,LLLAR8,MAXBMIN1,PIR8,DCOSB
+
+      INTEGER I,J,K,L,IERR,M,LTEST,MK,NGI,NGJ
+      INTEGER MFLUX,MSURF,MORO
+      INTEGER LUNIT,LUNIT2
+
+      INTEGER MAXL, MAXB, MLEVEL, LEVOUT,LEVMIN,LEVMAX
+      INTEGER MOMEGA,MOMEGADIFF,MGAUSS,MSMOOTH, MNAUF,META,METADIFF
+      INTEGER MDPDETA,METAPAR
+      REAL RLO0, RLO1, RLA0, RLA1
+      CHARACTER*300 MLEVELIST
+
+      INTEGER MAUF, MANF,IFAX(10)
+
+      INTEGER IGRIB(1),iret,ogrib
+
+      CHARACTER*80 FILENAME
+
+      NAMELIST /NAMGEN/ &
+                 MAXL, MAXB, &
+                 MLEVEL,MLEVELIST,MNAUF,METAPAR, &
+                 RLO0, RLO1, RLA0, RLA1, &
+                 MOMEGA,MOMEGADIFF,MGAUSS,MSMOOTH,META,METADIFF,&
+                 MDPDETA
+
+      LTEST=1
+
+      call posnam (4,'NAMGEN')
+      read (4,NAMGEN)
+
+      MAUF=INT(360.*(REAL(MAXL)-1.)/(RLO1-RLO0)+0.0001)
+!      PRINT*, MAUF
+
+      MANF=INT(REAL(MAUF)/360.*(360.+RLO0)+1.0001)
+      IF(MANF .gt. MAUF) MANF=MANF-MAUF
+
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                       ALLOCATE VARIABLES                        !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+      ALLOCATE (LNPS(0:(MNAUF+1)*(MNAUF+2)-1,1))
+
+      ALLOCATE (H(0:(MNAUF+2)*(MNAUF+3)/2))
+
+     
+      ALLOCATE (OM(MAXL, MAXB, MLEVEL))
+
+      ALLOCATE (ETA(MAXL,MAXB,MLEVEL))
+
+      ALLOCATE (PS(MAXL, MAXB,1),DPSDT(MAXL, MAXB,1))
+
+      
+      ALLOCATE (WSAVE(4*MAUF+15),WSAVE2(4*MAUF+15))
+
+      ALLOCATE (BREITE(MAXB),AK(MLEVEL+1),BK(MLEVEL+1),pv(2*mlevel+2))
+      
+      ALLOCATE (MPAR(2))
+
+      ALLOCATE (COSL(MAXL),SINL(MAXL))
+		
+      ALLOCATE (CUA(2,4,MLEVEL),CVA(2,4,MLEVEL))
+    	
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!     GAUSS STUFF !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+      IF(MGAUSS .EQ. 1) THEN
+      LUNIT=0
+      FILENAME='fort.18'
+
+      call grib_open_file(LUNIT, TRIM(FILENAME),'r')
+ 
+      call grib_new_from_file(LUNIT,igrib(1), iret)
+ 
+! we can close the file
+      call grib_close_file(LUNIT)
+ 
+!      call grib_get(igrib(1),'gridType', j)
+
+      NGJ=MNAUF+1
+
+      ALLOCATE (GWSAVE(8*NGJ+15,NGJ/2))
+      ALLOCATE(GIFAX(10,NGJ))
+      ALLOCATE (GBREITE(NGJ),WEIGHT(NGJ))
+      ALLOCATE (MLAT(NGJ))
+      ALLOCATE (P(0:((MNAUF+3)*(MNAUF+4))/2,NGJ/2))
+      ALLOCATE (PP(NGJ/2,0:((MNAUF+3)*(MNAUF+4))/2))
+      ALLOCATE (Z(0:((MNAUF+3)*(MNAUF+4))/2,MAXB))
+
+      call grib_get(igrib(1),'numberOfPointsAlongAMeridian', NGJ)
+ 
+      !     get as a integer
+      call grib_get(igrib(1),'pl', MLAT)
+
+      NGI=SUM(MLAT)
+
+      call grib_get(igrib(1),'numberOfVerticalCoordinateValues',mk)
+
+      IF(mk/2-1 .ne. MLEVEL) THEN 
+        WRITE(*,*) 'FATAL: Number of model levels',mk, &
+          ' does not agree with', MLEVEL,' in namelist'
+        STOP
+      ENDIF
+      call grib_get(igrib(1),'pv',pv)
+        AK=pv(1:1+MLEVEL)
+        BK=pv(2+MLEVEL:2*MLEVEL+2)
+
+      ALLOCATE (LNPMN(0:(MNAUF+1)*(MNAUF+2)-1))
+      ALLOCATE (LNPMN2(0:(MNAUF+1)*(MNAUF+2)-1))
+      ALLOCATE (UGVG(NGI, 2*MLEVEL),HILFUV(2*MAXL,2))
+
+
+      ALLOCATE (DPSDL(NGI,1),DPSDM(NGI,1))
+
+      ALLOCATE (PSG(NGI),HILF(NGI))
+      ALLOCATE (UV(MAXL, MAXB, 2*MLEVEL))
+!      ALLOCATE (UV2(MAXL, MAXB, 2*MLEVEL))
+
+      ALLOCATE (XMN(0:(MNAUF+1)*(MNAUF+2)-1, 2*MLEVEL))
+      ALLOCATE (DG(NGI,MLEVEL),ETAG(NGI,MLEVEL))
+
+! Initialisieren  Legendretransformation
+!	auf das LaT/LON Gitter
+
+      PI=ACOS(-1.D0)
+!$OMP PARALLEL DO
+      DO 20 J=1,MAXB
+
+      BREITE(J)=SIN((RLA1-(J-1.D0)*(RLA1-RLA0)/(MAXB-1))* PI/180.D0)
+
+      CALL PLGNFA(MNAUF,BREITE(J),Z(0,J))
+
+20    CONTINUE
+!$OMP END PARALLEL DO
+
+! Avoid possible Pole problem
+!      IF(RLA0 .EQ. -90.0) BREITE(MAXB)=sin(-89.99*PI/180.d0)
+!      IF(RLA1 .EQ. 90.0)  BREITE(1)=sin(89.99*PI/180.d0)
+
+! Initialisation of fields for  FFT and Legendre transformation
+!	to  Gaussian grid and back to phase space
+	X1=-1.D0
+	X2=1.D0
+	CALL GAULEG(X1,X2,GBREITE,WEIGHT,NGJ)
+
+!$OMP PARALLEL DO PRIVATE(M)
+	DO J=1,NGJ/2
+               CALL PLGNFA(MNAUF,GBREITE(J),P(:,J))
+		DO M=0,(MNAUF+3)*(MNAUF+4)/2
+		  PP(J,M)=P(M,J)
+	ENDDO
+	ENDDO
+!$OMP END PARALLEL DO
+
+
+!       MPAR(1)=152
+        FILENAME='fort.12'
+        CALL READSPECTRAL(FILENAME,LNPMN,MNAUF,1,MLEVEL,(/152/),AK,BK)
+!      goto 111
+        CALL SET99(WSAVE,IFAX,mauf)
+        CALL PHGCUT(LNPMN,PS,WSAVE,IFAX,Z, &
+      MNAUF,MNAUF,MAUF,MANF,MAXL,MAXB,1)
+      CALL STATIS(MAXL,MAXB,1,EXP(PS),RMS,MW,SIG)
+      WRITE(*,'(A12,3F12.4)') 'STATISTICS: ',RMS,MW,SIG
+
+        DO J=1,NGJ/2
+          CALL SET99(GWSAVE(1,J),GIFAX(1,J),MLAT(J))
+        ENDDO
+	CALL PHGR213(LNPMN,HILF,GWSAVE,GIFAX,P,MLAT,MNAUF,NGI,NGJ,1)
+        PSG=HILF
+	CALL GRPH213(LNPMN2,PSG,GWSAVE,GIFAX,PP,WEIGHT,MLAT, &
+      MNAUF,NGI,NGJ,1)
+	CALL PHGR213(LNPMN2,HILF,GWSAVE,GIFAX,P,MLAT,MNAUF,NGI,NGJ,1)
+
+
+        HILF=exp(PSG)-exp(HILF)
+
+      CALL STATIS(NGI,1,1,HILF,RMS,MW,SIG)
+      WRITE(*,'(A12,3F11.4)') 'STATISTICS: ',RMS,MW,SIG
+
+        PSG=EXP(PSG)
+        HILF=PSG
+      CALL STATIS(NGI,1,1,HILF,RMS,MW,SIG)
+      WRITE(*,'(A12,3F11.4)') 'STATISTICS: ',RMS,MW,SIG
+
+  111         FILENAME='fort.10'
+       CALL READSPECTRAL(FILENAME, &
+      XMN,MNAUF,2*MLEVEL,MLEVEL,(/131,132/),AK,BK) 
+!	Transformieren des Windes auf das Gaussgitter	
+	CALL PHGR213(XMN,UGVG,GWSAVE,GIFAX,P,MLAT,MNAUF,NGI,NGJ,2*MLEVEL)
+       DO K=1,MLEVEL
+! North Pole
+            CALL JSPPOLE(XMN(:,K),1,MNAUF,.TRUE.,CUA(:,:,K))
+            CALL JSPPOLE(XMN(:,MLEVEL+K),1,MNAUF,.TRUE.,CVA(:,:,K))
+! South Pole
+            CALL JSPPOLE(XMN(:,K),-1,MNAUF,.TRUE.,CUA(:,3:4,K))
+            CALL JSPPOLE(XMN(:,MLEVEL+K),-1,MNAUF,.TRUE.,CVA(:,3:4,K))
+       ENDDO
+    
+        DO K=1,2*MLEVEL
+          IF(MSMOOTH .ne. 0) CALL SPFILTER(XMN(:,K),MNAUF,MSMOOTH)
+        ENDDO
+        CALL PHGCUT(XMN,UV,WSAVE,IFAX,Z, &
+      MNAUF,MNAUF,MAUF,MANF,MAXL,MAXB,2*MLEVEL)
+
+
+ 112        FILENAME='fort.13'
+      CALL READSPECTRAL(FILENAME,XMN,MNAUF,MLEVEL,MLEVEL,(/155/),AK,BK)
+!	Transformieren der horizontalen Divergenz auf das Gaussgitter			
+	CALL PHGR213(XMN,DG,GWSAVE,GIFAX,P,MLAT,MNAUF,NGI,NGJ,MLEVEL)
+
+
+!	Berechnung des Gradienten des Logarithmus des Bodendrucks 
+!       auf dem Gaussgitter	
+	CALL PHGRAD(LNPMN,DPSDL,DPSDM,GWSAVE,GIFAX,P,H,MLAT,MNAUF,NGI,NGJ,1)
+
+!	Berechnung der Vertikalgeschwindigkeit auf dem Gaussgitter
+      CALL CONTGL(HILF,DPSDL,DPSDM,DG,UGVG(:,1),UGVG(:,MLEVEL+1), &
+      GBREITE,ETAG,MLAT,AK,BK,NGI,NGJ,MLEVEL)
+
+ 
+ 	CALL GRPH213(XMN,ETAG,GWSAVE,GIFAX,PP,WEIGHT,MLAT, &
+      MNAUF,NGI,NGJ,MLEVEL)
+        DO K=1,MLEVEL
+          IF(MSMOOTH .ne. 0) CALL SPFILTER(XMN(:,K),MNAUF,MSMOOTH)
+        ENDDO
+        CALL PHGCUT(XMN,ETA,WSAVE,IFAX,Z,MNAUF,MNAUF,MAUF,MANF,MAXL,MAXB,MLEVEL)
+
+	CALL GRPH213(XMN,HILF,GWSAVE,GIFAX,PP,WEIGHT,MLAT, MNAUF,NGI,NGJ,1)
+
+        IF(MSMOOTH .ne. 0) CALL SPFILTER(XMN(:,1),MNAUF,MSMOOTH)
+        CALL PHGCUT(XMN,DPSDT,WSAVE,IFAX,Z,MNAUF,MNAUF,MAUF,MANF,MAXL,MAXB,1)
+!       GOTO 114
+
+      CALL STATIS(MAXL,MAXB,1,DPSDT,RMS,MW,SIG)
+      WRITE(*,'(A12,3F11.4)') 'STATISTICS DPSDT: ',RMS,MW,SIG
+
+       IF(MOMEGADIFF .ne. 0) THEN
+!	Berechnung von Omega auf dem Gaussgitter
+	CALL OMEGA(PSG,DPSDL,DPSDM,DG,UGVG(:,1),UGVG(:,MLEVEL+1), &
+      GBREITE,ETAG,MLAT,AK,BK,NGI ,NGJ,MLEVEL) 
+
+	CALL GRPH213(XMN,ETAG,GWSAVE,GIFAX,PP,WEIGHT,MLAT,&
+      MNAUF,NGI,NGJ,MLEVEL)
+        DO K=1,MLEVEL
+          IF(MSMOOTH .ne. 0) CALL SPFILTER(XMN(:,K),MNAUF,MSMOOTH)
+        ENDDO
+        CALL PHGCUT(XMN,OM,WSAVE,IFAX,Z,MNAUF,MNAUF,MAUF,MANF,MAXL,MAXB,MLEVEL)
+
+       ENDIF !MOMEGA
+
+	CALL GRPH213(XMN,PSG,GWSAVE,GIFAX,PP,WEIGHT,MLAT,MNAUF,NGI,NGJ,1)
+        CALL PHGCUT(XMN,PS,WSAVE,IFAX,Z,MNAUF,MNAUF,MAUF,MANF,MAXL,MAXB,1)
+
+      CALL STATIS(MAXL,MAXB,1,PS,RMS,MW,SIG)
+      WRITE(*,'(A12,3F11.4)') 'STATISTICS: ',RMS,MW,SIG
+
+ 114  DEALLOCATE(HILF,PSG,DPSDL,DPSDM,ETAG,DG,LNPMN)
+
+!      ALLOCATE (UV(MAXL, MAXB, 2*MLEVEL))
+!	CALL GRPH213(XMN,UGVG,GWSAVE,GIFAX,PP,WEIGHT,MLAT,
+!     *MNAUF,NGI,NGJ,2*MLEVEL)
+!        DO K=1,2*MLEVEL
+!          IF(MSMOOTH .ne. 0) CALL SPFILTER(XMN(:,K),MNAUF,MSMOOTH)
+!        ENDDO
+!        CALL PHGCUT(XMN,UV,WSAVE,IFAX,Z,
+!     *MNAUF,MNAUF,MAUF,MANF,MAXL,MAXB,2*MLEVEL)
+        DEALLOCATE(PP,P,UGVG,MLAT,GBREITE,WEIGHT,GWSAVE,XMN)
+
+!        CALL ETAGAUSS(Z,WSAVE
+!     *,BREITE,UV,ETA,OM,PS,
+!     *MAUF,MAXB,MAXL,MANF,MNAUF,MLEVEL,MSMOOTH)
+
+      ELSE
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!          READING OF PREPARED METEOROLOGICAL FIELDS              !
+!                                                                 !
+!          THE FOLLOWING FIELDS ARE EXPECTED:                     !
+!                                                                 !
+!          UNIT 11: T,U,V        (REGULAR GRID)                   !
+!          UNIT 17: Q            (REGULAR GRID)                   !
+!          UNIT 13: D            (REGULAR GRID)                   !
+!          UNIT 12: LNSP         (SPHERICAL HARMONICS)            !
+!          UNIT 14: SURFACE DATA (REGULAR GRID)                   !
+!          UNIT 16: FLUX DATA    (REGULAR GRID)                   !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!
+      ALLOCATE (MLAT(MAXB))
+      MLAT=MAXL
+      ALLOCATE (Z(0:((MNAUF+3)*(MNAUF+4))/2,1))
+      ALLOCATE (DPSDL(MAXL,MAXB),DPSDM(MAXL,MAXB))
+      ALLOCATE (UV(MAXL, MAXB, 2*MLEVEL),DIV(MAXL,MAXB,MLEVEL))
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                  READING OF SURFACE PRESSURE                    !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+      FILENAME='fort.12'
+        CALL READSPECTRAL(FILENAME,LNPS,MNAUF,1,MLEVEL,(/152/),AK,BK)
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                      READING OF U,V                      !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!
+! OPENING OF UNBLOCKED GRIB FILE
+!
+      FILENAME='fort.10'
+      CALL READLATLON(FILENAME,UV,MAXL,MAXB,2*MLEVEL,(/131,132/))
+
+
+      PI=ACOS(-1.D0)
+      DO J=1,MAXB
+
+        BREITE(J)=SIN((RLA1-(J-1.D0)*(RLA1-RLA0)/(MAXB-1))*PI/180.D0)
+
+      ENDDO
+! Avoid possible Pole problem
+!      IF(RLA0 .EQ. -90.0) BREITE(MAXB)=sin(-89.99*PI/180.d0)
+!      IF(RLA1 .EQ. 90.0)  BREITE(1)=sin(89.99*PI/180.d0)
+
+      DO K=1,2*MLEVEL
+        DO J=1,MAXB
+          COSB=SQRT(1.0-(BREITE(J))*(BREITE(J)))
+          IF(RLA0 .EQ. -90.0 .AND. J .EQ. MAXB .OR. &
+             RLA1 .EQ. 90.0 .AND. J .EQ. 1) then
+            UV(:,J,K)=UV(:,J,K)/1.D6
+          else
+            UV(:,J,K)=UV(:,J,K)*COSB
+          endif
+      ENDDO
+      ENDDO
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                     READING OF LNSP on grid                !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+! For debugging only
+!      FILENAME='LNSPG_G.20060330.600'
+!      INQUIRE(FILE=FILENAME,EXIST=EX)
+!      CALL READLATLON(FILENAME,QA,
+!     *MAXL,MAXB,1,1,(/152/))
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                     READING OF DIVERGENCE                       !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+      IF(META .EQ. 0 .OR. METADIFF .EQ. 1) THEN
+        FILENAME='fort.13'
+        CALL READLATLON(FILENAME,DIV,MAXL,MAXB,MLEVEL,(/155/))
+      ENDIF
+
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!       CALCULATION OF ETAPOINT --> TOTAL TIME DERIVATIVE OF       !
+!      ECMWF VERTICAL COORDINATE ETA MULTIPLIED BY DERIVATIVE     !
+!      OF PRESSURE IN ETA DIRECTION                               !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+! Initialisieren  Legendretransformation
+!	auf das LaT/LON Gitter
+! Without Gaussian grid calculation Legendre Polynomials are calculated
+! only for one latitude to save space
+
+      DO J=1,MAXB
+
+        CALL PLGNFA(MNAUF,BREITE(J),Z(0,1))
+
+        CALL PHGCUT(LNPS,PS(:,J,1),WSAVE,IFAX,Z,MNAUF,MNAUF,MAUF,MANF,MAXL,1,1)
+
+
+        IF(META .EQ. 0 .or. METADIFF .EQ. 1 ) THEN
+          CALL PHGRACUT(LNPS,DPSDL(:,J),DPSDM(:,J),WSAVE,IFAX,Z,H,MAUF, &
+      MNAUF,MAXL,1,MANF,1)
+        ENDIF
+      ENDDO
+     
+      PS=EXP(PS)
+
+! For debugging only
+      CALL STATIS(MAXL,MAXB,1,PS(:,:,1),RMS,MW,SIG)
+      WRITE(*,'(A12,3F11.4)') 'STATISTICS: ',RMS,MW,SIG
+      
+      
+       IF(MOMEGADIFF .ne. 0) THEN
+
+         CALL OMEGA(PS,DPSDL,DPSDM,DIV,UV(:,:,1),UV(:,:,MLEVEL+1), &
+      BREITE,OM,MLAT,AK,BK,MAXL*MAXB,MAXB,MLEVEL) 
+       ENDIF
+     
+       IF(META .EQ. 0 .OR. METADIFF .ne. 0) THEN
+         DPSDT=PS
+         CALL CONTGL(DPSDT,DPSDL,DPSDM,DIV,UV(:,:,1),UV(:,:,MLEVEL+1), &
+      BREITE,ETA,MLAT,AK,BK,MAXL*MAXB,MAXB,MLEVEL)
+       ENDIF
+
+      ENDIF ! MGAUSS
+
+! CREATE FILE VERTICAL.EC NEEDED BY POP MODEL
+
+      open(21,file='VERTICAL.EC')
+      write(21,'(a)')
+      write(21,'(a)') 'VERTICAL DISCRETIZATION OF POP MODEL'
+      write(21,'(a)')
+      write(21,'(i3,a)') MLEVEL,'   number of layers'
+      write(21,'(a)')
+      write(21,'(a)') '* A(NLEV+1)'
+      write(21,'(a)')
+      do 205 i=1,MLEVEL+1
+205      write(21,'(f18.12)') AK(I)
+      write(21,'(a)')
+      write(21,'(a)') '* B(NLEV+1)'
+      write(21,'(a)')
+      do 210 i=1,MLEVEL+1
+210      write(21,'(f18.12)') BK(I)
+      close(21)
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                     READING OF OMEGA                       !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+      IF(MOMEGA .NE. 0 ) THEN
+
+
+
+         ALLOCATE (OMR(MAXL, MAXB, MLEVEL))
+ 
+         FILENAME='fort.19'
+         CALL READLATLON(FILENAME,OMR,MAXL,MAXB,MLEVEL,(/135/))
+
+      IF(MOMEGADIFF .NE. 0 ) THEN
+
+      DO K=1,MLEVEL
+        CALL STATIS(MAXL,MAXB,1,ETA(:,:,K),RMS,MW,SIG)        
+        WRITE(*,'(A12,I3,3F11.4)') '       ETA: ',K,RMS,MW,SIG
+        CALL STATIS(MAXL,MAXB,1,OMR(:,:,K),RMS,MW,SIG)        
+        WRITE(*,'(A12,I3,3F11.4)') '     OMEGA: ',K,RMS,MW,SIG
+        CALL STATIS(MAXL,MAXB,1,OM(:,:,K)-OMR(:,:,K),RMS,MW,SIG)        
+        WRITE(*,'(A12,I3,3F11.4)') 'OMEGA DIFF: ',K,RMS,MW,SIG
+      ENDDO
+
+      ENDIF
+      ENDIF
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                     READING OF ETA                       !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+      IF(META .NE. 0 ) THEN
+
+         ALLOCATE (ETAR(MAXL, MAXB, MLEVEL))
+ 
+         P00=101325.
+         FILENAME='fort.21'
+         CALL READLATLON(FILENAME,ETAR,MAXL,MAXB,MLEVEL,(/77/))
+
+         if(MDPDETA .EQ. 1) THEN
+          DO K=1,MLEVEL
+           DAK=AK(K+1)-AK(K)
+           DBK=BK(K+1)-BK(K)
+           DO J=1,MAXB
+             DO I=1,MAXL
+               ETAR(I,J,K)=2*ETAR(I,J,K)*PS(I,J,1)*(DAK/PS(I,J,1)+DBK)/ &
+                          (DAK/P00+DBK)
+               IF(K .GT. 1) ETAR(I,J,K)=ETAR(I,J,K)-ETAR(I,J,K-1)
+             ENDDO
+           ENDDO
+          ENDDO
+         ENDIF
+
+        IF(METADIFF .NE. 0 ) THEN
+
+         DO K=1,MLEVEL
+          CALL STATIS(MAXL,MAXB,1,ETA(:,:,K),RMS,MW,SIG)        
+          WRITE(*,'(A12,I3,3F11.4)') '       ETA: ',K,RMS,MW,SIG
+          CALL STATIS(MAXL,MAXB,1,ETAR(:,:,K),RMS,MW,SIG)        
+          WRITE(*,'(A12,I3,3F11.4)') '     ETAR: ',K,RMS,MW,SIG
+          CALL STATIS(MAXL,MAXB,1,ETA(:,:,K)-ETAR(:,:,K),RMS,MW,SIG)        
+          WRITE(*,'(A12,I3,3F11.4)') 'ETA DIFF: ',K,RMS,MW,SIG
+         ENDDO
+         DO K=1,MLEVEL
+          WRITE(*,'(I3,2F11.4)') K,ETA(1,MAXB/2,K),ETAR(1,MAXB/2,K)
+         ENDDO
+        ELSE
+          ETA=ETAR
+        ENDIF
+      ENDIF
+
+      ALLOCATE (T(MAXL, MAXB, MLEVEL))
+      ALLOCATE (QA(MAXL, MAXB, MLEVEL))
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                      READING OF T                      !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!
+! OPENING OF UNBLOCKED GRIB FILE
+!
+      FILENAME='fort.11'
+      CALL READLATLON(FILENAME,T,MAXL,MAXB,MLEVEL,(/130/))
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                     READING OF SPECIFIC HUMIDITY                !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+      FILENAME='fort.17'
+      CALL READLATLON(FILENAME,QA,MAXL,MAXB,MLEVEL,(/133/))
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                     TEST READING OF UV from MARS (debug only)   !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+!      FILENAME='fort.22'
+!      CALL READLATLON(FILENAME,UV2,MAXL,MAXB,2*MLEVEL,2,(/131,132/))
+
+
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!                    WRITE MODEL LEVEL DATA TO fort.15            !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+!     Calculation of etadot in CONTGL needed scaled winds (ucosphi,vcosphi)
+!     Now we are transforming back to the usual winds. 
+      DO K=1,MLEVEL
+        DO J=2,MAXB-1
+          COSB=SQRT(1.0-(BREITE(J))*(BREITE(J)))
+          UV(:,J,K)=UV(:,J,K)/COSB
+          UV(:,J,MLEVEL+K)=UV(:,J,MLEVEL+K)/COSB
+        ENDDO
+! special treatment for poles, if necessary. 
+        DO J=1,MAXB,MAXB-1
+          COSB=SQRT(1.0-(BREITE(J))*(BREITE(J)))
+          if(1.0-BREITE(J)*BREITE(J) .gt. 0 .OR. MGAUSS .NE. 1) then
+            IF(RLA0 .EQ. -90.0 .AND. J .EQ. MAXB .OR. &
+             RLA1 .EQ. 90.0 .AND. J .EQ. 1) then
+             UV(:,J,K)=UV(:,J,K)*1.D6
+             UV(:,J,MLEVEL+K)=UV(:,J,MLEVEL+K)*1.D6
+            else
+             UV(:,J,K)=UV(:,J,K)/COSB
+             UV(:,J,MLEVEL+K)=UV(:,J,MLEVEL+K)/COSB
+            endif
+          else
+              HILFUV(5:MAXL,:)=0.
+              HILFUV(1:2,:)=0.
+            IF(J.EQ.MAXB) THEN
+! Suedpol
+              HILFUV(3:4,1)=CUA(:,4,K)
+              HILFUV(3:4,2)=CVA(:,4,K)
+            ELSE
+! Nordpol
+              HILFUV(3:4,1)=CUA(:,2,K)
+              HILFUV(3:4,2)=CVA(:,2,K)
+            ENDIF
+              CALL RFOURTR(HILFUV(:,1),WSAVE,IFAX,MAXL/2-1,MAXL,-1)
+              DO I=0,MAXL-1
+                IF(MANF+I.LE.MAXL) THEN
+                  UV(I+1,J,K)=HILFUV(MANF+I,1)
+                ELSE
+                  UV(I+1,J,K)=HILFUV(MANF-MAXL+I,1)
+                ENDIF
+              ENDDO
+              CALL RFOURTR(HILFUV(:,2),WSAVE,IFAX,MAXL/2-1,MAXL,-1)
+              DO I=0,MAXL-1
+                IF(MANF+I.LE.MAXL) THEN
+                  UV(I+1,J,MLEVEL+K)=HILFUV(MANF+I,2)
+                ELSE
+                  UV(I+1,J,MLEVEL+K)=HILFUV(MANF-MAXL+I,2)
+                ENDIF
+              ENDDO
+          endif
+      ENDDO
+      ENDDO
+
+! open output file
+      call grib_open_file(LUNIT,'fort.15','w')
+
+! we use temperature on lat/lon on model levels as template for model level data
+      LUNIT2=0
+      call grib_open_file(LUNIT2,'fort.11','r')
+      call grib_new_from_file(LUNIT2,igrib(1), iret)
+      call grib_close_file(LUNIT2)
+
+
+      CALL WRITELATLON(LUNIT,igrib(1),ogrib,UV(:,:,1),MAXL,MAXB,MLEVEL,MLEVELIST,1,(/131/))
+
+      CALL WRITELATLON(LUNIT,igrib(1),ogrib,UV(:,:,MLEVEL+1),MAXL,MAXB,MLEVEL,MLEVELIST,1,(/132/))
+
+      IF(MDPDETA .ne. 1 .AND. MGAUSS .EQ. 0 .and. META .eq. 1) THEN
+        CALL WRITELATLON(LUNIT,igrib(1),ogrib,ETA,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/77/))
+      ELSE
+        CALL WRITELATLON(LUNIT,igrib(1),ogrib,ETA,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/METAPAR/))
+      ENDIF
+
+      CALL WRITELATLON(LUNIT,igrib(1),ogrib,T,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/130/))
+
+      CALL WRITELATLON(LUNIT,igrib(1),ogrib,PS,MAXL,MAXB,1,'1',1,(/134/))
+ 
+      call grib_set(igrib(1),"levelType","ml")
+      call grib_set(igrib(1),"typeOfLevel","hybrid")
+      CALL WRITELATLON(LUNIT,igrib(1),ogrib,QA,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/133/))
+
+  
+      IF(MOMEGA .EQ. 1) THEN
+        call grib_open_file(LUNIT2,'fort.25','w')
+        CALL WRITELATLON(LUNIT2,igrib(1),ogrib,OMR,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/135/))
+
+        IF(MOMEGADIFF .EQ. 1) THEN
+
+          CALL WRITELATLON(LUNIT2,igrib(1),ogrib,DPSDT,MAXL,MAXB,1,'1',1,(/158/))
+
+        OM=OM-OMR
+        CALL WRITELATLON(LUNIT2,igrib(1),ogrib,OM,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/001/))
+      call grib_close_file(LUNIT2)
+        ENDIF
+      ENDIF
+
+      IF(META .EQ. 1 .and. METADIFF .EQ. 1) THEN
+        call grib_open_file(LUNIT2,'fort.26','w')
+        CALL WRITELATLON(LUNIT2,igrib(1),ogrib,ETAR,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/135/))
+
+!        IF(MOMEGADIFF .EQ. 1) THEN
+
+          CALL WRITELATLON(LUNIT2,igrib(1),ogrib,DPSDT,MAXL,MAXB,1,'1',1,(/158/))
+
+        OM=ETA-ETAR
+        CALL WRITELATLON(LUNIT2,igrib(1),ogrib,OM,MAXL,MAXB,MLEVEL,MLEVELIST,1,(/001/))
+      call grib_close_file(LUNIT2)
+!        ENDIF
+      ENDIF
+
+
+      call grib_close_file(LUNIT)
+
+
+
+ 2000 STOP 'SUCCESSFULLY FINISHED CONVERT_PRE: CONGRATULATIONS'
+ 3000 STOP 'ROUTINE CONVERT_PRE: ERROR'
+ 9999 stop 'ROUTINE CONVERT_PRE: ERROR'
+      END
+
+      
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+      INTEGER FUNCTION IA (FIELD1,NI,NJ,NK,G)
+
+      IMPLICIT NONE
+      INTEGER NI,NJ,NK,I,J,K
+      REAL FIELD1(NI,NJ,NK)
+      REAL G
+      REAL RMIN,RMAX,XMAX,A,A1,A2
+
+      RMAX=FIELD1(1,1,1)
+      RMIN=FIELD1(1,1,1)
+      
+      DO 100 K=1,NK
+      DO 100 J=1,NJ
+        DO 100 I=1,NI
+          IF (FIELD1(I,J,K).GT.RMAX)RMAX=FIELD1(I,J,K)
+          IF (FIELD1(I,J,K).LT.RMIN)RMIN=FIELD1(I,J,K)
+100   CONTINUE
+
+      IF (ABS(RMIN).GT.RMAX.OR.ABS(RMIN).EQ.RMAX) THEN
+      XMAX=ABS(RMIN)
+      ELSE
+      XMAX=RMAX
+      ENDIF
+      
+      IF (XMAX.EQ.0) THEN
+      IA = 0
+      RETURN
+      ENDIF
+      
+      A1=LOG10 ((G/10.d0)/XMAX)
+      A2=LOG10 ( G/XMAX )
+      IF(A1 .gt. A2) THEN
+        A=A2
+      ELSE 
+        A=A1
+      ENDIF
+      
+      IF (A.GT.0) IA=INT(A)
+      IF (A.LT.0) IA=INT(A-1.0)
+      
+      RETURN
+      END
+      
+      SUBROUTINE STATIS (NI,NJ,NK,PHI,RMS,MW,SIG)
+	IMPLICIT REAL (A-H,O-Z)
+
+      REAL PHI(NI,NJ,NK),SIG,MW,RMS,P
+ 
+      N=NI*NJ*NK
+ 
+      RMS=0.
+	MW=0.
+ 
+      DO 10 I=1,NI
+      DO 10 J=1,NJ
+        DO 10 K=1,NK
+	      P=PHI(I,J,K)
+          RMS=RMS+P*P
+ 		MW=MW+P
+10    CONTINUE
+
+      RMS=SQRT(RMS/N)
+	MW=MW/N
+	
+	IF(RMS*RMS-MW*MW.LT.0.) THEN	  
+	  SIG=0.0
+	ELSE
+	  SIG=SQRT(RMS*RMS-MW*MW)
+	ENDIF
+ 
+      RETURN
+      END
+      
Index: Source/Fortran/rwGRIB2.f90
===================================================================
--- Source/Fortran/rwGRIB2.f90	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Fortran/rwGRIB2.f90	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,265 @@
+      MODULE RWGRIB2
+
+      CONTAINS
+
+      SUBROUTINE READLATLON(FILENAME,FELD,MAXL,MAXB,MLEVEL,MPAR)
+
+      USE GRIB_API
+
+      IMPLICIT NONE
+
+        integer                            ::  ifile
+        integer                            ::  iret
+   integer                            ::  n,mk,parid,nm
+   integer                            ::  i,k
+   integer,dimension(:),allocatable   ::  igrib
+   integer                            ::  numberOfPointsAlongAParallel
+   integer                            ::  numberOfPointsAlongAMeridian
+   real, dimension(:), allocatable    ::  values
+   integer                            ::  numberOfValues
+   real,dimension(maxl,maxb,mlevel)   ::  feld  
+   integer:: maxl,maxb,mlevel,mstride,mpar(:),irest,div,level
+   integer :: l(size(mpar))
+   character*(*):: filename   
+   
+   feld=0.                          
+
+   call grib_open_file(ifile, TRIM(FILENAME),'r')
+ 
+   ! count the messages in the file
+   call grib_count_in_file(ifile,n)
+   allocate(igrib(n))
+   igrib=-1
+ 
+   ! Load the messages from the file.
+   DO i=1,n
+      call grib_new_from_file(ifile,igrib(i), iret)
+   END DO
+ 
+   ! we can close the file
+   call grib_close_file(ifile)
+ 
+      nm=size(mpar)
+      div=mlevel/nm
+      l=0
+      
+   ! Loop on all the messages in memory
+  iloop:  DO i=1,n
+!      write(*,*) 'processing message number ',i
+      !     get as a integer
+      call grib_get(igrib(i),'numberOfPointsAlongAParallel', &
+           numberOfPointsAlongAParallel)
+ 
+      !     get as a integer
+      call grib_get(igrib(i),'numberOfPointsAlongAMeridian', &
+           numberOfPointsAlongAMeridian)
+
+      call grib_get(igrib(i),'numberOfVerticalCoordinateValues',mk)
+
+      call grib_get_size(igrib(i),'values',numberOfValues)
+!      write(*,*) 'numberOfValues=',numberOfValues
+ 
+      allocate(values(numberOfValues), stat=iret)
+      !     get data values
+      call grib_get(igrib(i),'values',values)
+
+      call grib_get(igrib(i),'paramId',parid)
+      call grib_get(igrib(i),'level',level)
+
+ kloop:  do k=1,nm
+        if(parid .eq. mpar(k)) then
+!         l(k)=l(k)+1
+         feld(:,:,(k-1)*div+level)=reshape(values,(/maxl,maxb/))
+!         print*,(k-1)*div+l(k),parid
+         exit kloop
+        endif
+      enddo kloop
+      if(k .gt. nm .and. parid .ne. mpar(nm)) then
+        write(*,*) k,nm,parid,mpar(nm)
+        write(*,*) 'ERROR readlatlon: parameter ',parid,'is not',mpar
+        stop
+      endif
+
+!      print*,i
+   END DO iloop
+   write(*,*) 'readlatlon: ',i-1,' records read'
+ 
+   DO i=1,n
+     call grib_release(igrib(i))
+   END DO
+ 
+   deallocate(values)
+   deallocate(igrib)
+
+      END SUBROUTINE READLATLON
+
+      SUBROUTINE WRITELATLON(iunit,igrib,ogrib,FELD,MAXL,MAXB,MLEVEL,&
+      MLEVELIST,MSTRIDE,MPAR)
+
+      USE GRIB_API
+
+      IMPLICIT NONE
+
+      INTEGER IFIELD,MLEVEL,MNAUF,I,J,K,L,MSTRIDE,IERR,JOUT
+      INTEGER MPAR(MSTRIDE),MAXL,MAXB,LEVMIN,LEVMAX
+      INTEGER IUNIT,igrib,ogrib
+      REAL ZSEC4(MAXL*MAXB)
+      REAL    FELD(MAXL,MAXB,MLEVEL)
+      CHARACTER*(*) MLEVELIST
+      INTEGER ILEVEL(MLEVEL),MLINDEX(MLEVEL+1),LLEN
+
+    ! parse MLEVELIST
+   
+      LLEN=len(trim(MLEVELIST))
+      if(index(MLEVELIST,'to') .ne. 0 .or. index(MLEVELIST,'TO') .ne. 0) THEN
+        i=index(MLEVELIST,'/')
+        read(MLEVELIST(1:i-1),*) LEVMIN
+        i=index(MLEVELIST,'/',.true.)
+        read(MLEVELIST(i+1:LLEN),*) LEVMAX
+        l=0
+        do i=LEVMIN,LEVMAX
+          l=l+1
+          ILEVEL(l)=i
+        enddo
+      else
+        l=1
+	MLINDEX(1)=0
+        do i=1,LLEN
+          if(MLEVELIST(i:i) .eq. '/') THEN
+	    l=l+1
+	    MLINDEX(l)=i
+	  endif
+	enddo
+	MLINDEX(l+1)=LLEN+1
+	do i=1,l
+	  read(MLEVELIST(MLINDEX(i)+1:MLINDEX(i+1)-1),*) ILEVEL(i)
+	enddo
+      endif 
+
+      DO k=1,l
+        call grib_set(igrib,"level",ILEVEL(k))
+        DO j=1,MSTRIDE
+         call grib_set(igrib,"paramId",MPAR(j))
+!         if(MPAR(j) .eq. 87) then
+!           call grib_set(igrib,"shortName","etadot")
+!           call grib_set(igrib,"units","Pa,s**-1")
+!         endif
+!         if(MPAR(j) .eq. 77) then
+!           call grib_set(igrib,"shortName","etadot")
+!           call grib_set(igrib,"units","s**-1")
+!         endif
+	 if(l .ne. mlevel) then
+           zsec4(1:maxl*maxb)=RESHAPE(FELD(:,:,ILEVEL(k)),(/maxl*maxb/))
+	 else
+	   zsec4(1:maxl*maxb)=RESHAPE(FELD(:,:,k),(/maxl*maxb/))
+	 endif
+         call grib_set(igrib,"values",zsec4)
+
+         call grib_write(igrib,iunit)
+
+        ENDDO
+      ENDDO
+
+
+
+      END SUBROUTINE WRITELATLON
+
+      SUBROUTINE READSPECTRAL(FILENAME,CXMN,MNAUF,MLEVEL,&
+        MAXLEV,MPAR,A,B)
+
+      USE GRIB_API
+
+      IMPLICIT NONE
+
+
+        integer                            ::  ifile
+   integer                            ::  iret
+   integer                            ::  n,mk,div,nm,k
+   integer                            ::  i,j,parid
+   integer,dimension(:),allocatable   ::  igrib
+   real, dimension(:), allocatable    ::  values
+   integer                            ::  numberOfValues,maxlev
+   REAL :: A(MAXLEV+1),B(MAXLEV+1),pv(2*MAXLEV+2)
+   REAL:: CXMN(0:(MNAUF+1)*(MNAUF+2)-1,MLEVEL)
+integer:: maxl,maxb,mlevel,mstride,mpar(:),mnauf,ioffset,ipar,ilev,l(size(mpar))
+character*(*):: filename                             
+ 
+   call grib_open_file(ifile, TRIM(FILENAME),'r')
+ 
+   ! count the messages in the file
+   call grib_count_in_file(ifile,n)
+   allocate(igrib(n))
+   igrib=-1
+ 
+   ! Load the messages from the file.
+   DO i=1,n
+      call grib_new_from_file(ifile,igrib(i), iret)
+   END DO
+ 
+   ! we can close the file
+   call grib_close_file(ifile)
+ 
+    l=0
+   ! Loop on all the messages in memory
+   iloop: DO i=1,n
+   ! write(*,*) 'processing message number ',i
+      !     get as a integer
+      call grib_get(igrib(i),'pentagonalResolutionParameterJ', j)
+
+      call grib_get_size(igrib(i),'values',numberOfValues)
+   !   write(*,*) 'numberOfValues=',numberOfValues
+ 
+      call grib_get(igrib(i),'numberOfVerticalCoordinateValues',mk)
+
+      call grib_get(igrib(i),'level',ilev)
+
+      
+
+      call grib_get(igrib(i),'pv',pv)
+
+      allocate(values(numberOfValues), stat=iret)
+      !     get data values
+      call grib_get(igrib(i),'values',values)
+
+!      IOFFSET=mod(i-1,MSTRIDE)*(mk/2-1)
+!           CXMN(:,IOFFSET+ilev)=values(1:(MNAUF+1)*(MNAUF+2))
+
+      call grib_get(igrib(i),'paramId',parid)
+    nm=size(mpar)
+    div=mlevel/nm
+    kloop:  do k=1,nm
+        if(parid .eq. mpar(k)) then
+         l(k)=l(k)+1
+         cxmn(:,(k-1)*div+l(k))=values(1:(MNAUF+1)*(MNAUF+2))
+!         print*,(k-1)*div+l(k),parid
+         exit kloop
+        endif
+        
+      enddo kloop
+      if(k .gt. nm .and. parid .ne. mpar(nm)) then
+        write(*,*) k,nm,parid,mpar(nm)
+        write(*,*) 'ERROR readspectral: parameter ',parid,'is not',mpar
+        stop
+      endif
+
+!      print*,i
+
+   END DO iloop
+
+   write(*,*) 'readspectral: ',i-1,' records read'
+ 
+   DO i=1,n
+     call grib_release(igrib(i))
+   END DO
+ 
+   deallocate(values)
+   deallocate(igrib)
+
+
+
+        A=pv(1:1+MAXLEV)
+        B=pv(2+MAXLEV:2*MAXLEV+2)
+
+      END SUBROUTINE READSPECTRAL
+
+      END MODULE RWGRIB2
Index: Source/Python/Classes/ControlFile.py
===================================================================
--- Source/Python/Classes/ControlFile.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Classes/ControlFile.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,705 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Leopold Haimberger (University of Vienna)
+#
+# @Date: November 2015
+#
+# @Change History:
+#
+#   February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - applied some minor modifications in programming style/structure
+#        - changed name of class Control to ControlFile for more
+#          self-explanation naming
+#        - outsource of class ControlFile
+#        - initialisation of class attributes ( to avoid high number of
+#          conditional statements and set default values )
+#        - divided assignment of attributes and the check of conditions
+#        - outsourced the commandline argument assignments to control attributes
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#
+# @Class Methods:
+#    __init__
+#    _read_controlfile
+#    __str__
+#    assign_args_to_control
+#    assign_envs_to_control
+#    check_conditions
+#    check_install_conditions
+#    to_list
+#*******************************************************************************
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+import re
+import sys
+import inspect
+
+# software specific classes and modules from flex_extract
+sys.path.append('../')
+import _config
+from Mods.tools import my_error, silent_remove
+from Mods.checks import (check_grid, check_area, check_levels, check_purefc,
+                         check_step, check_mail, check_queue, check_pathes,
+                         check_dates, check_maxstep, check_type, check_request,
+                         check_basetime, check_public, check_acctype,
+                         check_acctime, check_accmaxstep, check_time,
+                         check_logicals_type, check_len_type_time_step,
+                         check_addpar, check_job_chunk, check_number)
+
+# ------------------------------------------------------------------------------
+# CLASS
+# ------------------------------------------------------------------------------
+class ControlFile(object):
+    '''
+    Contains the information which are stored in the CONTROL files.
+
+    The CONTROL file is the steering part of the FLEXPART extraction
+    software. All necessary parameters needed to retrieve the data fields
+    from the MARS archive for driving FLEXPART are set in a CONTROL file.
+    Some specific parameters like the start and end dates can be overwritten
+    by the command line parameters, but in generel all parameters needed
+    for a complete set of fields for FLEXPART can be set in the CONTROL file.
+
+    Attributes
+    ----------
+    controlfile : str
+        The name of the control file to be processed. Default value is the
+        filename passed to the init function when initialised.
+
+    start_date : str
+        The first day of the retrieval period. Default value is None.
+
+    end_date :str
+        The last day of the retrieval period. Default value is None.
+
+    date_chunk : int
+        Length of period for a single mars retrieval. Default value is 3.
+
+    dtime :str
+        The time step in hours. Default value is None.
+
+    basetime : int
+        The time for a half day retrieval. The 12 hours upfront are to be
+        retrieved. Default value is None.
+
+    maxstep : int
+        The maximum forecast step for non flux data. Default value is None.
+
+    type : list of str
+        List of field type per retrieving hour. Default value is None.
+
+    time : list of str
+        List of retrieving times in hours. Default valuer is None.
+
+    step : list of str or str
+        List of forecast time steps in hours for non flux data.
+        Default value is None.
+
+    acctype : str
+        The field type for the accumulated forecast fields.
+        Default value is None.
+
+    acctime : str
+        The starting time of the accumulated forecasts. Default value is None.
+
+    accmaxstep : int
+        The maximum forecast step for the accumulated forecast fields
+        (flux data). Default value is None.
+
+    marsclass : str
+        Characterisation of dataset. Default value is None.
+
+    dataset : str
+        For public datasets there is the specific naming and parameter
+        dataset which has to be used to characterize the type of
+        data. Default value is None.
+
+    stream : str
+        Identifies the forecasting system used to generate the data.
+        Default value is None.
+
+    number : str
+        Selects the member in ensemble forecast run. Default value is 'OFF'.
+
+    expver : str
+        The version number of the dataset. Default value is '1'.
+
+    gaussian : str
+        This parameter is deprecated and should no longer be used.
+        Specifies the desired type of Gaussian grid for the output.
+        Default value is an empty string ''.
+
+    grid : str
+        Specifies the output grid which can be either a Gaussian grid
+        or a Latitude/Longitude grid. Default value is None.
+
+    area : str
+        Specifies the desired sub-area of data to be extracted.
+        Default value is None.
+
+    left : str
+        The western most longitude of the area to be extracted.
+        Default value is None.
+
+    lower : str
+        The southern most latitude of the area to be extracted.
+        Default value is None.
+
+    upper : str
+        The northern most latitued of the area to be extracted.
+        Default value is None.
+
+    right : str
+        The eastern most longitude of the area to be extracted.
+        Default value is None.
+
+    level : str
+        Specifies the maximum level. Default value is None.
+
+    levelist : str
+        Specifies the required level list. Default value is None.
+
+    resol : str
+        Specifies the desired triangular truncation of retrieved data,
+        before carrying out any other selected post-processing.
+        Default value is None.
+
+    gauss : int
+        Switch to select gaussian fields (1) or regular lat/lon (0).
+        Default value is 0.
+
+    accuracy : int
+        Specifies the number of bits per value to be used in the
+        generated GRIB coded fields. Default value is 24.
+
+    omega : int
+       Switch to select omega retrieval (1) or not (0). Default value is 0.
+
+    omegadiff : int
+        Switch to decide to calculate Omega and Dps/Dt from continuity
+        equation for diagnostic purposes (1) or not (0). Default value is 0.
+
+    eta : int
+        Switch to select direct retrieval of etadot from MARS (1) or
+        wether it has to be calculated (0). Then Default value is 0.
+
+    etadiff : int
+        Switch to select calculation of etadot and Dps/Dt from continuity
+        equation for diagnostic purposes (1) or not (0). Default value is 0.
+
+    etapar : int
+        GRIB parameter Id for etadot fields. Default value is 77.
+
+    dpdeta : int
+        Switch to select multiplication of etadot with dpdeta.
+        Default value is 1.
+
+    smooth : int
+        Spectral truncation of ETADOT after calculation on Gaussian grid.
+        Default value is 0.
+
+    format : str
+        The format of the GRIB data. Default value is 'GRIB1'.
+
+    addpar : str
+        List of additional surface level ECMWF parameter to be retrieved.
+        Default value is None.
+
+    prefix : str
+        Prefix string for the final FLEXPART/FLEXTRA ready input files.
+        Default value is 'EN'.
+
+    cwc : int
+        Switch to select wether the sum of cloud liquid water content and
+        cloud ice water content should be retrieved. Default value is 0.
+
+    wrf : int
+        Switch to select further parameters for retrievment to support
+        WRF simulations. Default value is 0.
+
+    ecfsdir : str
+        Path to the ECMWF storage  'ectmp:/${USER}/econdemand/'
+
+    mailfail : list of str
+        Email list for sending error log files from ECMWF servers.
+        The email addresses should be seperated by a comma.
+        Default value is ['${USER}'].
+
+    mailops : list of str
+        Email list for sending operational log files from ECMWF servers.
+        The email addresses should be seperated by a comma.
+        Default value is ['${USER}'].
+
+    grib2flexpart : int 0
+        Switch to select generation of preprocessed FLEXPART files ".fp".
+        If it is selected, the program grib2flexpart will try
+        to convert the flex_extract output files into ".fp" format.
+
+    ecstorage : int
+        Switch to select storage of FLEXPART ready output files
+        in the ECFS file system. Default value is 0.
+
+    ectrans : int
+        Switch to select the transfer of FLEXPART ready output files
+        to the gateway server. Default value is 0.
+
+    inputdir : str
+        Path to the temporary directory for the retrieval grib files and
+        other processing files. Default value is _config.PATH_INPUT_DIR.
+
+    outputdir : str
+        Path to the final directory where the final FLEXPART ready input
+        files are stored. Default value is None.
+
+    flexextractdir : str
+        Path to the flex_extract root directory. Default value is
+        _config.PATH_FLEXEXTRACT_DIR.
+
+    exedir : str
+        Path to the FORTRAN executable file. Default value is
+        _config.PATH_FORTRAN_SRC.
+
+    installdir : str
+        Path to a FLEXPART root directory. Default value is None.
+
+    makefile : str
+        Name of the makefile to be used for the Fortran program.
+        Default value is 'Makefile.gfortran'.
+
+    destination : str
+        The remote destination which is used to transfer files
+        from ECMWF server to local gateway server. Default value is None.
+
+    gateway : str
+        The gateway server the user is using. Default value is None.
+
+    ecuid : str
+        The user id on ECMWF server. Default value is None.
+
+    ecgid : str
+        The group id on ECMWF server. Default value is None.
+
+    install_target : str
+        Defines the location where the installation is to be done.
+        Default value is None.
+
+    debug : int
+        Switch to keep temporary files at the end of postprocessing (1) or
+        to delete all temporary files except the final output files (0).
+        Default value is 0.
+
+    oper : int
+        Switch to prepare the operational job script. Start date, end date and
+        basetime will be prepared with environment variables.
+        Default value is 0.
+
+    request : int
+        Switch to select between just retrieving the data (0), writing the mars
+        parameter values to a csv file (1) or doing both (2).
+        Default value is 0.
+
+    public : int
+        Switch to select kind of ECMWF Web Api access and the
+        possible data sets. Public data sets (1) and Memberstate data sets (0).
+        Default value is 0.
+
+    ec_api : boolean
+        Tells wether the ECMWF Web API was able to load or not.
+        Default value is None.
+
+    cds_api : boolean
+        Tells wether the CDS API was able to load or not.
+        Default value is None.
+
+    purefc : int
+        Switch to decide wether the job is a pure forecast retrieval or
+        coupled with analysis data. Default value is 0.
+
+    rrint : int
+        Switch to select between old precipitation disaggregation method (0)
+        or the new IA3 disaggegration method (1). Default value is 0.
+
+    doubleelda : int
+        Switch to select the calculation of extra ensemble members for the
+        ELDA stream. It doubles the amount of retrieved ensemble members.
+
+    logicals : list of str
+        List of the names of logical switches which controls the flow
+        of the program. Default list is ['gauss', 'omega', 'omegadiff', 'eta',
+        'etadiff', 'dpdeta', 'cwc', 'wrf', 'grib2flexpart', 'ecstorage',
+        'ectrans', 'debug', 'request', 'public', 'purefc', 'rrint', 'doubleelda']
+    '''
+
+    def __init__(self, filename):
+        '''Initialises the instance of ControlFile class and defines
+        all class attributes with default values. Afterwards calls
+        function __read_controlfile__ to read parameter from Control file.
+
+        Parameters
+        ----------
+        filename : str
+            Name of CONTROL file.
+
+        Return
+        ------
+
+        '''
+
+        # list of all possible class attributes and their default values
+        self.controlfile = filename
+        self.start_date = None
+        self.end_date = None
+        self.date_chunk = 3
+        self.job_chunk = None
+        self.dtime = None
+        self.basetime = None
+        self.maxstep = None
+        self.type = None
+        self.time = None
+        self.step = None
+        self.acctype = None
+        self.acctime = None
+        self.accmaxstep = None
+        self.marsclass = None
+        self.dataset = None
+        self.stream = None
+        self.number = 'OFF'
+        self.expver = '1'
+        self.gaussian = ''
+        self.grid = None
+        self.area = ''
+        self.left = None
+        self.lower = None
+        self.upper = None
+        self.right = None
+        self.level = None
+        self.levelist = None
+        self.resol = None
+        self.gauss = 0
+        self.accuracy = 24
+        self.omega = 0
+        self.omegadiff = 0
+        self.eta = 0
+        self.etadiff = 0
+        self.etapar = 77
+        self.dpdeta = 1
+        self.smooth = 0
+        self.format = 'GRIB1'
+        self.addpar = None
+        self.prefix = 'EN'
+        self.cwc = 0
+        self.wrf = 0
+        self.ecfsdir = 'ectmp:/${USER}/econdemand/'
+        self.mailfail = ['${USER}']
+        self.mailops = ['${USER}']
+        self.grib2flexpart = 0
+        self.ecstorage = 0
+        self.ectrans = 0
+        self.inputdir = _config.PATH_INPUT_DIR
+        self.outputdir = None
+        self.flexextractdir = _config.PATH_FLEXEXTRACT_DIR
+        self.exedir = _config.PATH_FORTRAN_SRC
+        self.installdir = None
+        self.makefile = 'Makefile.gfortran'
+        self.destination = None
+        self.gateway = None
+        self.ecuid = None
+        self.ecgid = None
+        self.install_target = None
+        self.debug = 0
+        self.oper = 0
+        self.request = 0
+        self.public = 0
+        self.ec_api = None
+        self.cds_api = None
+        self.purefc = 0
+        self.rrint = 0
+        self.doubleelda = 0
+
+        self.logicals = ['gauss', 'omega', 'omegadiff', 'eta', 'etadiff',
+                         'dpdeta', 'cwc', 'wrf', 'grib2flexpart', 'ecstorage',
+                         'ectrans', 'debug', 'oper', 'request', 'public',
+                         'purefc', 'rrint', 'doubleelda']
+
+        self._read_controlfile()
+
+        return
+
+    def _read_controlfile(self):
+        '''Read CONTROL file and assign all CONTROL file variables.
+
+        Parameters
+        ----------
+
+        Return
+        ------
+
+        '''
+
+        try:
+            cfile = os.path.join(_config.PATH_CONTROLFILES, self.controlfile)
+            with open(cfile) as f:
+                fdata = f.read().split('\n')
+        except IOError:
+            print('Could not read CONTROL file "' + cfile + '"')
+            print('Either it does not exist or its syntax is wrong.')
+            print('Try "' + sys.argv[0].split('/')[-1] + \
+                      ' -h" to print usage information')
+            sys.exit(1)
+
+        # go through every line and store parameter
+        for ldata in fdata:
+            if ldata and ldata[0] == '#':
+                # ignore comment line in control file
+                continue
+            if '#' in ldata:
+                # cut off comment
+                ldata = ldata.split('#')[0]
+            data = ldata.split()
+            if len(data) > 1:
+                if 'm_' in data[0].lower():
+                    data[0] = data[0][2:]
+                if data[0].lower() == 'class':
+                    data[0] = 'marsclass'
+                if data[0].lower() == 'day1':
+                    data[0] = 'start_date'
+                if data[0].lower() == 'day2':
+                    data[0] = 'end_date'
+                if len(data) == 2:
+                    if '$' in data[1]:
+                        setattr(self, data[0].lower(), data[1])
+                        while '$' in data[1]:
+                            i = data[1].index('$')
+                            j = data[1].find('{')
+                            k = data[1].find('}')
+                            var = os.getenv(data[1][j+1:k])
+                            if var is not None:
+                                data[1] = data[1][:i] + var + data[1][k+1:]
+                            else:
+                                my_error('Could not find variable '
+                                         + data[1][j+1:k] + ' while reading ' +
+                                         self.controlfile)
+                        setattr(self, data[0].lower() + '_expanded', data[1])
+                    else:
+                        if data[1].lower() != 'none':
+                            setattr(self, data[0].lower(), data[1])
+                        else:
+                            setattr(self, data[0].lower(), None)
+                elif len(data) > 2:
+                    setattr(self, data[0].lower(), (data[1:]))
+            else:
+                pass
+
+        return
+
+    def __str__(self):
+        '''Prepares a string which have all the ControlFile class attributes
+        with its associated values. Each attribute is printed in one line and
+        in alphabetical order.
+
+        Example
+        -------
+        'age': 10
+        'color': 'Spotted'
+        'kids': 0
+        'legs': 2
+        'name': 'Dog'
+        'smell': 'Alot'
+
+        Parameters
+        ----------
+
+        Return
+        ------
+        string
+            Single string of concatenated ControlFile class attributes
+            with their values
+        '''
+        import collections
+
+        attrs = vars(self).copy()
+        attrs = collections.OrderedDict(sorted(attrs.items()))
+
+        return '\n'.join("%s: %s" % item for item in attrs.items())
+
+    def assign_args_to_control(self, args):
+        '''Overwrites the existing ControlFile instance attributes with
+        the command line arguments.
+
+        Parameters
+        ----------
+        args : Namespace
+            Contains the commandline arguments from script/program call.
+
+        Return
+        ------
+
+        '''
+
+        # get dictionary of command line parameters and eliminate all
+        # parameters which are None (were not specified)
+        args_dict = vars(args)
+        arguments = {k : args_dict[k] for k in args_dict
+                     if args_dict[k] != None}
+
+        # assign all passed command line arguments to ControlFile instance
+        for k, v in arguments.items():
+            setattr(self, str(k), v)
+
+        return
+
+    def assign_envs_to_control(self, envs):
+        '''Assigns the ECMWF environment parameter.
+
+        Parameters
+        ----------
+        envs : dict of str
+            Contains the ECMWF environment parameternames "ECUID", "ECGID",
+            "DESTINATION" and "GATEWAY" with its corresponding values.
+            They were read from the file "ECMWF_ENV".
+
+        Return
+        ------
+
+        '''
+
+        for k, v in envs.items():
+            setattr(self, str(k).lower(), str(v))
+
+        return
+
+    def check_conditions(self, queue):
+        '''Checks a couple of necessary attributes and conditions,
+        such as if they exist and contain values.
+        Otherwise set default values.
+
+        Parameters
+        ----------
+        queue : str
+            Name of the queue if submitted to the ECMWF servers.
+            Used to check if ecuid, ecgid, gateway and destination
+            are set correctly and are not empty.
+
+        Return
+        ------
+
+        '''
+        check_logicals_type(self, self.logicals)
+
+        self.mailfail = check_mail(self.mailfail)
+
+        self.mailops = check_mail(self.mailops)
+
+        check_queue(queue, self.gateway, self.destination,
+                    self.ecuid, self.ecgid)
+
+        self.outputdir, self.installdir = check_pathes(self.inputdir,
+             self.outputdir, self.installdir, self.flexextractdir)
+
+        self.start_date, self.end_date = check_dates(self.start_date,
+                                                     self.end_date)
+
+        self.basetime = check_basetime(self.basetime)
+
+        self.levelist, self.level = check_levels(self.levelist, self.level)
+
+        self.step = check_step(self.step, self.mailfail)
+
+        self.maxstep = check_maxstep(self.maxstep, self.step)
+
+        check_request(self.request,
+                      os.path.join(self.inputdir, _config.FILE_MARS_REQUESTS))
+
+        check_public(self.public, self.dataset)
+
+        self.type = check_type(self.type, self.step)
+
+        self.time = check_time(self.time)
+
+        self.purefc = check_purefc(self.type)
+
+        self.type, self.time, self.step = check_len_type_time_step(self.type,
+                                                                   self.time,
+                                                                   self.step,
+                                                                   self.maxstep,
+                                                                   self.purefc)
+
+        self.acctype = check_acctype(self.acctype, self.type)
+
+        self.acctime = check_acctime(self.acctime, self.marsclass,
+                                     self.purefc, self.time)
+
+        self.accmaxstep = check_accmaxstep(self.accmaxstep, self.marsclass,
+                                           self.purefc, self.maxstep)
+
+        self.grid = check_grid(self.grid)
+
+        self.area = check_area(self.grid, self.area, self.upper, self.lower,
+                               self.left, self.right)
+
+        self.addpar = check_addpar(self.addpar)
+
+        self.job_chunk = check_job_chunk(self.job_chunk)
+
+        self.number = check_number(self.number, self.mailfail)
+
+        return
+
+    def to_list(self):
+        '''Just generates a list of strings containing the attributes and
+        assigned values except the attributes "_expanded", "exedir",
+        "flexextractdir" and "installdir".
+
+        Parameters
+        ----------
+
+        Return
+        ------
+        l : list of *
+            A sorted list of the all ControlFile class attributes with
+            their values except the attributes "_expanded", "exedir",
+            "flexextractdir" and "installdir".
+        '''
+
+        import collections
+
+        attrs = collections.OrderedDict(sorted(vars(self).copy().items()))
+
+        l = list()
+
+        for item in attrs.items():
+            if '_expanded' in item[0]:
+                pass
+            elif 'exedir' in item[0]:
+                pass
+            elif 'installdir' in item[0]:
+                pass
+            elif 'flexextractdir' in item[0]:
+                pass
+            else:
+                if isinstance(item[1], list):
+                    stot = ''
+                    for s in item[1]:
+                        stot += s + ' '
+
+                    l.append("%s %s\n" % (item[0], stot))
+                else:
+                    l.append("%s %s\n" % item)
+
+        return sorted(l)
+
Index: Source/Python/Classes/EcFlexpart.py
===================================================================
--- Source/Python/Classes/EcFlexpart.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Classes/EcFlexpart.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,1867 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Fouilloux (University of Oslo)
+#
+# @Date: October 2014
+#
+# @Change History:
+#
+#    November 2015 - Leopold Haimberger (University of Vienna):
+#        - extended with class Control
+#        - removed functions mkdir_p, daterange, years_between, months_between
+#        - added functions darain, dapoly, to_param_id, init128, normal_exit,
+#          my_error, clean_up, install_args_and_control,
+#          interpret_args_and_control,
+#        - removed function __del__ in class EIFLexpart
+#        - added the following functions in EIFlexpart:
+#            - create_namelist
+#            - process_output
+#            - deacc_fluxes
+#        - modified existing EIFlexpart - functions for the use in
+#          flex_extract
+#        - retrieve also longer term forecasts, not only analyses and
+#          short term forecast data
+#        - added conversion into GRIB2
+#
+#    February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - removed function getFlexpartTime in class EcFlexpart
+#        - outsourced class ControlFile
+#        - outsourced class MarsRetrieval
+#        - changed class name from EIFlexpart to EcFlexpart
+#        - applied minor code changes (style)
+#        - removed "dead code" , e.g. retrieval of Q since it is not needed
+#        - removed "times" parameter from retrieve-method since it is not used
+#        - seperated function "retrieve" into smaller functions (less code
+#          duplication, easier testing)
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+#pylint: disable=unsupported-assignment-operation
+# this is disabled because for this specific case its an error in pylint
+#pylint: disable=consider-using-enumerate
+# this is not useful in this case
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+import sys
+import glob
+import shutil
+import subprocess
+from datetime import datetime, timedelta
+
+# software specific classes and modules from flex_extract
+sys.path.append('../')
+import _config
+from Classes.GribUtil import GribUtil
+from Mods.tools import (init128, to_param_id, silent_remove, product,
+                        my_error, make_dir, get_informations, get_dimensions,
+                        execute_subprocess, to_param_id_with_tablenumber,
+                        generate_retrieval_period_boundary)
+from Classes.MarsRetrieval import MarsRetrieval
+from Classes.UioFiles import UioFiles
+import Mods.disaggregation as disaggregation
+
+# ------------------------------------------------------------------------------
+# CLASS
+# ------------------------------------------------------------------------------
+class EcFlexpart(object):
+    '''
+    Class to represent FLEXPART specific ECMWF data.
+
+    FLEXPART needs grib files in a specifc format. All necessary data fields
+    for one time step are stored in a single file. The class represents an
+    instance with all the parameter and settings necessary for retrieving
+    MARS data and modifing them so they are fitting FLEXPART needs. The class
+    is able to disaggregate the fluxes and convert grid types to the one needed
+    by FLEXPART, therefore using the FORTRAN program.
+
+    Attributes
+    ----------
+    mreq_count : int
+        Counter for the number of generated mars requests.
+
+    inputdir : str
+        Path to the directory where the retrieved data is stored.
+
+    dataset : str
+        For public datasets there is the specific naming and parameter
+        dataset which has to be used to characterize the type of
+        data.
+
+    basetime : int
+        The time for a half day retrieval. The 12 hours upfront are to be
+        retrieved.
+
+    dtime : str
+        Time step in hours.
+
+    acctype : str
+        The field type for the accumulated forecast fields.
+
+    acctime : str
+        The starting time from the accumulated forecasts.
+
+    accmaxstep : str
+        The maximum forecast step for the accumulated forecast fields.
+
+    marsclass : str
+        Characterisation of dataset.
+
+    stream : str
+        Identifies the forecasting system used to generate the data.
+
+    number : str
+        Selects the member in ensemble forecast run.
+
+    resol : str
+        Specifies the desired triangular truncation of retrieved data,
+        before carrying out any other selected post-processing.
+
+    accuracy : str
+        Specifies the number of bits per value to be used in the
+        generated GRIB coded fields.
+
+    addpar : str
+        List of additional parameters to be retrieved.
+
+    level : str
+        Specifies the maximum level.
+
+    expver : str
+        The version of the dataset.
+
+    levelist : str
+        Specifies the required levels.
+
+    glevelist : str
+        Specifies the required levels for gaussian grids.
+
+    gaussian : str
+        This parameter is deprecated and should no longer be used.
+        Specifies the desired type of Gaussian grid for the output.
+
+    grid : str
+        Specifies the output grid which can be either a Gaussian grid
+        or a Latitude/Longitude grid.
+
+    area : str
+        Specifies the desired sub-area of data to be extracted.
+
+    purefc : int
+        Switch for definition of pure forecast mode or not.
+
+    outputfilelist : list of str
+        The final list of FLEXPART ready input files.
+
+    types : dictionary
+        Determines the combination of type of fields, time and forecast step
+        to be retrieved.
+
+    params : dictionary
+        Collection of grid types and their corresponding parameters,
+        levels, level types and the grid definition.
+
+    server : ECMWFService or ECMWFDataServer
+        This is the connection to the ECMWF data servers.
+
+    public : int
+        Decides which Web API Server version is used.
+
+    dates : str
+        Contains start and end date of the retrieval in the format
+        "YYYYMMDD/to/YYYYMMDD"
+    '''
+
+    # --------------------------------------------------------------------------
+    # CLASS FUNCTIONS
+    # --------------------------------------------------------------------------
+    def __init__(self, c, fluxes=False):
+        '''Creates an object/instance of EcFlexpart with the associated
+        settings of its attributes for the retrieval.
+
+        Parameters:
+        -----------
+        c : ControlFile
+            Contains all the parameters of CONTROL file and
+            command line.
+
+        fluxes : boolean, optional
+            Decides if the flux parameter settings are stored or
+            the rest of the parameter list.
+            Default value is False.
+
+        Return
+        ------
+
+        '''
+        # set a counter for the number of generated mars requests
+        self.mreq_count = 0
+
+        self.inputdir = c.inputdir
+        self.dataset = c.dataset
+        self.basetime = c.basetime
+        self.dtime = c.dtime
+        self.acctype = c.acctype
+        self.acctime = c.acctime
+        self.accmaxstep = c.accmaxstep
+        self.marsclass = c.marsclass
+        self.stream = c.stream
+        self.number = c.number
+        self.resol = c.resol
+        self.accuracy = c.accuracy
+        self.addpar = c.addpar
+        self.level = c.level
+        self.expver = c.expver
+        self.levelist = c.levelist
+        self.glevelist = '1/to/' + c.level # in case of gaussian grid
+        self.gaussian = c.gaussian
+        self.grid = c.grid
+        self.area = c.area
+        self.purefc = c.purefc
+        self.outputfilelist = []
+
+        # Define the different types of field combinations (type, time, step)
+        self.types = {}
+        # Define the parameters and their level types, level list and
+        # grid resolution for the retrieval job
+        self.params = {}
+
+        if fluxes:
+            self._create_params_fluxes()
+        else:
+            self._create_params(c.gauss, c.eta, c.omega, c.cwc, c.wrf)
+
+        if fluxes:# and not c.purefc:
+            self._create_field_types_fluxes()
+        else:
+            self._create_field_types(c.type, c.time, c.step)
+        return
+
+    def _create_field_types(self, ftype, ftime, fstep):
+        '''Create the combination of field type, time and forecast step.
+
+        Parameters:
+        -----------
+        ftype : list of str
+            List of field types.
+
+        ftime : list of str
+            The time in hours of the field.
+
+        fstep : str
+            Specifies the forecast time step from forecast base time.
+            Valid values are hours (HH) from forecast base time.
+
+        Return
+        ------
+
+        '''
+        i = 0
+        for ty, st, ti in zip(ftype, fstep, ftime):
+            btlist = list(range(len(ftime)))
+            if self.basetime == 12:
+                btlist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+            if self.basetime == 0:
+                btlist = [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0]
+
+            # if ((ty.upper() == 'AN' and (int(c.time[i]) % int(c.dtime)) == 0) or
+                # (ty.upper() != 'AN' and (int(c.step[i]) % int(c.dtime)) == 0 and
+                 # (int(c.step[i]) % int(c.dtime) == 0)) ) and \
+                 # (int(c.time[i]) in btlist or c.purefc):
+
+            if (i in btlist) or self.purefc:
+
+                if ((ty.upper() == 'AN' and (int(ti) % int(self.dtime)) == 0) or
+                    (ty.upper() != 'AN' and (int(st) % int(self.dtime)) == 0)):
+
+                    if ty not in self.types.keys():
+                        self.types[ty] = {'times': '', 'steps': ''}
+
+                    if ti not in self.types[ty]['times']:
+                        if self.types[ty]['times']:
+                            self.types[ty]['times'] += '/'
+                        self.types[ty]['times'] += ti
+
+                    if st not in self.types[ty]['steps']:
+                        if self.types[ty]['steps']:
+                            self.types[ty]['steps'] += '/'
+                        self.types[ty]['steps'] += st
+            i += 1
+
+        return
+
+    def _create_field_types_fluxes(self):
+        '''Create the combination of field type, time and forecast step
+        for the flux data.
+
+        Parameters:
+        -----------
+
+        Return
+        ------
+
+        '''
+        self.types[str(self.acctype)] = {'times': str(self.acctime),
+                                         'steps': '{}/to/{}/by/{}'.format(
+                                             self.dtime,
+                                             self.accmaxstep,
+                                             self.dtime)}
+        return
+
+    def _create_params(self, gauss, eta, omega, cwc, wrf):
+        '''Define the specific parameter settings for retrievment.
+
+        The different parameters need specific grid types and level types
+        for retrievement. We might get following combination of types
+        (depending on selection and availability):
+        (These are short cuts for the grib file names (leading sequence)
+        SH__ML, OG__ML, GG__ML, SH__SL, OG__SL, GG__SL, OG_OROLSM_SL
+        where:
+            SH = Spherical Harmonics, GG = Gaussian Grid, OG = Output Grid,
+            ML = Model Level, SL = Surface Level
+
+        For each of this combination there is a list of parameter names,
+        the level type, the level list and the grid resolution.
+
+        There are different scenarios for data extraction from MARS:
+        1) Retrieval of etadot
+           eta=1, gauss=0, omega=0
+        2) Calculation of etadot from divergence
+           eta=0, gauss=1, omega=0
+        3) Calculation of etadot from omega (for makes sense for debugging)
+           eta=0, gauss=0, omega=1
+        4) Retrieval and Calculation of etadot (only for debugging)
+           eta=1, gauss=1, omega=0
+        5) Download also specific model and surface level data for FLEXPART-WRF
+
+        Parameters:
+        -----------
+        gauss : int
+            Gaussian grid is retrieved.
+
+        eta : int
+            Etadot parameter will be directly retrieved.
+
+        omega : int
+            The omega paramterwill be retrieved.
+
+        cwc : int
+            The cloud liquid and ice water content will be retrieved.
+
+        wrf : int
+            Additional model level and surface level data will be retrieved for
+            WRF/FLEXPART-WRF simulations.
+
+        Return
+        ------
+
+        '''
+        # SURFACE FIELDS
+        #-----------------------------------------------------------------------
+        self.params['SH__SL'] = ['LNSP', 'ML', '1', 'OFF']
+        self.params['OG__SL'] = ['SD/MSL/TCC/10U/10V/2T/2D/Z/LSM', \
+                                 'SFC', '1', self.grid]
+        if self.addpar:
+            self.params['OG__SL'][0] += self.addpar
+
+        if self.marsclass.upper() == 'EA' or self.marsclass.upper() == 'EP':
+            self.params['OG_OROLSM__SL'] = ["SDOR/CVL/CVH/FSR",
+                                            'SFC', '1', self.grid]
+        else:
+            self.params['OG_OROLSM__SL'] = ["SDOR/CVL/CVH/SR", \
+                                            'SFC', '1', self.grid]
+
+        # MODEL LEVEL FIELDS
+        #-----------------------------------------------------------------------
+        self.params['OG__ML'] = ['T/Q', 'ML', self.levelist, self.grid]
+
+        if not gauss and eta:
+            self.params['OG__ML'][0] += '/U/V/ETADOT'
+        elif gauss and not eta:
+            self.params['GG__SL'] = ['Q', 'ML', '1',
+                                     '{}'.format((int(self.resol) + 1) // 2)]
+            self.params['SH__ML'] = ['U/V/D', 'ML', self.glevelist, 'OFF']
+        elif not gauss and not eta:
+            self.params['OG__ML'][0] += '/U/V'
+        else:  # GAUSS and ETA
+            print('Warning: Collecting etadot and parameters for gaussian grid '
+                           'is a very costly parameter combination, '
+                           'use this combination only for debugging!')
+            self.params['GG__SL'] = ['Q', 'ML', '1',
+                                     '{}'.format((int(self.resol) + 1) // 2)]
+            self.params['GG__ML'] = ['U/V/D/ETADOT', 'ML', self.glevelist,
+                                     '{}'.format((int(self.resol) + 1) // 2)]
+
+        if omega:
+            self.params['OG__ML'][0] += '/W'
+
+        if cwc:
+            self.params['OG__ML'][0] += '/CLWC/CIWC'
+
+        # ADDITIONAL FIELDS FOR FLEXPART-WRF MODEL (IF QUESTIONED)
+        # -----------------------------------------------------------------------
+        if wrf:
+            self.params['OG__ML'][0] += '/Z/VO'
+            if '/D' not in self.params['OG__ML'][0]:
+                self.params['OG__ML'][0] += '/D'
+
+            wrf_sfc = ['SP','SKT','SST','CI','STL1','STL2', 'STL3','STL4',
+                       'SWVL1','SWVL2','SWVL3','SWVL4']
+            for par in wrf_sfc:
+                if par not in self.params['OG__SL'][0]:
+                    self.params['OG__SL'][0] += '/' + par
+
+        return
+
+
+    def _create_params_fluxes(self):
+        '''Define the parameter setting for flux data.
+
+        Flux data are accumulated fields in time and are stored on the
+        surface level. The leading short cut name for the grib files is:
+        "OG_acc_SL" with OG for Regular Output Grid, SL for Surface Level, and
+        acc for Accumulated Grid.
+        The params dictionary stores a list of parameter names, the level type,
+        the level list and the grid resolution.
+
+        The flux data are: LSP/CP/SSHF/EWSS/NSSS/SSR
+
+        Parameters:
+        -----------
+
+        Return
+        ------
+
+        '''
+        self.params['OG_acc_SL'] = ["LSP/CP/SSHF/EWSS/NSSS/SSR",
+                                    'SFC', '1', self.grid]
+        return
+
+
+    def _mk_targetname(self, ftype, param, date):
+        '''Creates the filename for the requested grib data to be stored in.
+        This name is passed as the "target" parameter in the request.
+
+        Parameters
+        ----------
+        ftype : str
+            Shortcut name of the type of the field. E.g. AN, FC, PF, ...
+
+        param : str
+            Shortcut of the grid type. E.g. SH__ML, SH__SL, GG__ML,
+            GG__SL, OG__ML, OG__SL, OG_OROLSM_SL, OG_acc_SL
+
+        date : str
+            The date period of the grib data to be stored in this file.
+
+        Return
+        ------
+        targetname : str
+            The target filename for the grib data.
+        '''
+        targetname = (self.inputdir + '/' + ftype + param + '.' + date + '.' +
+                      str(os.getppid()) + '.' + str(os.getpid()) + '.grb')
+
+        return targetname
+
+
+    def _start_retrievement(self, request, par_dict):
+        '''Creates the Mars Retrieval and prints or submits the request
+        depending on the status of the request variable.
+
+        Parameters
+        ----------
+        request : int
+            Selects the mode of retrieval.
+            0: Retrieves the data from ECMWF.
+            1: Prints the mars requests to an output file.
+            2: Retrieves the data and prints the mars request.
+
+        par_dict : dictionary
+            Contains all parameter which have to be set for creating the
+            Mars Retrievals. The parameter are:
+            marsclass, dataset, stream, type, levtype, levelist, resol,
+            gaussian, accuracy, grid, target, area, date, time, number,
+            step, expver, param
+
+        Return
+        ------
+
+        '''
+        # increase number of mars requests
+        self.mreq_count += 1
+
+        MR = MarsRetrieval(self.server,
+                           self.public,
+                           marsclass=par_dict['marsclass'],
+                           dataset=par_dict['dataset'],
+                           stream=par_dict['stream'],
+                           type=par_dict['type'],
+                           levtype=par_dict['levtype'],
+                           levelist=par_dict['levelist'],
+                           resol=par_dict['resol'],
+                           gaussian=par_dict['gaussian'],
+                           accuracy=par_dict['accuracy'],
+                           grid=par_dict['grid'],
+                           target=par_dict['target'],
+                           area=par_dict['area'],
+                           date=par_dict['date'],
+                           time=par_dict['time'],
+                           number=par_dict['number'],
+                           step=par_dict['step'],
+                           expver=par_dict['expver'],
+                           param=par_dict['param'])
+
+        if request == 0:
+            MR.display_info()
+            MR.data_retrieve()
+        elif request == 1:
+            MR.print_infodata_csv(self.inputdir, self.mreq_count)
+        elif request == 2:
+            MR.print_infodata_csv(self.inputdir, self.mreq_count)
+            MR.display_info()
+            MR.data_retrieve()
+        else:
+            print('Failure')
+
+        return
+
+
+    def _mk_index_values(self, inputdir, inputfiles, keys):
+        '''Creates an index file for a set of grib parameter keys.
+        The values from the index keys are returned in a list.
+
+        Parameters
+        ----------
+        keys : dictionary
+            List of parameter names which serves as index.
+
+        inputfiles : UioFiles
+            Contains a list of files.
+
+        Return
+        ------
+        iid : codes_index
+            This is a grib specific index structure to access
+            messages in a file.
+
+        index_vals : list of list  of str
+            Contains the values from the keys used for a distinct selection
+            of grib messages in processing  the grib files.
+            Content looks like e.g.:
+            index_vals[0]: ('20171106', '20171107', '20171108') ; date
+            index_vals[1]: ('0', '1200', '1800', '600') ; time
+            index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange
+        '''
+        from eccodes import codes_index_get
+
+        iid = None
+        index_keys = keys
+
+        indexfile = os.path.join(inputdir, _config.FILE_GRIB_INDEX)
+        silent_remove(indexfile)
+        grib = GribUtil(inputfiles.files)
+        # creates new index file
+        iid = grib.index(index_keys=index_keys, index_file=indexfile)
+
+        # read the values of index keys
+        index_vals = []
+        for key in index_keys:
+            key_vals = codes_index_get(iid, key)
+            # have to sort the key values for correct order,
+            # therefore convert to int first
+            key_vals = [int(k) for k in key_vals]
+            key_vals.sort()
+            key_vals = [str(k) for k in key_vals]
+            index_vals.append(key_vals)
+            # index_vals looks for example like:
+            # index_vals[0]: ('20171106', '20171107', '20171108') ; date
+            # index_vals[1]: ('0', '1200') ; time
+            # index_vals[2]: (3', '6', '9', '12') ; stepRange
+
+        return iid, index_vals
+
+
+    def retrieve(self, server, dates, public, request, inputdir='.'):
+        '''Finalizing the retrieval information by setting final details
+        depending on grid type.
+        Prepares MARS retrievals per grid type and submits them.
+
+        Parameters
+        ----------
+        server : ECMWFService or ECMWFDataServer
+            The connection to the ECMWF server. This is different
+            for member state users which have full access and non
+            member state users which have only access to the public
+            data sets. The decision is made from command line argument
+            "public"; for public access its True (ECMWFDataServer)
+            for member state users its False (ECMWFService)
+
+        dates : str
+            Contains start and end date of the retrieval in the format
+            "YYYYMMDD/to/YYYYMMDD"
+
+        request : int
+            Selects the mode of retrieval.
+            0: Retrieves the data from ECMWF.
+            1: Prints the mars requests to an output file.
+            2: Retrieves the data and prints the mars request.
+
+        inputdir : str, optional
+            Path to the directory where the retrieved data is about
+            to be stored. The default is the current directory ('.').
+
+        Return
+        ------
+
+        '''
+        self.dates = dates
+        self.server = server
+        self.public = public
+        self.inputdir = inputdir
+        oro = False
+
+        # define times with datetime module
+        t12h = timedelta(hours=12)
+        t24h = timedelta(hours=24)
+
+        # dictionary which contains all parameter for the mars request,
+        # entries with a "None" will change in different requests and will
+        # therefore be set in each request seperately
+        retr_param_dict = {'marsclass':self.marsclass,
+                           'dataset':self.dataset,
+                           'stream':None,
+                           'type':None,
+                           'levtype':None,
+                           'levelist':None,
+                           'resol':self.resol,
+                           'gaussian':None,
+                           'accuracy':self.accuracy,
+                           'grid':None,
+                           'target':None,
+                           'area':None,
+                           'date':None,
+                           'time':None,
+                           'number':self.number,
+                           'step':None,
+                           'expver':self.expver,
+                           'param':None}
+
+        for ftype in sorted(self.types):
+            # ftype contains field types such as
+            #     [AN, FC, PF, CV]
+            for pk, pv in sorted(self.params.items()):
+                # pk contains one of these keys of params
+                #     [SH__ML, SH__SL, GG__ML, GG__SL, OG__ML, OG__SL,
+                #      OG_OROLSM_SL, OG_acc_SL]
+                # pv contains all of the items of the belonging key
+                #     [param, levtype, levelist, grid]
+                if isinstance(pv, str):
+                    continue
+                retr_param_dict['type'] = ftype
+                retr_param_dict['time'] = self.types[ftype]['times']
+                retr_param_dict['step'] = self.types[ftype]['steps']
+                retr_param_dict['date'] = self.dates
+                retr_param_dict['stream'] = self.stream
+                retr_param_dict['target'] = \
+                    self._mk_targetname(ftype,
+                                        pk,
+                                        retr_param_dict['date'].split('/')[0])
+                table128 = init128(_config.PATH_GRIBTABLE)
+                ids = to_param_id_with_tablenumber(pv[0], table128)
+                retr_param_dict['param'] = ids
+                retr_param_dict['levtype'] = pv[1]
+                retr_param_dict['levelist'] = pv[2]
+                retr_param_dict['grid'] = pv[3]
+                retr_param_dict['area'] = self.area
+                retr_param_dict['gaussian'] = self.gaussian
+
+                if pk == 'OG_OROLSM__SL' and not oro:
+                    oro = True
+                    # in CERA20C (class EP) there is no stream "OPER"!
+                    if self.marsclass.upper() != 'EP':
+                        retr_param_dict['stream'] = 'OPER'
+                    retr_param_dict['type'] = 'AN'
+                    retr_param_dict['time'] = '00'
+                    retr_param_dict['step'] = '000'
+                    retr_param_dict['date'] = self.dates.split('/')[0]
+                    retr_param_dict['target'] = self._mk_targetname('',
+                                            pk, retr_param_dict['date'])
+                elif pk == 'OG_OROLSM__SL' and oro:
+                    continue
+                if pk == 'GG__SL' and pv[0] == 'Q':
+                    retr_param_dict['area'] = ""
+                    retr_param_dict['gaussian'] = 'reduced'
+                if ftype.upper() == 'FC' and \
+                    'acc' not in retr_param_dict['target']:
+                    if (int(retr_param_dict['time'][0]) +
+                        int(retr_param_dict['step'][0])) > 23:
+                        dates = retr_param_dict['date'].split('/')
+                        sdate = datetime.strptime(dates[0], '%Y%m%d%H')
+                        sdate = sdate - timedelta(days=1)
+                        retr_param_dict['date'] = '/'.join(
+                            [sdate.strftime("%Y%m%d")] +
+                            retr_param_dict['date'][1:])
+
+                        print('CHANGED FC start date to ' +
+                              sdate.strftime("%Y%m%d") +
+                              ' to accomodate TIME=' +
+                              retr_param_dict['time'][0] +
+                              ', STEP=' +
+                              retr_param_dict['time'][0])
+
+    # ------  on demand path  --------------------------------------------------
+                if self.basetime is None:
+                    # ******* start retrievement
+                    self._start_retrievement(request, retr_param_dict)
+    # ------  operational path  ------------------------------------------------
+                else:
+                    # check if mars job requests fields beyond basetime.
+                    # if yes eliminate those fields since they may not
+                    # be accessible with user's credentials
+
+                    enddate = retr_param_dict['date'].split('/')[-1]
+                    elimit = datetime.strptime(enddate + str(self.basetime),
+                                               '%Y%m%d%H')
+
+                    if self.basetime == 12:
+                        # --------------  flux data ----------------------------
+                        if 'acc' in pk:
+                            startdate = retr_param_dict['date'].split('/')[0]
+                            enddate = datetime.strftime(elimit - t24h,'%Y%m%d')
+                            retr_param_dict['date'] = '/'.join([startdate,
+                                                                'to',
+                                                                enddate])
+
+                            # ******* start retrievement
+                            self._start_retrievement(request, retr_param_dict)
+
+                            retr_param_dict['date'] = \
+                                datetime.strftime(elimit - t12h, '%Y%m%d')
+                            retr_param_dict['time'] = '00'
+                            retr_param_dict['target'] = \
+                                self._mk_targetname(ftype, pk,
+                                                    retr_param_dict['date'])
+
+                            # ******* start retrievement
+                            self._start_retrievement(request, retr_param_dict)
+
+                        # --------------  non flux data ------------------------
+                        else:
+                            # ******* start retrievement
+                            self._start_retrievement(request, retr_param_dict)
+
+                    elif self.basetime == 0:
+                        retr_param_dict['date'] = \
+                            datetime.strftime(elimit - t24h, '%Y%m%d')
+
+                        timesave = ''.join(retr_param_dict['time'])
+
+                        if ('/' in retr_param_dict['time'] and
+                            pk != 'OG_OROLSM__SL' and
+                            'acc' not in pk ) :
+                            times = retr_param_dict['time'].split('/')
+                            steps = retr_param_dict['step'].split('/')
+
+                            while int(times[0]) + int(steps[0]) <= 12:
+                                times = times[1:]
+                                if len(times) > 1:
+                                    retr_param_dict['time'] = '/'.join(times)
+                                else:
+                                    retr_param_dict['time'] = times[0]
+
+                        if (pk != 'OG_OROLSM__SL' and
+                            int(retr_param_dict['step'].split('/')[0]) == 0 and
+                            int(timesave.split('/')[0]) == 0):
+
+                            retr_param_dict['date'] = \
+                                datetime.strftime(elimit, '%Y%m%d')
+                            retr_param_dict['time'] = '00'
+                            retr_param_dict['step'] = '000'
+                            retr_param_dict['target'] = \
+                                self._mk_targetname(ftype, pk,
+                                                    retr_param_dict['date'])
+
+                        # ******* start retrievement
+                        self._start_retrievement(request, retr_param_dict)
+                    else:
+                        raise ValueError('ERROR: Basetime has an invalid value '
+                                                 '-> {}'.format(str(basetime)))
+
+        if request == 0 or request == 2:
+            print('MARS retrieve done ... ')
+        elif request == 1:
+            print('MARS request printed ...')
+
+        return
+
+
+    def write_namelist(self, c):
+        '''Creates a namelist file in the temporary directory and writes
+        the following values to it: maxl, maxb, mlevel,
+        mlevelist, mnauf, metapar, rlo0, rlo1, rla0, rla1,
+        momega, momegadiff, mgauss, msmooth, meta, metadiff, mdpdeta
+
+        Parameters
+        ----------
+        c : ControlFile
+            Contains all the parameters of CONTROL file and
+            command line.
+
+        filename : str
+                Name of the namelist file.
+
+        Return
+        ------
+
+        '''
+
+        from genshi.template.text import NewTextTemplate
+        from genshi.template import  TemplateLoader
+        from genshi.template.eval import  UndefinedError
+        import numpy as np
+
+        try:
+            loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
+            namelist_template = loader.load(_config.TEMPFILE_NAMELIST,
+                                            cls=NewTextTemplate)
+
+            self.inputdir = c.inputdir
+            area = np.asarray(self.area.split('/')).astype(float)
+            grid = np.asarray(self.grid.split('/')).astype(float)
+
+            if area[1] > area[3]:
+                area[1] -= 360
+            maxl = round((area[3] - area[1]) / grid[1]) + 1
+            maxb = round((area[0] - area[2]) / grid[0]) + 1
+
+            stream = namelist_template.generate(
+                maxl = str(maxl),
+                maxb = str(maxb),
+                mlevel = str(self.level),
+                mlevelist = str(self.levelist),
+                mnauf = str(self.resol),
+                metapar = '77',
+                rlo0 = str(area[1]),
+                rlo1 = str(area[3]),
+                rla0 = str(area[2]),
+                rla1 = str(area[0]),
+                momega = str(c.omega),
+                momegadiff = str(c.omegadiff),
+                mgauss = str(c.gauss),
+                msmooth = str(c.smooth),
+                meta = str(c.eta),
+                metadiff = str(c.etadiff),
+                mdpdeta = str(c.dpdeta)
+            )
+        except UndefinedError as e:
+            print('... ERROR ' + str(e))
+
+            sys.exit('\n... error occured while trying to generate namelist ' +
+                     _config.TEMPFILE_NAMELIST)
+        except OSError as e:
+            print('... ERROR CODE: ' + str(e.errno))
+            print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+            sys.exit('\n... error occured while trying to generate template ' +
+                     _config.TEMPFILE_NAMELIST)
+
+        try:
+            namelistfile = os.path.join(self.inputdir, _config.FILE_NAMELIST)
+
+            with open(namelistfile, 'w') as f:
+                f.write(stream.render('text'))
+        except OSError as e:
+            print('... ERROR CODE: ' + str(e.errno))
+            print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+            sys.exit('\n... error occured while trying to write ' +
+                     namelistfile)
+
+        return
+
+
+    def deacc_fluxes(self, inputfiles, c):
+        '''De-accumulate and disaggregate flux data.
+
+        Goes through all flux fields in ordered time and de-accumulate
+        the fields. Afterwards the fields are disaggregated in time.
+        Different versions of disaggregation is provided for rainfall
+        data (darain, modified linear) and the surface fluxes and
+        stress data (dapoly, cubic polynomial).
+
+        Parameters
+        ----------
+        inputfiles : UioFiles
+            Contains the list of files that contain flux data.
+
+        c : ControlFile
+            Contains all the parameters of CONTROL file and
+            command line.
+
+        Return
+        ------
+
+        '''
+        import numpy as np
+        from eccodes import (codes_index_select, codes_new_from_index, codes_get,
+                             codes_get_values, codes_set_values, codes_set,
+                             codes_write, codes_release, codes_new_from_index,
+                             codes_index_release)
+
+        table128 = init128(_config.PATH_GRIBTABLE)
+        # get ids from the flux parameter names
+        pars = to_param_id(self.params['OG_acc_SL'][0], table128)
+
+        iid = None
+        index_vals = None
+
+        # get the values of the keys which are used for distinct access
+        # of grib messages via product and save the maximum number of
+        # ensemble members if there is more than one
+        if '/' in self.number:
+            # more than one ensemble member is selected
+            index_keys = ["number", "date", "time", "step"]
+            # maximum ensemble number retrieved
+            # + 1 for the control run (ensemble number 0)
+            maxnum = int(self.number.split('/')[-1]) + 1
+            # remember the index of the number values
+            index_number = index_keys.index('number')
+            # empty set to save ensemble numbers which were already processed
+            ens_numbers = set()
+            # index for the ensemble number
+            inumb = 0
+        else:
+            index_keys = ["date", "time", "step"]
+            # maximum ensemble number
+            maxnum = None
+
+        # get sorted lists of the index values
+        # this is very important for disaggregating
+        # the flux data in correct order
+        iid, index_vals = self._mk_index_values(c.inputdir,
+                                                inputfiles,
+                                                index_keys)
+        # index_vals looks like e.g.:
+        # index_vals[0]: ('20171106', '20171107', '20171108') ; date
+        # index_vals[1]: ('0', '600', '1200', '1800') ; time
+        # index_vals[2]: ('0', '3', '6', '9', '12') ; stepRange
+
+        if c.rrint:
+            # set start and end timestamps for retrieval period
+            if not c.purefc:
+                start_date = datetime.strptime(c.start_date + '00', '%Y%m%d%H')
+                end_date = datetime.strptime(c.end_date + '23', '%Y%m%d%H')
+            else:
+                sdate_str = c.start_date + '{:0>2}'.format(index_vals[1][0])
+                start_date = datetime.strptime(sdate_str, '%Y%m%d%H')
+                edate_str = c.end_date + '{:0>2}'.format(index_vals[1][-1])
+                end_date = datetime.strptime(edate_str, '%Y%m%d%H')
+                end_date = end_date + timedelta(hours=c.maxstep)
+
+            # get necessary grid dimensions from grib files for storing the
+            # precipitation fields
+            info = get_informations(os.path.join(c.inputdir,
+                                                 inputfiles.files[0]))
+            dims = get_dimensions(info, c.purefc, c.dtime, index_vals,
+                                  start_date, end_date)
+
+            # create empty numpy arrays
+            if not maxnum:
+                lsp_np = np.zeros((dims[1] * dims[0], dims[2]), dtype=np.float64)
+                cp_np = np.zeros((dims[1] * dims[0], dims[2]), dtype=np.float64)
+            else:
+                lsp_np = np.zeros((maxnum, dims[1] * dims[0], dims[2]), dtype=np.float64)
+                cp_np = np.zeros((maxnum, dims[1] * dims[0], dims[2]), dtype=np.float64)
+
+            # index counter for time line
+            it_lsp = 0
+            it_cp = 0
+
+            # store the order of date and step
+            date_list = []
+            step_list = []
+
+        # initialize dictionaries to store flux values per parameter
+        orig_vals = {}
+        deac_vals = {}
+        for p in pars:
+            orig_vals[p] = []
+            deac_vals[p] = []
+
+        # "product" genereates each possible combination between the
+        # values of the index keys
+        for prod in product(*index_vals):
+            # e.g. prod = ('20170505', '0', '12')
+            #             ( date     ,time, step)
+
+            print('CURRENT PRODUCT: ', prod)
+
+            # the whole process has to be done for each seperate ensemble member
+            # therefore, for each new ensemble member we delete old flux values
+            # and start collecting flux data from the beginning time step
+            if maxnum and prod[index_number] not in ens_numbers:
+                ens_numbers.add(prod[index_number])
+                inumb = len(ens_numbers) - 1
+                # re-initialize dictionaries to store flux values per parameter
+                # for the next ensemble member
+                it_lsp = 0
+                it_cp = 0
+                orig_vals = {}
+                deac_vals = {}
+                for p in pars:
+                    orig_vals[p] = []
+                    deac_vals[p] = []
+
+            for i in range(len(index_keys)):
+                codes_index_select(iid, index_keys[i], prod[i])
+
+            # get first id from current product
+            gid = codes_new_from_index(iid)
+
+            # if there is no data for this specific time combination / product
+            # skip the rest of the for loop and start with next timestep/product
+            if not gid:
+                continue
+
+            # create correct timestamp from the three time informations
+            cdate = str(codes_get(gid, 'date'))
+            time = codes_get(gid, 'time') // 100  # integer
+            step = codes_get(gid, 'step') # integer
+            ctime = '{:0>2}'.format(time)
+            cstep = '{:0>3}'.format(step)
+
+            t_date = datetime.strptime(cdate + ctime, '%Y%m%d%H')
+            t_dt = t_date + timedelta(hours=step)
+            t_m1dt = t_date + timedelta(hours=step-int(c.dtime))
+            t_m2dt = t_date + timedelta(hours=step-2*int(c.dtime))
+            if c.basetime is not None:
+                t_enddate = datetime.strptime(c.end_date + str(c.basetime),
+                                              '%Y%m%d%H')
+            else:
+                t_enddate = t_date + timedelta(2*int(c.dtime))
+
+            # if necessary, add ensemble member number to filename suffix
+            # otherwise, add empty string
+            if maxnum:
+                numbersuffix = '.N{:0>3}'.format(int(prod[index_number]))
+            else:
+                numbersuffix = ''
+
+            if c.purefc:
+                fnout = os.path.join(c.inputdir, 'flux' +
+                                     t_date.strftime('%Y%m%d.%H') +
+                                     '.{:0>3}'.format(step-2*int(c.dtime)) +
+                                     numbersuffix)
+                gnout = os.path.join(c.inputdir, 'flux' +
+                                     t_date.strftime('%Y%m%d.%H') +
+                                     '.{:0>3}'.format(step-int(c.dtime)) +
+                                     numbersuffix)
+                hnout = os.path.join(c.inputdir, 'flux' +
+                                     t_date.strftime('%Y%m%d.%H') +
+                                     '.{:0>3}'.format(step) +
+                                     numbersuffix)
+            else:
+                fnout = os.path.join(c.inputdir, 'flux' +
+                                     t_m2dt.strftime('%Y%m%d%H') + numbersuffix)
+                gnout = os.path.join(c.inputdir, 'flux' +
+                                     t_m1dt.strftime('%Y%m%d%H') + numbersuffix)
+                hnout = os.path.join(c.inputdir, 'flux' +
+                                     t_dt.strftime('%Y%m%d%H') + numbersuffix)
+
+            print("outputfile = " + fnout)
+            f_handle = open(fnout, 'w')
+            h_handle = open(hnout, 'w')
+            g_handle = open(gnout, 'w')
+
+            # read message for message and store relevant data fields, where
+            # data keywords are stored in pars
+            while True:
+                if not gid:
+                    break
+                parId = codes_get(gid, 'paramId') # integer
+                step = codes_get(gid, 'step') # integer
+                time = codes_get(gid, 'time') # integer
+                ni = codes_get(gid, 'Ni') # integer
+                nj = codes_get(gid, 'Nj') # integer
+                if parId not in orig_vals.keys():
+                    # parameter is not a flux, find next one
+                    continue
+
+                # define conversion factor
+                if parId == 142 or parId == 143:
+                    fak = 1. / 1000.
+                else:
+                    fak = 3600.
+
+                # get parameter values and reshape
+                values = codes_get_values(gid)
+                values = (np.reshape(values, (nj, ni))).flatten() / fak
+
+                # save the original and accumulated values
+                orig_vals[parId].append(values[:])
+
+                if c.marsclass.upper() == 'EA' or step <= int(c.dtime):
+                    # no de-accumulation needed
+                    deac_vals[parId].append(values[:] / int(c.dtime))
+                else:
+                    # do de-accumulation
+                    deac_vals[parId].append(
+                        (orig_vals[parId][-1] - orig_vals[parId][-2]) /
+                         int(c.dtime))
+
+                # store precipitation if new disaggregation method is selected
+                # only the exact days are needed
+                if c.rrint:
+                    if start_date <= t_dt <= end_date:
+                        if not c.purefc:
+                            if t_dt not in date_list:
+                                date_list.append(t_dt)
+                                step_list = [0]
+                        else:
+                            if t_date not in date_list:
+                                date_list.append(t_date)
+                            if step not in step_list:
+                                step_list.append(step)
+                        # store precipitation values
+                        if maxnum and parId == 142:
+                            lsp_np[inumb, :, it_lsp] = deac_vals[parId][-1][:]
+                            it_lsp += 1
+                        elif not maxnum and parId == 142:
+                            lsp_np[:, it_lsp] = deac_vals[parId][-1][:]
+                            it_lsp += 1
+                        elif maxnum and parId == 143:
+                            cp_np[inumb, :, it_cp] = deac_vals[parId][-1][:]
+                            it_cp += 1
+                        elif not maxnum and parId == 143:
+                            cp_np[:, it_cp] = deac_vals[parId][-1][:]
+                            it_cp += 1
+
+                # information printout
+                print(parId, time, step, len(values), values[0], np.std(values))
+
+                # length of deac_vals[parId] corresponds to the
+                # number of time steps, max. 4 are needed for disaggegration
+                # with the old and original method
+                # run over all grib messages and perform
+                # shifting in time
+                if len(deac_vals[parId]) >= 3:
+                    if len(deac_vals[parId]) > 3:
+                        if not c.rrint and (parId == 142 or parId == 143):
+                            values = disaggregation.darain(deac_vals[parId])
+                        else:
+                            values = disaggregation.dapoly(deac_vals[parId])
+
+                        if not (step == c.maxstep and c.purefc \
+                                or t_dt == t_enddate):
+                            # remove first time step in list to shift
+                            # time line
+                            orig_vals[parId].pop(0)
+                            deac_vals[parId].pop(0)
+                    else:
+                        # if the third time step is read (per parId),
+                        # write out the first one as a boundary value
+                        if c.purefc:
+                            values = deac_vals[parId][1]
+                        else:
+                            values = deac_vals[parId][0]
+
+                    if not (c.rrint and (parId == 142 or parId == 143)):
+                        codes_set_values(gid, values)
+
+                        if c.purefc:
+                            codes_set(gid, 'stepRange', max(0, step-2*int(c.dtime)))
+                        else:
+                            codes_set(gid, 'stepRange', 0)
+                            codes_set(gid, 'time', t_m2dt.hour*100)
+                            codes_set(gid, 'date', int(t_m2dt.strftime('%Y%m%d')))
+
+                        codes_write(gid, f_handle)
+
+                        # squeeze out information of last two steps
+                        # contained in deac_vals[parId]
+                        # Note that deac_vals[parId][0] has not been popped
+                        # in this case
+
+                        if step == c.maxstep and c.purefc or \
+                           t_dt == t_enddate:
+                            # last step
+                            if c.purefc:
+                                values = deac_vals[parId][3]
+                                codes_set_values(gid, values)
+                                codes_set(gid, 'stepRange', step)
+                                #truedatetime = t_m2dt + timedelta(hours=2*int(c.dtime))
+                                codes_write(gid, h_handle)
+                            else:
+                                values = deac_vals[parId][3]
+                                codes_set_values(gid, values)
+                                codes_set(gid, 'stepRange', 0)
+                                truedatetime = t_m2dt + timedelta(hours=2*int(c.dtime))
+                                codes_set(gid, 'time', truedatetime.hour * 100)
+                                codes_set(gid, 'date', int(truedatetime.strftime('%Y%m%d')))
+                                codes_write(gid, h_handle)
+
+                            if parId == 142 or parId == 143:
+                                values = disaggregation.darain(list(reversed(deac_vals[parId])))
+                            else:
+                                values = disaggregation.dapoly(list(reversed(deac_vals[parId])))
+
+                            # step before last step
+                            if c.purefc:
+                                codes_set(gid, 'stepRange', step-int(c.dtime))
+                                #truedatetime = t_m2dt + timedelta(hours=int(c.dtime))
+                                codes_set_values(gid, values)
+                                codes_write(gid, g_handle)
+                            else:
+                                codes_set(gid, 'stepRange', 0)
+                                truedatetime = t_m2dt + timedelta(hours=int(c.dtime))
+                                codes_set(gid, 'time', truedatetime.hour * 100)
+                                codes_set(gid, 'date', int(truedatetime.strftime('%Y%m%d')))
+                                codes_set_values(gid, values)
+                                codes_write(gid, g_handle)
+
+                codes_release(gid)
+
+                gid = codes_new_from_index(iid)
+
+            f_handle.close()
+            g_handle.close()
+            h_handle.close()
+
+        codes_index_release(iid)
+
+        if c.rrint:
+            self._create_rr_grib_dummy(inputfiles.files[0], c.inputdir)
+
+            self._prep_new_rrint(dims[0], dims[1], dims[2], lsp_np,
+                                 cp_np, maxnum, index_keys, index_vals, c)
+
+        return
+
+    def _prep_new_rrint(self, ni, nj, nt, lsp_np, cp_np, maxnum, index_keys, index_vals, c):
+        '''Calculates and writes out the disaggregated precipitation fields.
+
+        Disaggregation is done in time and original times are written to the
+        flux files, while the additional subgrid times are written to
+        extra files output files. They are named like the original files with
+        suffixes "_1" and "_2" for the first and second subgrid point.
+
+        Parameters
+        ----------
+        ni : int
+            Amount of zonal grid points.
+
+        nj : int
+            Amount of meridional grid points.
+
+        nt : int
+            Number of time steps.
+
+        lsp_np : numpy array of float
+            The large scale precipitation fields for each time step.
+            Shape (ni * nj, nt).
+
+        cp_np : numpy array of float
+            The convective precipitation fields for each time step.
+            Shape (ni * nj, nt).
+
+        maxnum : int
+            The maximum number of ensemble members. It is None
+            if there are no or just one ensemble.
+
+        index_keys : dictionary
+            List of parameter names which serves as index.
+
+        index_vals : list of list  of str
+            Contains the values from the keys used for a distinct selection
+            of grib messages in processing  the grib files.
+            Content looks like e.g.:
+            index_vals[0]: ('20171106', '20171107', '20171108') ; date
+            index_vals[1]: ('0', '1200', '1800', '600') ; time
+            index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange
+
+        c : ControlFile
+            Contains all the parameters of CONTROL file and
+            command line.
+
+        Return
+        ------
+
+        '''
+        import numpy as np
+
+        print('... disaggregation of precipitation with new method.')
+
+        tmpfile = os.path.join(c.inputdir, 'rr_grib_dummy.grb')
+
+        # initialize new numpy arrays for disaggregated fields
+        if maxnum:
+            lsp_new_np = np.zeros((maxnum, ni * nj, nt * 3), dtype=np.float64)
+            cp_new_np = np.zeros((maxnum, ni * nj, nt * 3), dtype=np.float64)
+        else:
+            lsp_new_np = np.zeros((1, ni * nj, nt * 3), dtype=np.float64)
+            cp_new_np = np.zeros((1, ni * nj, nt * 3), dtype=np.float64)
+
+        # do the disaggregation, but neglect the last value of the
+        # original time series. This one corresponds for example to
+        # 24 hour, which we don't need. we use 0 - 23 UTC for a day.
+        if maxnum:
+            for inum in range(maxnum):
+                for ix in range(ni*nj):
+                    lsp_new_np[inum,ix,:] = disaggregation.IA3(lsp_np[inum,ix,:])[:-1]
+                    cp_new_np[inum,ix,:] = disaggregation.IA3(cp_np[inum,ix,:])[:-1]
+        else:
+            for ix in range(ni*nj):
+                lsp_new_np[0,ix,:] = disaggregation.IA3(lsp_np[ix,:])[:-1]
+                cp_new_np[0,ix,:] = disaggregation.IA3(cp_np[ix,:])[:-1]
+
+        # write to grib files (full/orig times to flux file and inbetween
+        # times with step 1 and 2, respectively)
+        print('... write disaggregated precipitation to files.')
+
+        if maxnum:
+            # remember the index of the number values
+            index_number = index_keys.index('number')
+            # empty set to save unique ensemble numbers which were already processed
+            ens_numbers = set()
+            # index for the ensemble number
+            inumb = 0
+        else:
+            inumb = 0
+
+        # index variable of disaggregated fields
+        it = 0
+
+        # "product" genereates each possible combination between the
+        # values of the index keys
+        for prod in product(*index_vals):
+            # e.g. prod = ('20170505', '0', '12')
+            #             ( date     ,time, step)
+            # or   prod = ('0'   , '20170505', '0', '12')
+            #             (number, date      ,time, step)
+
+            cdate = prod[index_keys.index('date')]
+            ctime = '{:0>2}'.format(int(prod[index_keys.index('time')])//100)
+            cstep = '{:0>3}'.format(int(prod[index_keys.index('step')]))
+
+            date = datetime.strptime(cdate + ctime, '%Y%m%d%H')
+            date += timedelta(hours=int(cstep))
+
+            start_period, end_period = generate_retrieval_period_boundary(c)
+            # skip all temporary times
+            # which are outside the retrieval period
+            if date < start_period or \
+               date > end_period:
+                continue
+
+            # the whole process has to be done for each seperate ensemble member
+            # therefore, for each new ensemble member we delete old flux values
+            # and start collecting flux data from the beginning time step
+            if maxnum and prod[index_number] not in ens_numbers:
+                ens_numbers.add(prod[index_number])
+                inumb = int(prod[index_number])
+                it = 0
+
+            # if necessary, add ensemble member number to filename suffix
+            # otherwise, add empty string
+            if maxnum:
+                numbersuffix = '.N{:0>3}'.format(int(prod[index_number]))
+            else:
+                numbersuffix = ''
+
+            # per original time stamp: write original time step and
+            # the two newly generated sub time steps
+            if c.purefc:
+                fluxfilename = 'flux' + date.strftime('%Y%m%d.%H') + '.' + cstep
+            else:
+                fluxfilename = 'flux' + date.strftime('%Y%m%d%H') + numbersuffix
+
+            # write original time step to flux file as usual
+            fluxfile = GribUtil(os.path.join(c.inputdir, fluxfilename))
+            fluxfile.set_keys(tmpfile, filemode='a', strict=True,
+                              wherekeynames=['paramId'], wherekeyvalues=[142],
+                              keynames=['perturbationNumber','date','time','stepRange','values'],
+                              keyvalues=[inumb, int(date.strftime('%Y%m%d')),
+                                         date.hour*100, 0, lsp_new_np[inumb,:,it]],
+                             )
+            fluxfile.set_keys(tmpfile, filemode='a', strict=True,
+                              wherekeynames=['paramId'], wherekeyvalues=[143],
+                              keynames=['perturbationNumber','date','time','stepRange','values'],
+                              keyvalues=[inumb,int(date.strftime('%Y%m%d')),
+                                         date.hour*100, 0, cp_new_np[inumb,:,it]]
+                             )
+
+            # rr for first subgrid point is identified by step = 1
+            fluxfile.set_keys(tmpfile, filemode='a', strict=True,
+                              wherekeynames=['paramId'], wherekeyvalues=[142],
+                              keynames=['perturbationNumber','date','time','stepRange','values'],
+                              keyvalues=[inumb,int(date.strftime('%Y%m%d')),
+                                         date.hour*100, '1', lsp_new_np[inumb,:,it+1]]
+                              )
+            fluxfile.set_keys(tmpfile, filemode='a', strict=True,
+                              wherekeynames=['paramId'], wherekeyvalues=[143],
+                              keynames=['perturbationNumber','date','time','stepRange','values'],
+                              keyvalues=[inumb,int(date.strftime('%Y%m%d')),
+                                         date.hour*100, '1', cp_new_np[inumb,:,it+1]]
+                              )
+
+            # rr for second subgrid point is identified by step = 2
+            fluxfile.set_keys(tmpfile, filemode='a', strict=True,
+                              wherekeynames=['paramId'], wherekeyvalues=[142],
+                              keynames=['perturbationNumber','date','time','stepRange','values'],
+                              keyvalues=[inumb,int(date.strftime('%Y%m%d')),
+                                         date.hour*100, '2', lsp_new_np[inumb,:,it+2]]
+                              )
+            fluxfile.set_keys(tmpfile, filemode='a', strict=True,
+                              wherekeynames=['paramId'], wherekeyvalues=[143],
+                              keynames=['perturbationNumber','date','time','stepRange','values'],
+                              keyvalues=[inumb,int(date.strftime('%Y%m%d')),
+                                         date.hour*100, '2', cp_new_np[inumb,:,it+2]]
+                              )
+
+            it = it + 3 # jump to next original time step in rr fields
+        return
+
+    def _create_rr_grib_dummy(self, ifile, inputdir):
+        '''Creates a grib file with a dummy message for the two precipitation
+        types lsp and cp each.
+
+        Parameters
+        ----------
+        ifile : str
+            Filename of the input file to read the grib messages from.
+
+        inputdir : str, optional
+            Path to the directory where the retrieved data is stored.
+
+        Return
+        ------
+
+        '''
+
+        gribfile = GribUtil(os.path.join(inputdir,'rr_grib_dummy.grb'))
+
+        gribfile.copy_dummy_msg(ifile, keynames=['paramId'],
+                      keyvalues=[142], filemode='w')
+
+        gribfile.copy_dummy_msg(ifile, keynames=['paramId'],
+                      keyvalues=[143], filemode='a')
+
+        return
+
+    def create(self, inputfiles, c):
+        '''An index file will be created which depends on the combination
+        of "date", "time" and "stepRange" values. This is used to iterate
+        over all messages in each grib file which were passed through the
+        parameter "inputfiles" to seperate specific parameters into fort.*
+        files. Afterwards the FORTRAN program is called to convert
+        the data fields all to the same grid and put them in one file
+        per unique time step (combination of "date", "time" and
+        "stepRange").
+
+        Note
+        ----
+        This method is based on the ECMWF example index.py
+        https://software.ecmwf.int/wiki/display/GRIB/index.py
+
+        Parameters
+        ----------
+        inputfiles : UioFiles
+            Contains a list of files.
+
+        c : ControlFile
+            Contains all the parameters of CONTROL file and
+            command line.
+
+        Return
+        ------
+
+        '''
+        from eccodes import (codes_index_select, codes_new_from_index, codes_get,
+                             codes_get_values, codes_set_values, codes_set,
+                             codes_write, codes_release, codes_new_from_index,
+                             codes_index_release)
+
+        # generate start and end timestamp of the retrieval period
+        start_period = datetime.strptime(c.start_date + c.time[0], '%Y%m%d%H')
+        start_period = start_period + timedelta(hours=int(c.step[0]))
+        end_period = datetime.strptime(c.end_date + c.time[-1], '%Y%m%d%H')
+        end_period = end_period + timedelta(hours=int(c.step[-1]))
+
+        if c.wrf:
+            table128 = init128(_config.PATH_GRIBTABLE)
+            wrfpars = to_param_id('sp/mslp/skt/2t/10u/10v/2d/z/lsm/sst/ci/sd/\
+                                   stl1/stl2/stl3/stl4/swvl1/swvl2/swvl3/swvl4',
+                                  table128)
+
+        # these numbers are indices for the temporary files "fort.xx"
+        # which are used to seperate the grib fields to,
+        # for the Fortran program input
+        # 10: U,V | 11: T | 12: lnsp | 13: D | 16: sfc fields
+        # 17: Q | 18: Q, SL, GG| 19: omega | 21: etadot | 22: clwc+ciwc
+        fdict = {'10':None, '11':None, '12':None, '13':None, '16':None,
+                 '17':None, '18':None, '19':None, '21':None, '22':None}
+
+        iid = None
+        index_vals = None
+
+        # get the values of the keys which are used for distinct access
+        # of grib messages via product
+        if '/' in self.number:
+            # more than one ensemble member is selected
+            index_keys = ["number", "date", "time", "step"]
+        else:
+            index_keys = ["date", "time", "step"]
+        iid, index_vals = self._mk_index_values(c.inputdir,
+                                                inputfiles,
+                                                index_keys)
+        # index_vals looks like e.g.:
+        # index_vals[0]: ('20171106', '20171107', '20171108') ; date
+        # index_vals[1]: ('0', '600', '1200', '1800') ; time
+        # index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange
+
+        # "product" genereates each possible combination between the
+        # values of the index keys
+        for prod in product(*index_vals):
+            # e.g. prod = ('20170505', '0', '12')
+            #             (  date    ,time, step)
+
+            print('current product: ', prod)
+
+            for i in range(len(index_keys)):
+                codes_index_select(iid, index_keys[i], prod[i])
+
+            # get first id from current product
+            gid = codes_new_from_index(iid)
+
+            # if there is no data for this specific time combination / product
+            # skip the rest of the for loop and start with next timestep/product
+            if not gid:
+                continue
+#============================================================================================
+            # remove old fort.* files and open new ones
+            # they are just valid for a single product
+            for k, f in fdict.items():
+                fortfile = os.path.join(c.inputdir, 'fort.' + k)
+                silent_remove(fortfile)
+                fdict[k] = open(fortfile, 'w')
+#============================================================================================
+            # create correct timestamp from the three time informations
+            cdate = str(codes_get(gid, 'date'))
+            ctime = '{:0>2}'.format(codes_get(gid, 'time') // 100)
+            cstep = '{:0>3}'.format(codes_get(gid, 'step'))
+            timestamp = datetime.strptime(cdate + ctime, '%Y%m%d%H')
+            timestamp += timedelta(hours=int(cstep))
+            cdate_hour = datetime.strftime(timestamp, '%Y%m%d%H')
+
+            # skip all temporary times
+            # which are outside the retrieval period
+            if timestamp < start_period or \
+               timestamp > end_period:
+                continue
+
+            # if the timestamp is out of basetime start/end date period,
+            # skip this specific product
+            if c.basetime is not None:
+                time_delta = timedelta(hours=12-int(c.dtime))
+                start_time = datetime.strptime(c.end_date + str(c.basetime),
+                                                '%Y%m%d%H') - time_delta
+                end_time = datetime.strptime(c.end_date + str(c.basetime),
+                                             '%Y%m%d%H')
+                if timestamp < start_time or timestamp > end_time:
+                    continue
+
+            if c.wrf:
+                if 'olddate' not in locals() or cdate != olddate:
+                    fwrf = open(os.path.join(c.outputdir,
+                                'WRF' + cdate + '.' + ctime + '.000.grb2'), 'w')
+                    olddate = cdate[:]
+#============================================================================================
+            # savedfields remembers which fields were already used.
+            savedfields = []
+            # sum of cloud liquid and ice water content
+            scwc = None
+            while 1:
+                if not gid:
+                    break
+                paramId = codes_get(gid, 'paramId')
+                gridtype = codes_get(gid, 'gridType')
+                levtype = codes_get(gid, 'typeOfLevel')
+                if paramId == 77: # ETADOT
+                    codes_write(gid, fdict['21'])
+                elif paramId == 130: # T
+                    codes_write(gid, fdict['11'])
+                elif paramId == 131 or paramId == 132: # U, V wind component
+                    codes_write(gid, fdict['10'])
+                elif paramId == 133 and gridtype != 'reduced_gg': # Q
+                    codes_write(gid, fdict['17'])
+                elif paramId == 133 and gridtype == 'reduced_gg': # Q, gaussian
+                    codes_write(gid, fdict['18'])
+                elif paramId == 135: # W
+                    codes_write(gid, fdict['19'])
+                elif paramId == 152: # LNSP
+                    codes_write(gid, fdict['12'])
+                elif paramId == 155 and gridtype == 'sh': # D
+                    codes_write(gid, fdict['13'])
+                elif paramId == 246 or paramId == 247: # CLWC, CIWC
+                    # sum cloud liquid water and ice
+                    if scwc is None:
+                        scwc = codes_get_values(gid)
+                    else:
+                        scwc += codes_get_values(gid)
+                        codes_set_values(gid, scwc)
+                        codes_set(gid, 'paramId', 201031)
+                        codes_write(gid, fdict['22'])
+                        scwc = None
+                elif c.wrf and paramId in [129, 138, 155] and \
+                      levtype == 'hybrid': # Z, VO, D
+                    # do not do anything right now
+                    # these are specific parameter for WRF
+                    pass
+                else:
+                    if paramId not in savedfields:
+                        # SD/MSL/TCC/10U/10V/2T/2D/Z/LSM/SDOR/CVL/CVH/SR
+                        # and all ADDPAR parameter
+                        codes_write(gid, fdict['16'])
+                        savedfields.append(paramId)
+                    else:
+                        print('duplicate ' + str(paramId) + ' not written')
+
+                try:
+                    if c.wrf:
+                        # model layer
+                        if levtype == 'hybrid' and \
+                           paramId in [129, 130, 131, 132, 133, 138, 155]:
+                            codes_write(gid, fwrf)
+                        # sfc layer
+                        elif paramId in wrfpars:
+                            codes_write(gid, fwrf)
+                except AttributeError:
+                    pass
+
+                codes_release(gid)
+                gid = codes_new_from_index(iid)
+#============================================================================================
+            for f in fdict.values():
+                f.close()
+#============================================================================================
+            # call for Fortran program to convert e.g. reduced_gg grids to
+            # regular_ll and calculate detadot/dp
+            pwd = os.getcwd()
+            os.chdir(c.inputdir)
+            if os.stat('fort.21').st_size == 0 and c.eta:
+                print('Parameter 77 (etadot) is missing, most likely it is '
+                      'not available for this type or date / time\n')
+                print('Check parameters CLASS, TYPE, STREAM, START_DATE\n')
+                my_error('fort.21 is empty while parameter eta '
+                         'is set to 1 in CONTROL file')
+# ============================================================================================
+            # write out all output to log file before starting fortran programm
+            sys.stdout.flush()
+
+            # Fortran program creates file fort.15 (with u,v,etadot,t,sp,q)
+            execute_subprocess([os.path.join(c.exedir,
+                                _config.FORTRAN_EXECUTABLE)],
+                               error_msg='FORTRAN PROGRAM FAILED!')#shell=True)
+
+            os.chdir(pwd)
+# ============================================================================================
+            # create name of final output file, e.g. EN13040500 (ENYYMMDDHH)
+            if c.purefc:
+                suffix = cdate[2:8] + '.' + ctime + '.' + cstep
+            else:
+                suffix = cdate_hour[2:10]
+
+            # if necessary, add ensemble member number to filename suffix
+            if 'number' in index_keys:
+                index_number = index_keys.index('number')
+                if len(index_vals[index_number]) > 1:
+                    suffix = suffix + '.N{:0>3}'.format(int(prod[index_number]))
+
+            fnout = os.path.join(c.inputdir, c.prefix + suffix)
+            print("outputfile = " + fnout)
+            # collect for final processing
+            self.outputfilelist.append(os.path.basename(fnout))
+            # # get additional precipitation subgrid data if available
+            # if c.rrint:
+                # self.outputfilelist.append(os.path.basename(fnout + '_1'))
+                # self.outputfilelist.append(os.path.basename(fnout + '_2'))
+# ============================================================================================
+            # create outputfile and copy all data from intermediate files
+            # to the outputfile (final GRIB input files for FLEXPART)
+            orolsm = os.path.basename(glob.glob(c.inputdir +
+                                        '/OG_OROLSM__SL.*.' + c.ppid + '*')[0])
+            fluxfile = 'flux' + cdate[0:2] + suffix
+            if not c.cwc:
+                flist = ['fort.15', fluxfile, 'fort.16', orolsm]
+            else:
+                flist = ['fort.15', 'fort.22', fluxfile, 'fort.16', orolsm]
+
+            with open(fnout, 'wb') as fout:
+                for f in flist:
+                    shutil.copyfileobj(open(os.path.join(c.inputdir, f), 'rb'),
+                                       fout)
+
+            if c.omega:
+                with open(os.path.join(c.outputdir, 'OMEGA'), 'wb') as fout:
+                    shutil.copyfileobj(open(os.path.join(c.inputdir, 'fort.25'),
+                                            'rb'), fout)
+# ============================================================================================
+        if c.wrf:
+            fwrf.close()
+
+        codes_index_release(iid)
+
+        return
+
+
+    def calc_extra_elda(self, path, prefix):
+        ''' Calculates extra ensemble members for ELDA - Stream.
+
+        This is a specific feature which doubles the number of ensemble members
+        for the ELDA Stream.
+
+        Parameters
+        ----------
+        path : str
+            Path to the output files.
+
+        prefix : str
+            The prefix of the output filenames as defined in Control file.
+
+        Return
+        ------
+
+        '''
+        from eccodes import (codes_grib_new_from_file, codes_get_array,
+                             codes_set_array, codes_release, codes_set_values,
+                             codes_set, codes_write, codes_release)
+
+        # max number
+        maxnum = int(self.number.split('/')[-1])
+
+        # get a list of all prepared output files with control forecast (CF)
+        CF_filelist = UioFiles(path, prefix + '*.N000')
+
+        for cffile in CF_filelist.files:
+            with open(cffile, 'rb') as f:
+                cfvalues=[]
+                while True:
+                    fid = codes_grib_new_from_file(f)
+                    if fid is None:
+                        break
+                    cfvalues.append(codes_get_array(fid, 'values'))
+                    codes_release(fid)
+
+            filename = cffile.split('N000')[0]
+            for i in range(1, maxnum + 1):
+
+                # read an ensemble member
+                g = open(filename + 'N{:0>3}'.format(i), 'rb')
+                # create file for newly calculated ensemble member
+                h = open(filename + 'N{:0>3}'.format(i+maxnum), 'wb')
+                # number of message in grib file
+                j = 0
+                while True:
+                    gid = codes_grib_new_from_file(g)
+                    if gid is None:
+                        break
+                    values = codes_get_array(gid, 'values')
+                    # generate a new ensemble member by subtracting
+                    # 2 * ( current time step value - last time step value )
+                    codes_set_array(gid, 'values',
+                                    values-2*(values-cfvalues[j]))
+                    codes_set(gid, 'number', i+maxnum)
+                    codes_write(gid, h)
+                    codes_release(gid)
+                    j += 1
+
+                g.close()
+                h.close()
+                print('wrote ' + filename + 'N{:0>3}'.format(i+maxnum))
+                self.outputfilelist.append(
+                    os.path.basename(filename + 'N{:0>3}'.format(i+maxnum)))
+
+        return
+
+
+    def process_output(self, c):
+        '''Postprocessing of FLEXPART input files.
+
+        The grib files are postprocessed depending on the selection in
+        CONTROL file. The resulting files are moved to the output
+        directory if its not equal to the input directory.
+        The following modifications might be done if
+        properly switched in CONTROL file:
+        GRIB2 - Conversion to GRIB2
+        ECTRANS - Transfer of files to gateway server
+        ECSTORAGE - Storage at ECMWF server
+
+        Parameters
+        ----------
+        c : ControlFile
+            Contains all the parameters of CONTROL file and
+            command line.
+
+        Return
+        ------
+
+        '''
+
+        print('\n\nPostprocessing:\n Format: {}\n'.format(c.format))
+
+        if _config.FLAG_ON_ECMWFSERVER:
+            print('ecstorage: {}\n ecfsdir: {}\n'.
+                  format(c.ecstorage, c.ecfsdir))
+            print('ectrans: {}\n gateway: {}\n destination: {}\n '
+                  .format(c.ectrans, c.gateway, c.destination))
+
+        print('Output filelist: ')
+        print(sorted(self.outputfilelist))
+
+        for ofile in self.outputfilelist:
+            ofile = os.path.join(self.inputdir, ofile)
+
+            if c.format.lower() == 'grib2':
+                execute_subprocess(['grib_set', '-s', 'edition=2,' +
+                                    'productDefinitionTemplateNumber=8',
+                                    ofile, ofile + '_2'],
+                                   error_msg='GRIB2 CONVERSION FAILED!')
+
+                execute_subprocess(['mv', ofile + '_2', ofile],
+                                   error_msg='RENAMING FOR NEW GRIB2 FORMAT '
+                                   'FILES FAILED!')
+
+            if c.ectrans and _config.FLAG_ON_ECMWFSERVER:
+                execute_subprocess(['ectrans', '-overwrite', '-gateway',
+                                    c.gateway, '-remote', c.destination,
+                                    '-source', ofile],
+                                   error_msg='TRANSFER TO LOCAL SERVER FAILED!')
+
+            if c.ecstorage and _config.FLAG_ON_ECMWFSERVER:
+                execute_subprocess(['ecp', '-o', ofile,
+                                    os.path.expandvars(c.ecfsdir)],
+                                   error_msg='COPY OF FILES TO ECSTORAGE '
+                                   'AREA FAILED!')
+
+            if c.outputdir != c.inputdir:
+                execute_subprocess(['mv', os.path.join(c.inputdir, ofile),
+                                    c.outputdir],
+                                   error_msg='RELOCATION OF OUTPUT FILES '
+                                   'TO OUTPUTDIR FAILED!')
+
+        return
+
Index: Source/Python/Classes/GribUtil.py
===================================================================
--- Source/Python/Classes/GribUtil.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Classes/GribUtil.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,334 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Fouilloux (University of Oslo)
+#
+# @Date: July 2014
+#
+# @Change History:
+#   February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - changed some naming
+#
+# @License:
+#    (C) Copyright 2014-2018.
+#
+#    This software is licensed under the terms of the Apache Licence Version 2.0
+#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# @Class Description:
+#    The GRIB API provides all necessary tools to work directly with the
+#    grib files. Nevertheless, the GRIB API tools are very basic and are in
+#    direct connection with the grib files. This class provides some higher
+#    functions which apply a set of GRIB API tools together in the respective
+#    context. So, the class initially contains a list of grib files (their
+#    names) and the using program then applies the methods directly on the
+#    class objects without having to think about how the actual GRIB API
+#    tools have to be arranged.
+#
+# @Class Content:
+#    - __init__
+#    - get_keys
+#    - set_keys
+#    - copy_dummy_msg
+#    - index
+#
+# @Class Attributes:
+#    - filenames
+#
+#*******************************************************************************
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+
+# ------------------------------------------------------------------------------
+# CLASS
+# ------------------------------------------------------------------------------
+class GribUtil(object):
+    '''
+    Class for GRIB utilities (new methods) based on GRIB API
+    '''
+    # --------------------------------------------------------------------------
+    # CLASS FUNCTIONS
+    # --------------------------------------------------------------------------
+    def __init__(self, filenames):
+        '''Initialise an object of GribUtil and assign a list of filenames.
+
+        Parameters
+        ----------
+        filenames : :obj:`list` of :obj:`strings`
+             A list of filenames.
+
+        Return
+        ------
+
+        '''
+
+        self.filenames = filenames
+
+        return
+
+
+    def get_keys(self, keynames, wherekeynames=[], wherekeyvalues=[]):
+        '''Get keyvalues for a given list of keynames a where statement
+        can be given (list of key and list of values)
+
+        Parameters
+        ----------
+        keynames : :obj:`list` of :obj:`string`
+            List of keynames.
+
+        wherekeynames : :obj:`list` of :obj:`string`, optional
+            Default value is an empty list.
+
+        wherekeyvalues : :obj:`list` of :obj:`string`, optional
+            Default value is an empty list.
+
+        Return
+        ------
+        return_list : :obj:`list` of :obj:`string`
+            List of keyvalues for given keynames.
+        '''
+        from eccodes import (codes_new_from_file, codes_is_defined, codes_get,
+                             codes_release)
+
+        fileid = open(self.filenames, 'r')
+
+        return_list = []
+
+        while 1:
+            gid = codes_new_from_file(fileid)
+
+            if gid is None:
+                break
+
+            if len(wherekeynames) != len(wherekeyvalues):
+                raise Exception("Number of key values and key names must be \
+                                 the same. Give a value for each keyname!")
+
+            select = True
+            i = 0
+            for wherekey in wherekeynames:
+                if not codes_is_defined(gid, wherekey):
+                    raise Exception("where key was not defined")
+
+                select = (select and (str(wherekeyvalues[i]) ==
+                                      str(codes_get(gid, wherekey))))
+                i += 1
+
+            if select:
+                llist = []
+                for key in keynames:
+                    llist.extend([str(codes_get(gid, key))])
+                return_list.append(llist)
+
+            codes_release(gid)
+
+        fileid.close()
+
+        return return_list
+
+
+    def set_keys(self, fromfile, keynames, keyvalues, wherekeynames=[],
+                 wherekeyvalues=[], strict=False, filemode='w'):
+        '''Opens the file to read the grib messages and then write
+        the selected messages (with wherekeys) to a new output file.
+        Also, the keyvalues of the passed list of keynames are set.
+
+        Parameters
+        ----------
+        fromfile : :obj:`string`
+            Filename of the input file to read the grib messages from.
+
+        keynames : :obj:`list` of :obj:`string`
+            List of keynames to set in the selected messages.
+            Default is an empty list.
+
+        keyvalues : :obj:`list` of :obj:`string`
+            List of keyvalues to set in the selected messages.
+            Default is an empty list.
+
+        wherekeynames : :obj:`list` of :obj:`string`, optional
+            List of keynames to select correct message.
+            Default value is an empty list.
+
+        wherekeyvalues : :obj:`list` of :obj:`string`, optional
+            List of keyvalues for keynames to select correct message.
+            Default value is an empty list.
+
+        strict : :obj:`boolean`, optional
+            Decides if everything from keynames and keyvalues
+            is written out the grib file (False) or only those
+            meeting the where statement (True). Default is False.
+
+        filemode : :obj:`string`, optional
+            Sets the mode for the output file. Default is "w".
+
+        Return
+        ------
+
+        '''
+        from eccodes import (codes_grib_new_from_file, codes_is_defined,
+                             codes_get, codes_set, codes_write,
+                             codes_set_values, codes_release)
+
+        if len(wherekeynames) != len(wherekeyvalues):
+            raise Exception("Give a value for each keyname!")
+
+        fout = open(self.filenames, filemode)
+        fin = open(fromfile)
+
+        while 1:
+            gid = codes_grib_new_from_file(fin)
+
+            if gid is None:
+                break
+
+            select = True
+            i = 0
+            for wherekey in wherekeynames:
+                if not codes_is_defined(gid, wherekey):
+                    raise Exception("wherekey was not defined")
+
+                select = (select and (str(wherekeyvalues[i]) ==
+                                      str(codes_get(gid, wherekey))))
+                i += 1
+
+            if select:
+                i = 0
+                for key in keynames:
+                    if key == 'values':
+                        codes_set_values(gid, keyvalues[i])
+                    else:
+                        codes_set(gid, key, keyvalues[i])
+                    i += 1
+
+                codes_write(gid, fout)
+
+            codes_release(gid)
+
+        fin.close()
+        fout.close()
+
+        return
+
+    def copy_dummy_msg(self, filename_in, selectWhere=True,
+                 keynames=[], keyvalues=[], filemode='w'):
+        '''Add the content of another input grib file to the objects file but
+        only messages corresponding to keys/values passed to the function.
+        The selectWhere switch decides if to copy the keys equal to (True) or
+        different to (False) the keynames/keyvalues list passed to the function.
+
+        Parameters
+        ----------
+        filename_in : :obj:`string`
+            Filename of the input file to read the grib messages from.
+
+        selectWhere : :obj:`boolean`, optional
+            Decides if to copy the keynames and values equal to (True) or
+            different to (False) the keynames/keyvalues list passed to the
+            function. Default is True.
+
+        keynames : :obj:`list` of :obj:`string`, optional
+            List of keynames. Default is an empty list.
+
+        keyvalues : :obj:`list` of :obj:`string`, optional
+            List of keyvalues. Default is an empty list.
+
+        filemode : :obj:`string`, optional
+            Sets the mode for the output file. Default is "w".
+
+        Return
+        ------
+
+        '''
+        from eccodes import (codes_grib_new_from_file, codes_is_defined,
+                             codes_get, codes_release, codes_write)
+
+        if len(keynames) != len(keyvalues):
+            raise Exception("Give a value for each keyname!")
+
+        fin = open(filename_in, 'rb')
+        fout = open(self.filenames, filemode)
+
+        fields = 0
+
+        while fields < 1:
+            gid = codes_grib_new_from_file(fin)
+
+            if gid is None:
+                break
+
+            select = True
+            i = 0
+            for key in keynames:
+                if not codes_is_defined(gid, key):
+                    raise Exception("Key was not defined")
+
+                if selectWhere:
+                    select = (select and (str(keyvalues[i]) ==
+                                          str(codes_get(gid, key))))
+                else:
+                    select = (select and (str(keyvalues[i]) !=
+                                          str(codes_get(gid, key))))
+                i += 1
+
+            if select:
+                fields = fields + 1
+                codes_write(gid, fout)
+
+            codes_release(gid)
+
+        fin.close()
+        fout.close()
+
+        return
+
+    def index(self, index_keys=["mars"], index_file="my.idx"):
+        '''Create index file from a list of files if it does not exist or
+        read an index file.
+
+        Parameters
+        ----------
+        index_keys: :obj:`list` of :obj:`string`, optional
+            Contains the list of key parameter names from
+            which the index is to be created.
+            Default is a list with a single entry string "mars".
+
+        index_file: :obj:`string`, optional
+            Filename where the indices are stored.
+            Default is "my.idx".
+
+        Return
+        ------
+        iid: :obj:`integer`
+            Grib index id.
+        '''
+        from eccodes import (codes_index_read, codes_index_new_from_file,
+                             codes_index_add_file, codes_index_write)
+
+        print("... index will be done")
+        iid = None
+
+        if os.path.exists(index_file):
+            iid = codes_index_read(index_file)
+            print("Use existing index file: %s " % (index_file))
+        else:
+            for filename in self.filenames:
+                print("Inputfile: %s " % (filename))
+                if iid is None:
+                    iid = codes_index_new_from_file(filename, index_keys)
+                else:
+                    codes_index_add_file(iid, filename)
+
+            if iid is not None:
+                codes_index_write(iid, index_file)
+
+        print('... index done')
+
+        return iid
Index: Source/Python/Classes/MarsRetrieval.py
===================================================================
--- Source/Python/Classes/MarsRetrieval.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Classes/MarsRetrieval.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,535 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Fouilloux (University of Oslo)
+#
+# @Date: October 2014
+#
+# @Change History:
+#
+#   November 2015 - Leopold Haimberger (University of Vienna):
+#        - optimized display_info
+#        - optimized data_retrieve and seperate between python and shell
+#          script call
+#
+#   February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - applied some minor modifications in programming style/structure
+#        - added writing of mars request attributes to a csv file
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+import sys
+import subprocess
+import traceback
+
+# software specific classes and modules from flex_extract
+sys.path.append('../')
+import _config
+try:
+    ec_api = True
+    import ecmwfapi
+except ImportError:
+    ec_api = False
+
+try:
+    cds_api = True
+    import cdsapi
+except ImportError:
+    cds_api = False
+# ------------------------------------------------------------------------------
+# CLASS
+# ------------------------------------------------------------------------------
+class MarsRetrieval(object):
+    '''Specific syntax and content for submission of MARS retrievals.
+
+    A MARS revtrieval has a specific syntax with a selection of keywords and
+    their corresponding values. This class provides the necessary functions
+    by displaying the selected parameters and their values and the actual
+    retrievement of the data through a mars request or a Python web api
+    interface. The initialization already expects all the keyword values.
+
+    A description of MARS keywords/arguments and examples of their
+    values can be found here:
+    https://software.ecmwf.int/wiki/display/UDOC/\
+                   Identification+keywords#Identificationkeywords-class
+
+    Attributes
+    ----------
+    server : ECMWFService or ECMWFDataServer
+        This is the connection to the ECMWF data servers.
+
+    public : int
+        Decides which Web API Server version is used.
+
+    marsclass : str, optional
+        Characterisation of dataset.
+
+    dataset : str, optional
+        For public datasets there is the specific naming and parameter
+        dataset which has to be used to characterize the type of
+        data.
+
+    type : str, optional
+        Determines the type of fields to be retrieved.
+
+    levtype : str, optional
+        Denotes type of level.
+
+    levelist : str, optional
+        Specifies the required levels.
+
+    repres : str, optional
+        Selects the representation of the archived data.
+
+    date : str, optional
+        Specifies the Analysis date, the Forecast base date or
+        Observations date.
+
+    resol : str, optional
+        Specifies the desired triangular truncation of retrieved data,
+        before carrying out any other selected post-processing.
+
+    stream : str, optional
+        Identifies the forecasting system used to generate the data.
+
+    area : str, optional
+        Specifies the desired sub-area of data to be extracted.
+
+    time : str, optional
+        Specifies the time of the data in hours and minutes.
+
+    step : str, optional
+        Specifies the forecast time step from forecast base time.
+
+    expver : str, optional
+        The version of the dataset.
+
+    number : str, optional
+        Selects the member in ensemble forecast run.
+
+    accuracy : str, optional
+        Specifies the number of bits per value to be used in the
+        generated GRIB coded fields.
+
+    grid : str, optional
+        Specifies the output grid which can be either a Gaussian grid
+        or a Latitude/Longitude grid.
+
+    gaussian : str, optional
+        This parameter is deprecated and should no longer be used.
+        Specifies the desired type of Gaussian grid for the output.
+
+    target : str, optional
+        Specifies a file into which data is to be written after
+        retrieval or manipulation.
+
+    param : str, optional
+        Specifies the meteorological parameter.
+    '''
+
+    def __init__(self, server, public, marsclass="EA", dataset="", type="",
+                 levtype="", levelist="", repres="", date="", resol="",
+                 stream="", area="", time="", step="", expver="1",
+                 number="", accuracy="", grid="", gaussian="", target="",
+                 param=""):
+        '''Initialises the instance of the MarsRetrieval class and
+        defines and assigns a set of the necessary retrieval parameters
+        for the FLEXPART input data.
+        A description of MARS keywords/arguments, their dependencies
+        on each other and examples of their values can be found here:
+
+        https://software.ecmwf.int/wiki/display/UDOC/MARS+keywords
+
+        Parameters
+        ----------
+        server : ECMWFService or ECMWFDataServer
+            This is the connection to the ECMWF data servers.
+            It is needed for the pythonic access of ECMWF data.
+
+        public : int
+            Decides which Web API version is used:
+            0: member-state users and full archive access
+            1: public access and limited access to the public server and
+               datasets. Needs the parameter dataset.
+            Default is "0" and for member-state users.
+
+        marsclass : str, optional
+            Characterisation of dataset. E.g. EI (ERA-Interim),
+            E4 (ERA40), OD (Operational archive), EA (ERA5).
+            Default is the ERA5 dataset "EA".
+
+        dataset : str, optional
+            For public datasets there is the specific naming and parameter
+            dataset which has to be used to characterize the type of
+            data. Usually there is less data available, either in times,
+            domain or parameter.
+            Default is an empty string.
+
+        type : str, optional
+            Determines the type of fields to be retrieved.
+            Selects between observations, images or fields.
+            Examples for fields: Analysis (an), Forecast (fc),
+            Perturbed Forecast (pf), Control Forecast (cf) and so on.
+            Default is an empty string.
+
+        levtype : str, optional
+            Denotes type of level. Has a direct implication on valid
+            levelist values!
+            E.g. model level (ml), pressure level (pl), surface (sfc),
+            potential vorticity (pv), potential temperature (pt)
+            and depth (dp).
+            Default is an empty string.
+
+        levelist : str, optional
+            Specifies the required levels. It has to have a valid
+            correspondence to the selected levtype.
+            Examples: model level: 1/to/137, pressure levels: 500/to/1000
+            Default is an empty string.
+
+        repres : str, optional
+            Selects the representation of the archived data.
+            E.g. sh - spherical harmonics, gg - Gaussian grid,
+            ll - latitude/longitude, ...
+            Default is an empty string.
+
+        date : str, optional
+            Specifies the Analysis date, the Forecast base date or
+            Observations date. Valid formats are:
+            Absolute as YYYY-MM-DD or YYYYMMDD.
+            Default is an empty string.
+
+        resol : str, optional
+            Specifies the desired triangular truncation of retrieved data,
+            before carrying out any other selected post-processing.
+            The default is automatic truncation (auto), by which the lowest
+            resolution compatible with the value specified in grid is
+            automatically selected for the retrieval.
+            Users wanting to perform post-processing from full spectral
+            resolution should specify Archived Value (av).
+            The following are examples of existing resolutions found in
+            the archive: 63, 106, 159, 213, 255, 319, 399, 511, 799 or 1279.
+            This keyword has no meaning/effect if the archived data is
+            not in spherical harmonics representation.
+            The best selection can be found here:
+            https://software.ecmwf.int/wiki/display/UDOC/\
+                  Retrieve#Retrieve-Truncationbeforeinterpolation
+            Default is an empty string.
+
+        stream : str, optional
+            Identifies the forecasting system used to generate the data.
+            E.g. oper (Atmospheric model), enfo (Ensemble forecats), ...
+            Default is an empty string.
+
+        area : str, optional
+            Specifies the desired sub-area of data to be extracted.
+            Areas can be defined to wrap around the globe.
+
+            Latitude values must be given as signed numbers, with:
+                north latitudes (i.e. north of the equator)
+                    being positive (e.g: 40.5)
+                south latitutes (i.e. south of the equator)
+                    being negative (e.g: -50.5)
+            Longtitude values must be given as signed numbers, with:
+                east longitudes (i.e. east of the 0 degree meridian)
+                    being positive (e.g: 35.0)
+                west longitudes (i.e. west of the 0 degree meridian)
+                    being negative (e.g: -20.5)
+
+            E.g.: North/West/South/East
+            Default is an empty string.
+
+        time : str, optional
+            Specifies the time of the data in hours and minutes.
+            Valid values depend on the type of data: Analysis time,
+            Forecast base time or First guess verification time
+            (all usually at synoptic hours: 00, 06, 12 and 18 ).
+            Observation time (any combination in hours and minutes is valid,
+            subject to data availability in the archive).
+            The syntax is HHMM or HH:MM. If MM is omitted it defaults to 00.
+            Default is an empty string.
+
+        step : str, optional
+            Specifies the forecast time step from forecast base time.
+            Valid values are hours (HH) from forecast base time. It also
+            specifies the length of the forecast which verifies at
+            First Guess time.
+            E.g. 1/3/6-hourly
+            Default is an empty string.
+
+        expver : str, optional
+            The version of the dataset. Each experiment is assigned a
+            unique code (version). Production data is assigned 1 or 2,
+            and experimental data in Operations 11, 12 ,...
+            Research or Member State's experiments have a four letter
+            experiment identifier.
+            Default is "1".
+
+        number : str, optional
+            Selects the member in ensemble forecast run. (Only then it
+            is necessary.) It has a different meaning depending on
+            the type of data.
+            E.g. Perturbed Forecasts: specifies the Ensemble forecast member
+            Default is an empty string.
+
+        accuracy : str, optional
+            Specifies the number of bits per value to be used in the
+            generated GRIB coded fields.
+            A positive integer may be given to specify the preferred number
+            of bits per packed value. This must not be greater than the
+            number of bits normally used for a Fortran integer on the
+            processor handling the request (typically 32 or 64 bit).
+            Within a compute request the accuracy of the original fields
+            can be passed to the result field by specifying accuracy=av.
+            Default is an empty string.
+
+        grid : str, optional
+            Specifies the output grid which can be either a Gaussian grid
+            or a Latitude/Longitude grid. MARS requests specifying
+            grid=av will return the archived model grid.
+
+            Lat/Lon grid: The grid spacing needs to be an integer
+            fraction of 90 degrees e.g. grid = 0.5/0.5
+
+            Gaussian grid: specified by a letter denoting the type of
+            Gaussian grid followed by an integer (the grid number)
+            representing the number of lines between the Pole and Equator,
+            e.g.
+            grid = F160 - full (or regular) Gaussian grid with
+                   160 latitude lines between the pole and equator
+            grid = N320 - ECMWF original reduced Gaussian grid with
+                   320 latitude lines between the pole and equator,
+                   see Reduced Gaussian Grids for grid numbers used at ECMWF
+            grid = O640 - ECMWF octahedral (reduced) Gaussian grid with
+                   640 latitude lines between the pole and equator
+            Default is an empty string.
+
+        gaussian : str, optional
+            This parameter is deprecated and should no longer be used.
+            Specifies the desired type of Gaussian grid for the output.
+            Valid Gaussian grids are quasi-regular (reduced) or regular.
+            Keyword gaussian can only be specified together with
+            keyword grid. Gaussian without grid has no effect.
+            Default is an empty string.
+
+        target : str, optional
+            Specifies a file into which data is to be written after
+            retrieval or manipulation. Path names should always be
+            enclosed in double quotes. The MARS client supports automatic
+            generation of multiple target files using MARS keywords
+            enclosed in square brackets [ ].  If the environment variable
+            MARS_MULTITARGET_STRICT_FORMAT is set to 1 before calling mars,
+            the keyword values will be used in the filename as shown by
+            the ecCodes GRIB tool grib_ls -m, e.g. with
+            MARS_MULTITARGET_STRICT_FORMAT set to 1 the keywords time,
+            expver and param will be formatted as 0600, 0001 and 129.128
+            rather than 600, 1 and 129.
+            Default is an empty string.
+
+        param : str, optional
+            Specifies the meteorological parameter.
+            The list of meteorological parameters in MARS is extensive.
+            Their availability is directly related to their meteorological
+            meaning and, therefore, the rest of directives specified
+            in the MARS request.
+            Meteorological parameters can be specified by their
+            GRIB code (param=130), their mnemonic (param=t) or
+            full name (param=temperature).
+            The list of parameter should be seperated by a "/"-sign.
+            E.g. 130/131/133
+            Default is an empty string.
+
+        Return
+        ------
+
+        '''
+
+        self.server = server
+        self.public = public
+        self.marsclass = marsclass
+        self.dataset = dataset
+        self.type = type
+        self.levtype = levtype
+        self.levelist = levelist
+        self.repres = repres
+        self.date = date
+        self.resol = resol
+        self.stream = stream
+        self.area = area
+        self.time = time
+        self.step = step
+        self.expver = expver
+        self.number = number
+        self.accuracy = accuracy
+        self.grid = grid
+        self.gaussian = gaussian
+        self.target = target
+        self.param = param
+
+        return
+
+
+    def display_info(self):
+        '''Prints all class attributes and their values to the
+        standard output.
+
+        Parameters
+        ----------
+
+        Return
+        ------
+
+        '''
+        # Get all class attributes and their values as a dictionary
+        attrs = vars(self).copy()
+
+        # iterate through all attributes and print them
+        # with their corresponding values
+        for item in attrs.items():
+            if item[0] in ['server', 'public']:
+                pass
+            else:
+                print(item[0] + ': ' + str(item[1]))
+
+        return
+
+
+    def print_infodata_csv(self, inputdir, request_number):
+        '''Write all request parameter in alpabetical order into a "csv" file.
+
+        Parameters
+        ----------
+        inputdir : str
+            The path where all data from the retrievals are stored.
+
+        request_number : int
+            Number of mars requests for flux and non-flux data.
+
+        Return
+        ------
+
+        '''
+
+        # Get all class attributes and their values as a dictionary
+        attrs = vars(self).copy()
+        del attrs['server']
+        del attrs['public']
+
+        # open a file to store all requests to
+        with open(os.path.join(inputdir,
+                               _config.FILE_MARS_REQUESTS), 'a') as f:
+            f.write(str(request_number) + ', ')
+            f.write(', '.join(str(attrs[key])
+                              for key in sorted(attrs.keys())))
+            f.write('\n')
+
+        return
+
+    def data_retrieve(self):
+        '''Submits a MARS retrieval. Depending on the existence of
+        ECMWF Web-API or CDS API it is submitted via Python or a
+        subprocess in the Shell. The parameter for the mars retrieval
+        are taken from the defined class attributes.
+
+        Parameters
+        ----------
+
+        Return
+        ------
+
+        '''
+        # Get all class attributes and their values as a dictionary
+        attrs = vars(self).copy()
+
+        # eliminate unnecessary attributes from the dictionary attrs
+        del attrs['server']
+        del attrs['public']
+
+        # exchange parameter name for marsclass
+        mclass = attrs.get('marsclass')
+        del attrs['marsclass']
+        attrs['class'] = mclass
+
+        # prepare target variable as needed for the Web API or CDS API mode
+        # within the dictionary for full access
+        # as a single variable for public access
+        target = attrs.get('target')
+        if not int(self.public):
+            del attrs['target']
+        print('target: ' + target)
+
+        # find all keys without a value and convert all other values to strings
+        empty_keys = []
+        for key, value in attrs.items():
+            if value == '':
+                empty_keys.append(str(key))
+            else:
+                attrs[key] = str(value)
+
+        # delete all empty parameter from the dictionary
+        for key in empty_keys:
+            del attrs[key]
+
+        attrs['ppengine'] = 'emos'
+
+        # MARS request via Python script
+        if self.server:
+            try:
+                if cds_api and isinstance(self.server, cdsapi.Client):
+                    print('RETRIEVE ERA5 WITH CDS API!')
+                    self.server.retrieve(_config.CDS_DATASET,
+                                         attrs, target)
+                elif ec_api and isinstance(self.server, ecmwfapi.ECMWFDataServer):
+                    print('RETRIEVE PUBLIC DATA (NOT ERA5)!')
+                    self.server.retrieve(attrs)
+                elif ec_api and isinstance(self.server, ecmwfapi.ECMWFService):
+                    print('EXECUTE NON-PUBLIC RETRIEVAL (NOT ERA5)!')
+                    self.server.execute(attrs, target)
+                else:
+                    print('ERROR:')
+                    print('No match for Web API instance!')
+                    raise IOError
+            except Exception as e:
+                print('\n\nMARS Request failed!')
+                print(e)
+                tb = sys.exc_info()[2]
+                print(traceback.format_exc())
+                sys.exit()
+
+        # MARS request via call in shell
+        else:
+            request_str = 'ret'
+            for key, value in attrs.items():
+                request_str = request_str + ',' + key + '=' + str(value)
+            request_str += ',target="' + target + '"'
+            p = subprocess.Popen(['mars', '-e'],
+                                 stdin=subprocess.PIPE,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE,
+                                 bufsize=1)
+            pout = p.communicate(input=request_str.encode())[0]
+            print(pout.decode())
+
+            if 'Some errors reported' in pout.decode():
+                print('MARS Request failed - please check request')
+                raise IOError
+            elif os.stat(target).st_size == 0:
+                print('MARS Request returned no data - please check request')
+                raise IOError
+
+        return
Index: Source/Python/Classes/UioFiles.py
===================================================================
--- Source/Python/Classes/UioFiles.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Classes/UioFiles.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Fouilloux (University of Oslo)
+#
+# @Date: October 2014
+#
+# @Change History:
+#
+#    November 2015 - Leopold Haimberger (University of Vienna):
+#        - modified method list_files to work with glob instead of listdir
+#        - added pattern search in method list_files
+#
+#    February - December 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - optimisation of method list_files since it didn't work correctly
+#          for sub directories
+#        - additional speed up of method list_files
+#        - modified the class so that it is initiated with a pattern instead
+#          of suffixes. Gives more precision in selection of files.
+#        - added delete method
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+import os
+import sys
+import fnmatch
+
+# software specific modules from flex_extract
+sys.path.append('../')
+from Mods.tools import silent_remove, get_list_as_string
+
+# ------------------------------------------------------------------------------
+# CLASS
+# ------------------------------------------------------------------------------
+
+class UioFiles(object):
+    '''Collection of files matching a specific pattern.
+
+    The pattern can contain regular expressions for the files.
+    The files are listed and can be transformed to a single string or
+    they can be deleted.
+
+    Attributes
+    ----------
+    path : str
+        Directory where to list the files.
+
+    pattern : str
+        Regular expression pattern. For example: '\*.grb'
+
+    files : list of str
+        List of files matching the pattern in the path.
+    '''
+    # --------------------------------------------------------------------------
+    # CLASS METHODS
+    # --------------------------------------------------------------------------
+    def __init__(self, path, pattern):
+        '''Assignes a specific pattern for these files.
+
+        Parameters
+        ----------
+        path : str
+            Directory where to list the files.
+
+        pattern : str
+            Regular expression pattern. For example: '\*.grb'
+
+        Return
+        ------
+
+        '''
+
+        self.path = path
+        self.pattern = pattern
+        self.files = []
+
+        self._list_files(self.path)
+
+        return
+
+
+    def _list_files(self, path):
+        '''Lists all files in the directory with the matching
+        regular expression pattern.
+
+        Parameters
+        ----------
+        path : str
+            Path to the files.
+
+        Return
+        ------
+
+        '''
+        # Get the absolute path
+        path = os.path.abspath(path)
+
+        # get all files in the dir and subdir as absolut path
+        for root, dirnames, filenames in os.walk(path):
+            for filename in fnmatch.filter(filenames, self.pattern):
+                self.files.append(os.path.join(root, filename))
+
+        return
+
+
+    def __str__(self):
+        '''Converts the list of files into a single string.
+        The entries are sepereated by "," sign.
+
+        Parameters
+        ----------
+
+        Return
+        ------
+        files_string : str
+            The content of the list as a single string.
+        '''
+
+        filenames = [os.path.basename(f) for f in self.files]
+        files_string = get_list_as_string(filenames, concatenate_sign=', ')
+
+        return files_string
+
+
+    def delete_files(self):
+        '''Deletes the files.
+
+        Parameters
+        ----------
+
+        Return
+        ------
+
+        '''
+
+        for old_file in self.files:
+            silent_remove(old_file)
+
+        return
Index: Source/Python/Classes/__init__.py
===================================================================
--- Source/Python/Classes/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Classes/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: September 2018
+#
+# @License:
+#    (C) Copyright 2015-2018.
+#
+#    This software is licensed under the terms of the Apache Licence Version 2.0
+#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+#
+#*******************************************************************************
Index: Source/Python/Mods/__init__.py
===================================================================
--- Source/Python/Mods/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Mods/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: September 2018
+#
+# @License:
+#    (C) Copyright 2015-2018.
+#
+#    This software is licensed under the terms of the Apache Licence Version 2.0
+#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+#
+#*******************************************************************************
Index: Source/Python/Mods/checks.py
===================================================================
--- Source/Python/Mods/checks.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Mods/checks.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,887 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: November 2018
+#
+# @Change History:
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+'''This module contains check methods for the CONTROL paramaters.
+'''
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+
+from __future__ import print_function
+import os
+import sys
+
+import _config
+try:
+    import exceptions
+except ImportError:
+    import builtins as exceptions
+from datetime import datetime
+from Mods.tools import my_error, silent_remove
+# ------------------------------------------------------------------------------
+# FUNCTIONS
+# ------------------------------------------------------------------------------
+
+def check_logicals_type(c, logicals):
+    '''Check that the logical variables have correct type integer.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    logicals : list of (str or int)
+        Names of the switches that are used to control the flow of the
+        program.
+
+    Return
+    ------
+
+    '''
+
+    for var in logicals:
+        if not isinstance(getattr(c, var), int):
+            setattr(c, var, int(getattr(c, var)))
+
+    return
+
+def check_grid(grid):
+    '''Convert grid into correct Lat/Lon format. E.g. '0.5/0.5'
+
+    Checks on format of original grid. Wether it is in the order of 1000 or 1.
+    Convert to correct grid format and substitute into "Lat/Lon" format string.
+
+    Parameters
+    ----------
+    grid : str
+        Contains grid information
+
+    Return
+    ------
+    grid : str
+        Contains grid in format Lat/lon. E.g. 0.1/0.1
+    '''
+
+    if 'N' in grid:
+        return grid
+    if '/' in grid:
+        gridx, gridy = grid.split('/')
+        if gridx == gridy:
+            grid = gridx
+        else:
+            raise ValueError('GRID parameter contains two '
+                             'different values: %s' (grid))
+    # # determine grid format
+    # if float(grid) / 100. >= 0.5:
+    #    # grid is defined in 1/1000 degrees; old format
+    #    grid = '{}/{}'.format(float(grid) / 1000.,
+    #                          float(grid) / 1000.)
+    # elif float(grid) / 100. < 0.5:
+    #    # grid is defined in normal degree; new format
+    #    grid = '{}/{}'.format(float(grid), float(grid))
+
+
+    # determine grid format
+    # assumes that nobody wants grid spacings of 20 deg or more
+    if float(grid) >= 20.:
+        # grid is defined in 1/1000 degree; old format
+        grid = '{}/{}'.format(float(grid) / 1000., float(grid) / 1000.)
+    else:
+        # grid is defined in degree; new format
+        grid = '{}/{}'.format(float(grid), float(grid))
+
+
+    return grid
+
+def check_area(grid, area, upper, lower, left , right):
+    '''Defines the correct area string.
+
+    Checks on the format of the four area components. Wether it is of
+    the order of 1000 or 1. Also checks wether area was already set by command
+    line, then the four components are overwritten.
+    Convert to correct format of the order of magnitude "1" and sets the
+    area parameter (North/West/South/East).
+    E.g.: -5./20./10./10.
+
+    Parameters
+    ----------
+    grid : str
+        Contains grid information.
+
+    area : str
+        Contains area informtion.
+
+    upper : str
+        The northern most latitude.
+
+    lower : str
+        The souther most latitude.
+
+    left : str
+        The western most longitude.
+
+    right : str
+        The eastern most longiude.
+
+    Return
+    ------
+    grid : str
+        Contains grid in format Lat/lon. E.g. 0.1/0.1
+    '''
+    if 'N' in grid:  # Gaussian output grid
+        area = 'G'
+        return area
+
+    # if area was provided decompose area into its 4 components
+    if area:
+        components = area.split('/')
+        upper, left, lower, right = components
+
+    # determine area format
+    if ((abs(float(upper) / 10000.) >= 0.01 or float(upper) / 1000. == 0. ) and
+        (abs(float(lower) / 10000.) >= 0.01 or float(lower) / 1000. == 0. ) and
+        (abs(float(left) / 10000.) >= 0.01 or float(left) / 1000. == 0. ) and
+        (abs(float(right) / 10000.) >= 0.01 or float(right) / 1000. == 0.)):
+        # area is defined in 1/1000 degrees; old format
+        area = '{}/{}/{}/{}'.format(float(upper) / 1000.,
+                                    float(left) / 1000.,
+                                    float(lower) / 1000.,
+                                    float(right) / 1000.)
+    elif (abs(float(upper) / 10000.) < 0.05 and
+          abs(float(lower) / 10000.) < 0.05 and
+          abs(float(left) / 10000.) < 0.05 and
+          abs(float(right) / 10000.) < 0.05):
+        # area is already in new format
+        area = '{}/{}/{}/{}'.format(float(upper),
+                                    float(left),
+                                    float(lower),
+                                    float(right))
+    else:
+        raise ValueError('The area components have different '
+                         'formats (upper, lower, left, right): '
+                         '{}/{}/{}/{}'.format(str(upper), str(lower),
+                                              str(left) , str(right)))
+
+    return area
+
+def check_levels(levelist, level):
+    '''Defines correct level list and guarantees that the maximum level is
+    one of the available maximum levels.
+
+    Parameters
+    ----------
+    levelist : str
+        Specifies the level list.
+        Examples: model level: 1/to/137, pressure levels: 500/to/1000
+
+    level : str
+        Specifies the maximum level.
+
+    Return
+    ------
+    levelist : str
+        Specifies the required levels. It has to have a valid
+        correspondence to the selected levtype.
+        Examples: model level: 1/to/137, pressure levels: 500/to/1000
+
+    level : str
+        Specifies the maximum level. It has to be one of the
+        available maximum level number as contained in the variable
+        MAX_LEVEL_LIST in "_config". E.g. [16, 19, 31, 40, 50, 60, 62, 91, 137]
+
+    '''
+    # assure consistency of levelist and level
+    if not levelist and not level:
+        raise ValueError('ERROR: neither levelist nor level '
+                         'specified in CONTROL file')
+    elif not levelist and level:
+        levelist = '1/to/' + level
+    elif (levelist and not level) or \
+         (levelist[-1] != level[-1]):
+        level = levelist.split('/')[-1]
+    else:
+        pass
+
+    # check if max level is a valid level
+    if int(level) not in _config.MAX_LEVEL_LIST:
+        raise ValueError('ERROR: \n'
+                         'LEVEL must be the maximum level of a specified '
+                         'level list from ECMWF, e.g. {} \n'
+                         'Check parameter "LEVEL" or the max level of '
+                         '"LEVELIST"!'.format(str(_config.MAX_LEVEL_LIST)))
+
+    return levelist, level
+
+
+def check_ppid(c, ppid):
+    '''Sets the current PPID.
+
+    Parameters
+    ----------
+    c : ControlFile
+            Contains all the parameters of CONTROL file and
+            command line.
+
+    ppid : int or None
+        Contains the ppid number provided by the command line parameter
+        of is None otherwise.
+
+    Return
+    ------
+
+    '''
+
+    if not ppid:
+        c.ppid = str(os.getppid())
+    else:
+        c.ppid = ppid
+
+    return
+
+
+def check_purefc(ftype):
+    '''Check for a pure forecast mode.
+
+    Parameters
+    ----------
+    ftype : list of str
+        List of field types.
+
+    Return
+    ------
+    True or False:
+        True if pure forecasts are to be retrieved. False if there are
+        analysis fields in between.
+    '''
+
+    if 'AN' not in ftype and '4V' not in ftype:
+        # pure forecast
+        return 1
+
+    return 0
+
+
+def check_step(step, mailfail):
+    '''Checks on step format and convert into a list of steps.
+
+    If the steps were defined with "to" and "by" they are converted into
+    a list of steps. If the steps were set in a string, it is
+    converted into a list.
+
+    Parameters
+    ----------
+    step : list of str or str
+        Specifies the forecast time step from forecast base time.
+        Valid values are hours (HH) from forecast base time.
+
+    mailfail : list of str
+        Contains all email addresses which should be notified.
+        It might also contain just the ecmwf user name which will trigger
+        mailing to the associated email address for this user.
+
+    Return
+    ------
+    step : list of str
+        List of forecast steps in format e.g. [001, 002, ...]
+    '''
+    import numpy as np
+
+    if '/' in step:
+        steps = step.split('/')
+        if 'to' in step.lower() and 'by' in step.lower():
+            ilist = np.arange(int(steps[0]),
+                              int(steps[2]) + 1,
+                              int(steps[4]))
+            step = ['{:0>3}'.format(i) for i in ilist]
+        elif 'to' in step.lower() and 'by' not in step.lower():
+            my_error(step + ':\n' +
+                     'if "to" is used in steps parameter, '
+                     'please use "by" as well')
+        else:
+            step = steps
+
+    if not isinstance(step, list):
+        step = [step]
+
+    return step
+
+def check_type(ftype, steps):
+    '''Check if type variable is of type list and if analysis field has
+    forecast step 0.
+
+    Parameters
+    ----------
+    ftype : list of str or str
+        List of field types.
+
+    steps : str
+        Specifies the forecast time step from forecast base time.
+        Valid values are hours (HH) from forecast base time.
+
+    Return
+    ------
+    ftype : list of str
+        List of field types.
+    '''
+    if not isinstance(ftype, list):
+        ftype = [ftype]
+
+    for i, val in enumerate(ftype):
+        if ftype[i] == 'AN' and int(steps[i]) != 0:
+            print('Analysis retrievals must have STEP = 0 (now set to 0)')
+            ftype[i] = 0
+
+    return ftype
+
+def check_time(ftime):
+    '''Check if time variable is of type list. Otherwise convert to list.
+
+    Parameters
+    ----------
+    ftime : list of str or str
+        The time in hours of the field.
+
+    Return
+    ------
+    ftime : list of str
+        The time in hours of the field.
+    '''
+    if not isinstance(ftime, list):
+        ftime = [ftime]
+
+    return ftime
+
+def check_len_type_time_step(ftype, ftime, steps, maxstep, purefc):
+    '''Check if
+
+    Parameters
+    ----------
+    ftype : list of str
+        List of field types.
+
+    ftime : list of str or str
+        The time in hours of the field.
+
+    steps : str
+        Specifies the forecast time step from forecast base time.
+        Valid values are hours (HH) from forecast base time.
+
+    maxstep : int
+        The maximum forecast time step in hours from the forecast base time.
+        This is the maximum step for non flux (accumulated) forecast data.
+
+    purefc : int
+        Switch for definition of pure forecast mode or not.
+
+    Return
+    ------
+    ftype : list of str
+        List of field types.
+
+    ftime : list of str
+        The time in hours of the field.
+
+    steps : str
+        Specifies the forecast time step from forecast base time.
+        Valid values are hours (HH) from forecast base time.
+    '''
+    if not (len(ftype) == len(ftime) == len(steps)):
+        raise ValueError('ERROR: The number of field types, times and steps '
+                         'are not the same! Please check the setting in the '
+                         'CONTROL file!')
+
+    # if pure forecast is selected and only one field type/time is set
+    # prepare a complete list of type/time/step combination upto maxstep
+    if len(ftype) == 1 and purefc:
+        nftype = []
+        nsteps = []
+        nftime = []
+        for i in range(0, maxstep + 1):
+            nftype.append(ftype[0])
+            nsteps.append('{:0>3}'.format(i))
+            nftime.append(ftime[0])
+        return nftype, nftime, nsteps
+
+    return ftype, ftime, steps
+
+def check_mail(mail):
+    '''Check the string of mail addresses, seperate them and convert to a list.
+
+    Parameters
+    ----------
+    mail : list of str or str
+        Contains email addresses for notifications.
+        It might also contain just the ecmwf user name which will trigger
+        mailing to the associated email address for this user.
+
+    Return
+    ------
+    mail : list of str
+        Contains email addresses for notifications.
+        It might also contain just the ecmwf user name which will trigger
+        mailing to the associated email address for this user.
+
+    '''
+    if not isinstance(mail, list):
+        if ',' in mail:
+            mail = mail.split(',')
+        elif ' ' in mail:
+            mail = mail.split()
+        else:
+            mail = [mail]
+
+    return mail
+
+def check_queue(queue, gateway, destination, ecuid, ecgid):
+    '''Check if the necessary ECMWF parameters are set if the queue is
+    one of the QUEUES_LIST (in _config).
+
+    Parameters
+    ----------
+    queue : str
+        Name of the queue if submitted to the ECMWF servers.
+        Used to check if ecuid, ecgid, gateway and destination
+        are set correctly and are not empty.
+
+    gateway : str
+        The address of the gateway server.
+
+    destination : str
+        The name of the destination of the gateway server for data
+        transfer through ectrans. E.g. name@genericSftp
+
+    ecuid : str
+        ECMWF user id.
+
+    ecgid : str
+        ECMWF group id.
+
+    Return
+    ------
+
+    '''
+    if queue in _config.QUEUES_LIST and \
+            (not gateway or not destination or
+             not ecuid or not ecgid):
+        raise ValueError('\nEnvironment variables GATEWAY, DESTINATION, ECUID '
+                         'and ECGID were not set properly! \n '
+                         'Please check for existence of file "ECMWF_ENV" '
+                         'in the run directory!')
+    return
+
+def check_pathes(idir, odir, fpdir, fedir):
+    '''Check if output and flexpart pathes are set.
+
+    Parameters
+    ----------
+    idir : str
+        Path to the temporary directory for MARS retrieval data.
+
+    odir : str
+        Path to the final output directory where the FLEXPART input files
+        will be stored.
+
+    fpdir : str
+        Path to FLEXPART root directory.
+
+    fedir : str
+        Path to flex_extract root directory.
+
+    Return
+    ------
+    odir : str
+        Path to the final output directory where the FLEXPART input files
+        will be stored.
+
+    fpdir : str
+        Path to FLEXPART root directory.
+
+    '''
+    if not fpdir:
+        fpdir = fedir
+
+    if not odir:
+        odir = idir
+
+    return odir, fpdir
+
+def check_dates(start, end):
+    '''Checks if there is at least a start date for a one day retrieval.
+
+    Checks if end date lies after start date and end date is set.
+
+    Parameters
+    ----------
+    start : str
+        The start date of the retrieval job.
+
+    end : str
+        The end date of the retrieval job.
+
+    Return
+    ------
+    start : str
+        The start date of the retrieval job.
+
+    end : str
+        The end date of the retrieval job.
+
+    '''
+    # check for having at least a starting date
+    # otherwise program is not allowed to run
+    if not start:
+        raise ValueError('start_date was neither specified in command line nor '
+                         'in CONTROL file.\n'
+                         'Try "{} -h" to print usage information'
+                         .format(sys.argv[0].split('/')[-1]) )
+
+    # retrieve just one day if end_date isn't set
+    if not end:
+        end = start
+
+    dstart = datetime.strptime(start, '%Y%m%d')
+    dend = datetime.strptime(end, '%Y%m%d')
+    if dstart > dend:
+        raise ValueError('ERROR: Start date is after end date! \n'
+                         'Please adapt the dates in CONTROL file or '
+                         'command line! (start={}; end={})'.format(start, end))
+
+    return start, end
+
+def check_maxstep(maxstep, steps):
+    '''Convert maxstep into integer if it is already given. Otherwise, select
+    maxstep by going through the steps list.
+
+    Parameters
+    ----------
+    maxstep : str
+        The maximum forecast time step in hours from the forecast base time.
+        This is the maximum step for non flux (accumulated) forecast data.
+
+    steps : str
+        Specifies the forecast time step from forecast base time.
+        Valid values are hours (HH) from forecast base time.
+
+    Return
+    ------
+    maxstep : int
+        The maximum forecast time step in hours from the forecast base time.
+        This is the maximum step for non flux (accumulated) forecast data.
+
+    '''
+    # if maxstep wasn't provided
+    # search for it in the "step" parameter
+    if not maxstep:
+        maxstep = 0
+        for s in steps:
+            if int(s) > maxstep:
+                maxstep = int(s)
+    else:
+        maxstep = int(maxstep)
+
+    return maxstep
+
+def check_basetime(basetime):
+    '''Check if basetime is set and contains one of the two
+    possible values (0, 12).
+
+    Parameters
+    ----------
+    basetime : int or str or None
+        The time for a half day retrieval. The 12 hours upfront are to be
+        retrieved.
+
+    Return
+    ------
+    basetime : int or None
+        The time for a half day retrieval. The 12 hours upfront are to be
+        retrieved.
+    '''
+    if basetime is not None:
+        basetime = int(basetime)
+        if basetime != 0 and basetime != 12:
+            raise ValueError('ERROR: Basetime has an invalid value '
+                             '-> {}'.format(str(basetime)))
+    return basetime
+
+def check_request(request, marsfile):
+    '''Check if there is an old mars request file and remove it.
+
+    Parameters
+    ----------
+    request : int
+        Selects the mode of retrieval.
+        0: Retrieves the data from ECMWF.
+        1: Prints the mars requests to an output file.
+        2: Retrieves the data and prints the mars request.
+
+    marsfile : str
+        Path to the mars request file.
+
+    Return
+    ------
+
+    '''
+    if request != 0:
+        if os.path.isfile(marsfile):
+            silent_remove(marsfile)
+    return
+
+def check_public(public, dataset):
+    '''Check wether the dataset parameter is set for a
+    public data set retrieval.
+
+    Parameters
+    ----------
+    public : int
+        Specifies if public data are to be retrieved or not.
+
+    dataset : str
+        Specific name which identifies the public dataset.
+
+    Return
+    ------
+
+    '''
+    if public and not dataset:
+        raise ValueError('ERROR: If public mars data wants to be retrieved, '
+                         'the "dataset"-parameter has to be set too!')
+    return
+
+def check_acctype(acctype, ftype):
+    '''Guarantees that the accumulation field type is set.
+
+    If not set, it is derivated as in the old method (TYPE[1]).
+
+    Parameters
+    ----------
+    acctype : str
+        The field type for the accumulated forecast fields.
+
+    ftype : list of str
+        List of field types.
+
+    Return
+    ------
+    acctype : str
+        The field type for the accumulated forecast fields.
+    '''
+    if not acctype:
+        print('... Control parameter ACCTYPE was not defined.')
+        try:
+            if len(ftype) == 1 and ftype[0] != 'AN':
+                print('... Use same field type as for the non-flux fields.')
+                acctype = ftype[0]
+            elif len(ftype) > 1 and ftype[1] != 'AN':
+                print('... Use old setting by using TYPE[1] for flux forecast!')
+                acctype = ftype[1]
+        except:
+            raise ValueError('ERROR: Accumulation field type could not be set!')
+    else:
+        if acctype.upper() == 'AN':
+            raise ValueError('ERROR: Accumulation forecast fields can not be '
+                             'of type "analysis"!')
+    return acctype
+
+
+def check_acctime(acctime, marsclass, purefc, time):
+    '''Guarantees that the accumulation forecast times were set.
+
+    If it is not set, it tries to set the value for some of the
+    most commonly used data sets. Otherwise it raises an error.
+
+    Parameters
+    ----------
+    acctime : str
+        The starting time from the accumulated forecasts.
+
+    marsclass : str
+        ECMWF data classification identifier.
+
+    purefc : int
+        Switch for definition of pure forecast mode or not.
+
+    Return
+    ------
+    acctime : str
+        The starting time from the accumulated forecasts.
+    '''
+
+    if not acctime:
+        print('... Control parameter ACCTIME was not defined.')
+        print('... Value will be set depending on field type:\n '
+              '\t\t EA=06/18\n\t\t EI/OD=00/12\n\t\t EP=18')
+        if marsclass.upper() == 'EA': # Era 5
+            acctime = '06/18'
+        elif marsclass.upper() == 'EI': # Era-Interim
+            acctime = '00/12'
+        elif marsclass.upper() == 'EP': # CERA
+            acctime = '18'
+        elif marsclass.upper() == 'OD' and not purefc: # On-demand
+            acctime = '00/12'
+        elif marsclass.upper() == 'OD' and purefc: # On-demand
+            acctime = time[0]
+        else:
+            raise ValueError('ERROR: Accumulation forecast time can not '
+                             'automatically be derived!')
+    return acctime
+
+def check_accmaxstep(accmaxstep, marsclass, purefc, maxstep):
+    '''Guarantees that the accumulation forecast step were set.
+
+    Parameters
+    ----------
+    accmaxstep : str
+        The maximum forecast step for the accumulated forecast fields.
+
+    marsclass : str
+        ECMWF data classification identifier.
+
+    purefc : int
+        Switch for definition of pure forecast mode or not.
+
+    maxstep : str
+        The maximum forecast time step in hours from the forecast base time.
+        This is the maximum step for non flux (accumulated) forecast data.
+
+    Return
+    ------
+    accmaxstep : str
+        The maximum forecast step for the accumulated forecast fields.
+    '''
+    if not accmaxstep:
+        print('... Control parameter ACCMAXSTEP was not defined.')
+        print('... Value will be set depending on field type/time: '
+              '\n\t\t EA/EI/OD=12\n\t\t EP=24')
+        if marsclass.upper() in ['EA', 'EI', 'OD'] and not purefc:
+            # Era 5, Era-Interim, On-demand operational
+            accmaxstep = '12'
+        elif marsclass.upper() == 'EP': # CERA
+            accmaxstep = '24'
+        elif purefc and accmaxstep != maxstep:
+            accmaxstep = maxstep
+            print('... For pure forecast mode, the accumulated forecast must '
+                  'have the same maxstep as the normal forecast fields!\n'
+                  '\t\t Accmaxstep was set to maxstep!')
+        else:
+            raise ValueError('ERROR: Accumulation forecast step can not '
+                             'automatically be derived!')
+    else:
+        if purefc and int(accmaxstep) != int(maxstep):
+            accmaxstep = maxstep
+            print('... For pure forecast mode, the accumulated forecast must '
+                          'have the same maxstep as the normal forecast fields!\n'
+                          '\t\t Accmaxstep was set to maxstep!')
+    return accmaxstep
+
+def check_addpar(addpar):
+    '''Check that addpar has correct format of additional parameters in
+    a single string, so that it can be easily appended to the hard coded
+    parameters that are retrieved in any case.
+
+    Parameters
+    ----------
+    addpar : str or list of str
+        List of additional parameters to be retrieved.
+
+    Return
+    ------
+    addpar : str
+        List of additional parameters to be retrieved.
+    '''
+
+    if addpar and isinstance(addpar, str):
+        if '/' in addpar:
+            parlist = addpar.split('/')
+            parlist = [p for p in parlist if p is not '']
+        else:
+            parlist = [addpar]
+
+        addpar = '/' + '/'.join(parlist)
+
+    return addpar
+
+
+def check_job_chunk(job_chunk):
+    '''Checks that if job chunk is set, the number is positive and non zero.
+
+    Parameters
+    ----------
+    job_chunk : int
+        The number of days for a single job script.
+
+    Return
+    ------
+    job_chunk : int
+        The number of days for a single job script.
+    '''
+    if not job_chunk:
+        return job_chunk
+    else:
+        job_chunk = int(job_chunk)
+
+    if job_chunk < 0:
+        raise ValueError('ERROR: The number of job chunk is negative!\n'
+                         'It has to be a positive number!')
+    elif job_chunk == 0:
+        job_chunk = None
+    else:
+        pass
+
+    return job_chunk
+
+
+def check_number(number, mailfail):
+    '''Check for correct string format of ensemble member numbers.
+
+    Parameters
+    ----------
+    number : str
+        List of ensemble member forecast runs.
+
+    mailfail : list of str
+        Contains all email addresses which should be notified.
+        It might also contain just the ecmwf user name which will trigger
+        mailing to the associated email address for this user.
+
+    Return
+    ------
+    number : str
+        String with list of ensemble member forecast runs. E.g. '01/02/03/04'
+    '''
+
+    if '/' in number:
+        numbers = number.split('/')
+        if 'to' in number.lower() and 'by' in number.lower():
+            number = '{:0>3}'.format(int(numbers[0])) + '/TO/' + \
+                     '{:0>3}'.format(int(numbers[2])) + '/BY/' + \
+                     '{:0>3}'.format(int(numbers[4]))
+        elif 'to' in number.lower() and 'by' not in number.lower():
+            number = '{:0>3}'.format(int(numbers[0])) + '/TO/' + \
+                     '{:0>3}'.format(int(numbers[2]))
+        else:
+            numbers = ['{:0>3}'.format(i) for i in numbers]
+            number = '{:0>3}/'.join(numbers)
+    elif number.isdigit():
+        number = '{:0>3}'.format(int(number))
+    else:
+        pass
+
+    return number
Index: Source/Python/Mods/disaggregation.py
===================================================================
--- Source/Python/Mods/disaggregation.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Mods/disaggregation.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,416 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: March 2018
+#
+# @Change History:
+#
+#    November 2015 - Leopold Haimberger (University of Vienna):
+#        - migration of the methods dapoly and darain from Fortran
+#          (flex_extract_v6 and earlier) to Python
+#
+#    April 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added structured documentation
+#        - outsourced the disaggregation functions dapoly and darain
+#          to a new module named disaggregation
+#        - added the new disaggregation method for precipitation
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#
+# @Methods:
+#    - dapoly
+#    - darain
+#    - IA3
+#*******************************************************************************
+'''Disaggregation of deaccumulated flux data from an ECMWF model FG field.
+
+Initially the flux data to be concerned are:
+    - large-scale precipitation
+    - convective precipitation
+    - surface sensible heat flux
+    - surface solar radiation
+    - u stress
+    - v stress
+
+Different versions of disaggregation is provided for rainfall
+data (darain, modified linear) and the surface fluxes and
+stress data (dapoly, cubic polynomial).
+'''
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
+# FUNCTIONS
+# ------------------------------------------------------------------------------
+def dapoly(alist):
+    """Cubic polynomial interpolation of deaccumulated fluxes.
+
+    Interpolation of deaccumulated fluxes of an ECMWF model FG field
+    using a cubic polynomial solution which conserves the integrals
+    of the fluxes within each timespan.
+    Disaggregation is done for 4 accumluated timespans which
+    generates a new, disaggregated value which is output at the
+    central point of the 4 accumulation timespans.
+    This new point is used for linear interpolation of the complete
+    timeseries afterwards.
+
+    Parameters
+    ----------
+    alist : list of array of float
+        List of 4 timespans as 2-dimensional, horizontal fields.
+        E.g. [[array_t1], [array_t2], [array_t3], [array_t4]]
+
+    Return
+    ------
+    nfield : array of float
+        Interpolated flux at central point of accumulation timespan.
+
+    Note
+    ----
+    March 2000    : P. JAMES
+        Original author
+
+    June 2003     : A. BECK
+        Adaptations
+
+    November 2015 : Leopold Haimberger (University of Vienna)
+        Migration from Fortran to Python
+
+    """
+
+    pya = (alist[3] - alist[0] + 3. * (alist[1] - alist[2])) / 6.
+    pyb = (alist[2] + alist[0]) / 2. - alist[1] - 9. * pya / 2.
+    pyc = alist[1] - alist[0] - 7. * pya / 2. - 2. * pyb
+    pyd = alist[0] - pya / 4. - pyb / 3. - pyc / 2.
+    nfield = 8. * pya + 4. * pyb + 2. * pyc + pyd
+
+    return nfield
+
+
+def darain(alist):
+    """Linear interpolation of deaccumulated fluxes.
+
+    Interpolation of deaccumulated fluxes of an ECMWF model FG rainfall
+    field using a modified linear solution which conserves the integrals
+    of the fluxes within each timespan.
+    Disaggregation is done for 4 accumluated timespans which generates
+    a new, disaggregated value which is output at the central point
+    of the 4 accumulation timespans. This new point is used for linear
+    interpolation of the complete timeseries afterwards.
+
+    Parameters
+    ----------
+    alist : list of array of float
+        List of 4 timespans as 2-dimensional, horizontal fields.
+        E.g. [[array_t1], [array_t2], [array_t3], [array_t4]]
+
+    Return
+    ------
+    nfield : array of float
+        Interpolated flux at central point of accumulation timespan.
+
+    Note
+    ----
+    March 2000    : P. JAMES
+        Original author
+
+    June 2003     : A. BECK
+        Adaptations
+
+    November 2015 : Leopold Haimberger (University of Vienna)
+        Migration from Fortran to Python
+    """
+
+    xa = alist[0]
+    xb = alist[1]
+    xc = alist[2]
+    xd = alist[3]
+    xa[xa < 0.] = 0.
+    xb[xb < 0.] = 0.
+    xc[xc < 0.] = 0.
+    xd[xd < 0.] = 0.
+
+    xac = 0.5 * xb
+    mask = xa + xc > 0.
+    xac[mask] = xb[mask] * xc[mask] / (xa[mask] + xc[mask])
+    xbd = 0.5 * xc
+    mask = xb + xd > 0.
+    xbd[mask] = xb[mask] * xc[mask] / (xb[mask] + xd[mask])
+    nfield = xac + xbd
+
+    return nfield
+
+def IA3(g):
+    """ Interpolation with a non-negative geometric mean based algorithm.
+
+    The original grid is reconstructed by adding two sampling points in each
+    data series interval. This subgrid is used to keep all information during
+    the interpolation within the associated interval. Additionally, an advanced
+    monotonicity filter is applied to improve the monotonicity properties of
+    the series.
+
+    Note
+    ----
+    (C) Copyright 2017-2019
+    Sabine Hittmeir, Anne Philipp, Petra Seibert
+
+    This work is licensed under the Creative Commons Attribution 4.0
+    International License. To view a copy of this license, visit
+    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+
+    Parameters
+    ----------
+    g : list of float
+        Complete data series that will be interpolated having
+        the dimension of the original raw series.
+
+    Return
+    ------
+    f : list of float
+        The interpolated data series with additional subgrid points.
+        Its dimension is equal to the length of the input data series
+        times three.
+
+
+    References
+    ----------
+    For more information see article:
+    Hittmeir, S.; Philipp, A.; Seibert, P. (2017): A conservative
+    interpolation scheme for extensive quantities with application to the
+    Lagrangian particle dispersion model FLEXPART.,
+    Geoscientific Model Development
+    """
+
+    #######################  variable description #############################
+    #                                                                         #
+    # i      - index variable for looping over the data series                #
+    # g      - input data series                                              #
+    # f      - interpolated and filtered data series with additional          #
+    #          grid points                                                    #
+    # fi     - function value at position i, f_i                              #
+    # fi1    - first  sub-grid function value f_i^1                           #
+    # fi2    - second sub-grid function value f_i^2                           #
+    # fip1   - next function value at position i+1, f_(i+1)                   #
+    # dt     - time step                                                      #
+    # fmon   - monotonicity filter                                            #
+    #                                                                         #
+    ###########################################################################
+
+
+    import numpy as np
+
+    # time step
+    dt = 1.0
+
+    ############### Non-negative Geometric Mean Based Algorithm ###############
+
+    # for the left boundary the following boundary condition is valid:
+    # the value at t=0 of the interpolation algorithm coincides with the
+    # first data value according to the persistence hypothesis
+    f = [g[0]]
+
+    # compute two first sub-grid intervals without monotonicity check
+    # go through the data series and extend each interval by two sub-grid
+    # points and interpolate the corresponding data values
+    # except for the last interval due to boundary conditions
+    for i in range(0, 2):
+
+        # as a requirement:
+        # if there is a zero data value such that g[i]=0, then the whole
+        # interval in f has to be zero to such that f[i+1]=f[i+2]=f[i+3]=0
+        # according to Eq. (6)
+        if g[i] == 0.:
+            f.extend([0., 0., 0.])
+
+        # otherwise the sub-grid values are calculated and added to the list
+        else:
+            # temporal save of last value in interpolated list
+            # since it is the left boundary and hence the new (fi) value
+            fi = f[-1]
+
+            # the value at the end of the interval (fip1) is prescribed by the
+            # geometric mean, restricted such that non-negativity is guaranteed
+            # according to Eq. (25)
+            fip1=min( 3.*g[i] , 3.*g[i+1] , np.sqrt(g[i+1]*g[i]) )
+
+            # the function value at the first sub-grid point (fi1) is determined
+            # according to the equal area condition with Eq. (19)
+            fi1 = 3./2.*g[i]-5./12.*fip1-1./12.*fi
+
+            # the function value at the second sub-grid point (fi2) is determined
+            # according Eq. (18)
+            fi2 = fi1+1./3.*(fip1-fi)
+
+            # add next interval of interpolated (sub-)grid values
+            f.append(fi1)
+            f.append(fi2)
+            f.append(fip1)
+
+    # compute rest of the data series intervals
+    # go through the data series and extend each interval by two sub-grid
+    # points and interpolate the corresponding data values
+    # except for the last interval due to boundary conditions
+    for i in range(2, len(g)-1):
+
+        # as a requirement:
+        # if there is a zero data value such that g[i]=0, then the whole
+        # interval in f has to be zero to such that f[i+1]=f[i+2]=f[i+3]=0
+        # according to Eq. (6)
+        if g[i] == 0.:
+            # apply monotonicity filter for interval before
+            # check if there is "M" or "W" shape
+            if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5]) == -1 \
+               and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4]) == -1 \
+               and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3]) == -1:
+
+                # the monotonicity filter corrects the value at (fim1) by
+                # substituting (fim1) with (fmon), see Eq. (27), (28) and (29)
+                fmon = min(3.*g[i-2], \
+                           3.*g[i-1], \
+                           np.sqrt(max(0,(18./13.*g[i-2] - 5./13.*f[-7]) *
+                                         (18./13.*g[i-1] - 5./13.*f[-1]))))
+
+                # recomputation of the sub-grid interval values while the
+                # interval boundaries (fi) and (fip2) remains unchanged
+                # see Eq. (18) and (19)
+                f[-4] = fmon
+                f[-6] = 3./2.*g[i-2]-5./12.*fmon-1./12.*f[-7]
+                f[-5] = f[-6]+(fmon-f[-7])/3.
+                f[-3] = 3./2.*g[i-1]-5./12.*f[-1]-1./12.*fmon
+                f[-2] = f[-3]+(f[-1]-fmon)/3.
+
+            f.extend([0.,0.,0.])
+
+        # otherwise the sub-grid values are calculated and added to the list
+        else:
+            # temporal save of last value in interpolated list
+            # since it is the left boundary and hence the new (fi) value
+            fi = f[-1]
+
+            # the value at the end of the interval (fip1) is prescribed by the
+            # geometric mean, restricted such that non-negativity is guaranteed
+            # according to Eq. (25)
+            fip1 = min( 3.*g[i] , 3.*g[i+1] , np.sqrt(g[i+1]*g[i]) )
+
+            # the function value at the first sub-grid point (fi1) is determined
+            # according to the equal area condition with Eq. (19)
+            fi1 = 3./2.*g[i]-5./12.*fip1-1./12.*fi
+
+            # the function value at the second sub-grid point (fi2) is determined
+            # according Eq. (18)
+            fi2 = fi1+1./3.*(fip1-fi)
+
+            # apply monotonicity filter for interval before
+            # check if there is "M" or "W" shape
+            if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5]) == -1 \
+               and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4]) == -1 \
+               and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3]) == -1:
+
+                # the monotonicity filter corrects the value at (fim1) by
+                # substituting (fim1) with fmon, see Eq. (27), (28) and (29)
+                fmon = min(3.*g[i-2], \
+                           3.*g[i-1], \
+                           np.sqrt(max(0,(18./13.*g[i-2] - 5./13.*f[-7]) *
+                                         (18./13.*g[i-1] - 5./13.*f[-1]))))
+
+                # recomputation of the sub-grid interval values while the
+                # interval boundaries (fi) and (fip2) remains unchanged
+                # see Eq. (18) and (19)
+                f[-4] = fmon
+                f[-6] = 3./2.*g[i-2]-5./12.*fmon-1./12.*f[-7]
+                f[-5] = f[-6]+(fmon-f[-7])/3.
+                f[-3] = 3./2.*g[i-1]-5./12.*f[-1]-1./12.*fmon
+                f[-2] = f[-3]+(f[-1]-fmon)/3.
+
+            # add next interval of interpolated (sub-)grid values
+            f.append(fi1)
+            f.append(fi2)
+            f.append(fip1)
+
+    # separate treatment of the final interval
+
+    # as a requirement:
+    # if there is a zero data value such that g[i]=0, then the whole
+    # interval in f has to be zero to such that f[i+1]=f[i+2]=f[i+3]=0
+    # according to Eq. (6)
+    if g[-1] == 0.:
+        # apply monotonicity filter for interval before
+        # check if there is "M" or "W" shape
+        if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5]) == -1 \
+           and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4]) == -1 \
+           and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3]) == -1:
+
+            # the monotonicity filter corrects the value at (fim1) by
+            # substituting (fim1) with (fmon), see Eq. (27), (28) and (29)
+            fmon = min(3.*g[-3], \
+                       3.*g[-2], \
+                       np.sqrt(max(0,(18./13.*g[-3] - 5./13.*f[-7]) *
+                                     (18./13.*g[-2] - 5./13.*f[-1]))))
+
+            # recomputation of the sub-grid interval values while the
+            # interval boundaries (fi) and (fip2) remains unchanged
+            # see Eq. (18) and (19)
+            f[-4] = fmon
+            f[-6] = 3./2.*g[-3]-5./12.*fmon-1./12.*f[-7]
+            f[-5] = f[-6]+(fmon-f[-7])/3.
+            f[-3] = 3./2.*g[-2]-5./12.*f[-1]-1./12.*fmon
+            f[-2] = f[-3]+(f[-1]-fmon)/3.
+
+        f.extend([0.,0.,0.])
+
+    # otherwise the sub-grid values are calculated and added to the list
+    # using the persistence hypothesis as boundary condition
+    else:
+        # temporal save of last value in interpolated list
+        # since it is the left boundary and hence the new (fi) value
+        fi = f[-1]
+        # since last interval in series, last value is also fip1
+        fip1 = g[-1]
+        # the function value at the first sub-grid point (fi1) is determined
+        # according to the equal area condition with Eq. (19)
+        fi1 = 3./2.*g[-1]-5./12.*fip1-1./12.*fi
+        # the function value at the second sub-grid point (fi2) is determined
+        # according Eq. (18)
+        fi2 = fi1+dt/3.*(fip1-fi)
+
+        # apply monotonicity filter for interval before
+        # check if there is "M" or "W" shape
+        if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5]) == -1 \
+           and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4]) == -1 \
+           and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3]) == -1:
+
+            # the monotonicity filter corrects the value at (fim1) by
+            # substituting (fim1) with (fmon), see Eq. (27), (28) and (29)
+            fmon = min(3.*g[-3], \
+                       3.*g[-2], \
+                       np.sqrt(max(0,(18./13.*g[-3] - 5./13.*f[-7]) *
+                                     (18./13.*g[-2] - 5./13.*f[-1]))))
+
+            # recomputation of the sub-grid interval values while the
+            # interval boundaries (fi) and (fip2) remains unchanged
+            # see Eq. (18) and (19)
+            f[-4] = fmon
+            f[-6] = 3./2.*g[-3]-5./12.*fmon-1./12.*f[-7]
+            f[-5] = f[-6]+(fmon-f[-7])/3.
+            f[-3] = 3./2.*g[-2]-5./12.*f[-1]-1./12.*fmon
+            f[-2] = f[-3]+(f[-1]-fmon)/3.
+
+        # add next interval of interpolated (sub-)grid values
+        f.append(fi1)
+        f.append(fi2)
+        f.append(fip1)
+
+    return f
Index: Source/Python/Mods/get_mars_data.py
===================================================================
--- Source/Python/Mods/get_mars_data.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Mods/get_mars_data.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Fouilloux (University of Oslo)
+#
+# @Date: October 2014
+#
+# @Change History:
+#
+#    November 2015 - Leopold Haimberger (University of Vienna):
+#        - moved the getEIdata program into a function "get_mars_data"
+#        - moved the AgurmentParser into a seperate function
+#        - adatpted the function for the use in flex_extract
+#        - renamed file to get_mars_data
+#
+#    February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added structured documentation
+#        - minor changes in programming style for consistence
+#        - added function main and moved function calls vom __main__ there
+#          (necessary for better documentation with docstrings for later
+#          online documentation)
+#        - use of UIFiles class for file selection and deletion
+#        - seperated get_mars_data function into several smaller pieces:
+#          write_reqheader, mk_server, mk_dates, remove_old, do_retrievment
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+'''This script extracts MARS data from ECMWF servers.
+
+At first, the necessary parameters from command line and CONTROL files are
+extracted. They define the data set to be extracted from MARS.
+
+This file can also be imported as a module and contains the following
+functions:
+
+    * main - the main function of the script
+    * get_mars_data - overall control of ECMWF data retrievment
+    * write_reqheader - writes the header into the mars_request file
+    * mk_server - creates the server connection to ECMWF servers
+    * mk_dates - defines the start and end date
+    * remove_old - deletes old retrieved grib files
+    * do_retrievement - creates individual retrievals
+
+Type: get_mars_data.py --help
+to get information about command line parameters.
+Read the documentation for usage instructions.
+'''
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+import sys
+import inspect
+from datetime import datetime, timedelta
+
+# software specific classes and modules from flex_extract
+# add path to local main python path for flex_extract to get full access
+sys.path.append(os.path.dirname(os.path.abspath(
+    inspect.getfile(inspect.currentframe()))) + '/../')
+import _config
+from Mods.tools import (setup_controldata, my_error, normal_exit, get_cmdline_args,
+                   read_ecenv, make_dir)
+from Classes.EcFlexpart import EcFlexpart
+from Classes.UioFiles import UioFiles
+from Classes.MarsRetrieval import MarsRetrieval
+
+try:
+    ec_api = True
+    import ecmwfapi
+except ImportError:
+    ec_api = False
+
+try:
+    cds_api = True
+    import cdsapi
+except ImportError:
+    cds_api = False
+# ------------------------------------------------------------------------------
+# FUNCTION
+# ------------------------------------------------------------------------------
+def main():
+    '''Controls the program to get data out of mars.
+
+    This is done if it is called directly from command line.
+    Then it also takes program call arguments and control file input.
+
+    Parameters
+    ----------
+
+    Return
+    ------
+
+    '''
+
+    c, _, _, _ = setup_controldata()
+    get_mars_data(c)
+    normal_exit('Retrieving MARS data: Done!')
+
+    return
+
+def get_mars_data(c):
+    '''Retrieves the EC data needed for a FLEXPART simulation.
+
+    Start and end dates for retrieval period is set. Retrievals
+    are divided into smaller periods if necessary and datechunk parameter
+    is set.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    Return
+    ------
+
+    '''
+    c.ec_api = ec_api
+    c.cds_api = cds_api
+
+    if not os.path.exists(c.inputdir):
+        make_dir(c.inputdir)
+
+    if c.request == 0:
+        print("Retrieving EC data!")
+    else:
+        if c.request == 1:
+            print("Printing mars requests!")
+        elif c.request == 2:
+            print("Retrieving EC data and printing mars request!")
+        write_reqheader(os.path.join(c.inputdir, _config.FILE_MARS_REQUESTS))
+
+    print("start date %s " % (c.start_date))
+    print("end date %s " % (c.end_date))
+
+    server = mk_server(c)
+
+    # if data are to be retrieved, clean up any old grib files
+    if c.request == 0 or c.request == 2:
+        remove_old('*grb', c.inputdir)
+
+    # --------------  flux data ------------------------------------------------
+    start, end, datechunk = mk_dates(c, fluxes=True)
+    do_retrievement(c, server, start, end, datechunk, fluxes=True)
+
+    # --------------  non flux data --------------------------------------------
+    start, end, datechunk = mk_dates(c, fluxes=False)
+    do_retrievement(c, server, start, end, datechunk, fluxes=False)
+
+    return
+
+def write_reqheader(marsfile):
+    '''Writes header with column names into mars request file.
+
+    Parameters
+    ----------
+    marsfile : str
+        Path to the mars request file.
+
+    Return
+    ------
+
+    '''
+    MR = MarsRetrieval(None, None)
+    attrs = vars(MR).copy()
+    del attrs['server']
+    del attrs['public']
+    with open(marsfile, 'w') as f:
+        f.write('request_number' + ', ')
+        f.write(', '.join(str(key) for key in sorted(attrs.keys())))
+        f.write('\n')
+
+    return
+
+def mk_server(c):
+    '''Creates a server connection with available python API.
+
+    Which API is used depends on availability and the dataset to be retrieved.
+    The CDS API is used for ERA5 dataset no matter if the user is a member or
+    a public user. ECMWF WebAPI is used for all other available datasets.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    Return
+    ------
+    server : ECMWFDataServer, ECMWFService or Client
+        Connection to ECMWF server via python interface ECMWF WebAPI or CDS API.
+
+    '''
+    if cds_api and (c.marsclass.upper() == 'EA'):
+        server = cdsapi.Client()
+        c.ec_api = False
+    elif c.ec_api:
+        if c.public:
+            server = ecmwfapi.ECMWFDataServer()
+        else:
+            server = ecmwfapi.ECMWFService("mars")
+        c.cds_api = False
+    else:
+        server = False
+
+    print('Using ECMWF WebAPI: ' + str(c.ec_api))
+    print('Using CDS API: ' + str(c.cds_api))
+
+    return server
+
+
+def check_dates_for_nonflux_fc_times(types, times):
+    '''
+    '''
+    for ty, ti in zip(types,times):
+        if ty.upper() == 'FC' and int(ti) == 18:
+            return True
+    return False
+
+
+def mk_dates(c, fluxes):
+    '''Prepares start and end date depending on flux or non flux data.
+
+    If forecast for maximum one day (upto 24h) are to be retrieved, then
+    collect accumulation data (flux data) with additional days in the
+    beginning and at the end (used for complete disaggregation of
+    original period)
+
+    If forecast data longer than 24h are to be retrieved, then
+    collect accumulation data (flux data) with the exact start and end date
+    (disaggregation will be done for the exact time period with
+    boundary conditions)
+
+    Since for basetime the extraction contains the 12 hours upfront,
+    if basetime is 0, the starting date has to be the day before and
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    fluxes : boolean, optional
+        Decides if the flux parameter settings are stored or
+        the rest of the parameter list.
+        Default value is False.
+
+    Return
+    ------
+    start : datetime
+        The start date of the retrieving data set.
+
+    end : datetime
+        The end date of the retrieving data set.
+
+    chunk : datetime
+        Time period in days for one single mars retrieval.
+
+    '''
+    start = datetime.strptime(c.start_date, '%Y%m%d')
+    end = datetime.strptime(c.end_date, '%Y%m%d')
+    chunk = timedelta(days=int(c.date_chunk))
+
+    if c.basetime == 0:
+        start = start - timedelta(days=1)
+
+    if c.purefc and fluxes and c.maxstep < 24:
+        start = start - timedelta(days=1)
+        end = end + timedelta(days=1)
+
+    if not c.purefc and fluxes and not c.basetime == 0:
+        start = start - timedelta(days=1)
+        end = end + timedelta(days=1)
+
+    # if we have non-flux forecast data starting at 18 UTC
+    # we need to start retrieving data one day in advance
+    if not fluxes and check_dates_for_nonflux_fc_times(c.type, c.time):
+        start = start - timedelta(days=1)
+
+    return start, end, chunk
+
+def remove_old(pattern, inputdir):
+    '''Deletes old retrieval files from current input directory
+    matching the pattern.
+
+    Parameters
+    ----------
+    pattern : str
+        The sub string pattern which identifies the files to be deleted.
+
+    inputdir : str, optional
+        Path to the directory where the retrieved data is stored.
+
+    Return
+    ------
+
+    '''
+    print('... removing old content of ' + inputdir)
+
+    tobecleaned = UioFiles(inputdir, pattern)
+    tobecleaned.delete_files()
+
+    return
+
+
+def do_retrievement(c, server, start, end, delta_t, fluxes=False):
+    '''Divides the complete retrieval period in smaller chunks and
+    retrieves the data from MARS.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    server : ECMWFService or ECMWFDataServer
+            The server connection to ECMWF.
+
+    start : datetime
+        The start date of the retrieval.
+
+    end : datetime
+        The end date of the retrieval.
+
+    delta_t : datetime
+        Delta_t + 1 is the maximal time period of a single
+        retrieval.
+
+    fluxes : boolean, optional
+        Decides if the flux parameters are to be retrieved or
+        the rest of the parameter list.
+        Default value is False.
+
+    Return
+    ------
+
+    '''
+
+    # since actual day also counts as one day,
+    # we only need to add datechunk - 1 days to retrieval
+    # for a period
+    delta_t_m1 = delta_t - timedelta(days=1)
+
+    day = start
+    while day <= end:
+        flexpart = EcFlexpart(c, fluxes)
+        tmpday = day + delta_t_m1
+        if tmpday < end:
+            dates = day.strftime("%Y%m%d") + "/to/" + \
+                    tmpday.strftime("%Y%m%d")
+        else:
+            dates = day.strftime("%Y%m%d") + "/to/" + \
+                    end.strftime("%Y%m%d")
+
+        print("... retrieve " + dates + " in dir " + c.inputdir)
+
+        try:
+            flexpart.retrieve(server, dates, c.public, c.request, c.inputdir)
+        except IOError:
+            my_error('MARS request failed')
+
+        day += delta_t
+
+    return
+
+if __name__ == "__main__":
+    main()
Index: Source/Python/Mods/prepare_flexpart.py
===================================================================
--- Source/Python/Mods/prepare_flexpart.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Mods/prepare_flexpart.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Fouilloux (University of Oslo)
+#
+# @Date: October 2014
+#
+# @Change History:
+#
+#    November 2015 - Leopold Haimberger (University of Vienna):
+#        - using the WebAPI also for general MARS retrievals
+#        - job submission on ecgate and cca
+#        - job templates suitable for twice daily operational dissemination
+#        - dividing retrievals of longer periods into digestable chunks
+#        - retrieve also longer term forecasts, not only analyses and
+#          short term forecast data
+#        - conversion into GRIB2
+#        - conversion into .fp format for faster execution of FLEXPART
+#
+#    February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - minor changes in programming style for consistence
+#        - BUGFIX: removed call of clean_up-Function after call of
+#               prepareFlexpart in main since it is already called in
+#               prepareFlexpart at the end!
+#        - created function main and moved the two function calls for
+#          arguments and prepare_flexpart into it
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+'''This script prepares the final version of the grib files which are
+then used by FLEXPART.
+
+It converts the bunch of grib files extracted via get_mars_data before,
+by doing the necessary conversion to get consistent grids or the
+disaggregation of flux data. Finally, the data fields are combined
+in files per available hour with the naming convention xxYYMMDDHH,
+where xx should be 2 arbitrary letters (mostly xx is chosen to be "EN").
+
+This file can also be imported as a module which then contains the following
+functions:
+
+    * main
+    * prepare_flexpart
+
+Type: prepare_flexpart.py --help
+to get information about command line parameters.
+Read the documentation for usage instructions.
+'''
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import datetime
+import os
+import inspect
+import sys
+import socket
+
+# software specific classes and modules from flex_extract
+# add path to local main python path for flex_extract to get full access
+sys.path.append(os.path.dirname(os.path.abspath(
+    inspect.getfile(inspect.currentframe()))) + '/../')
+import _config
+from Mods.checks import check_ppid
+from Classes.UioFiles import UioFiles
+from Classes.ControlFile import ControlFile
+from Mods.tools import (setup_controldata, clean_up, get_cmdline_args,
+                        read_ecenv, make_dir, normal_exit)
+from Classes.EcFlexpart import EcFlexpart
+
+# ------------------------------------------------------------------------------
+# FUNCTION
+# ------------------------------------------------------------------------------
+def main():
+    '''Controls the program to prepare flexpart input files from mars data.
+
+    This is done if it is called directly from command line.
+    Then it also takes program call arguments and control file input.
+
+    Parameters
+    ----------
+
+    Return
+    ------
+
+    '''
+
+    c, ppid, _, _ = setup_controldata()
+    prepare_flexpart(ppid, c)
+    normal_exit('Preparing FLEXPART output files: Done!')
+
+    return
+
+def prepare_flexpart(ppid, c):
+    '''Converts the mars data into flexpart ready input files.
+
+    Specific data fields are converted to a different grid and the flux
+    data are going to be disaggregated. The data fields are collected by
+    hour and stored in a file with a specific FLEXPART relevant naming
+    convention.
+
+    Parameters
+    ----------
+    ppid : int
+        Contains the ppid number of the current ECMWF job. It will be None if
+        the method was called within this module.
+
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    Return
+    ------
+
+    '''
+    check_ppid(c, ppid)
+
+    # create the start and end date
+    start = datetime.date(year=int(c.start_date[:4]),
+                          month=int(c.start_date[4:6]),
+                          day=int(c.start_date[6:]))
+
+    end = datetime.date(year=int(c.end_date[:4]),
+                        month=int(c.end_date[4:6]),
+                        day=int(c.end_date[6:]))
+
+    # if basetime is 00
+    # assign starting date minus 1 day
+    # since we need the 12 hours upfront
+    # (the day before from 12 UTC to current day 00 UTC)
+    if c.basetime == 0:
+        start = start - datetime.timedelta(days=1)
+
+    print('Prepare ' + start.strftime("%Y%m%d") +
+           "/to/" + end.strftime("%Y%m%d"))
+
+    # create output dir if necessary
+    if not os.path.exists(c.outputdir):
+        make_dir(c.outputdir)
+
+    # get all files with flux data to be deaccumulated
+    inputfiles = UioFiles(c.inputdir, '*OG_acc_SL*.' + str(c.ppid) + '.*')
+
+    # deaccumulate the flux data
+    flexpart = EcFlexpart(c, fluxes=True)
+    flexpart.write_namelist(c)
+    flexpart.deacc_fluxes(inputfiles, c)
+
+    # get a list of all other files
+    inputfiles = UioFiles(c.inputdir, '????__??.*' + str(c.ppid) + '.*')
+
+    # produce FLEXPART-ready GRIB files and process them -
+    # copy/transfer/interpolate them or make them GRIB2
+    flexpart = EcFlexpart(c, fluxes=False)
+    flexpart.create(inputfiles, c)
+    if c.stream.lower() == 'elda' and c.doubleelda:
+        flexpart.calc_extra_elda(c.inputdir, c.prefix)
+    flexpart.process_output(c)
+
+    # make use of a possible conversion to a
+    # specific flexpart binary format
+    if c.grib2flexpart:
+        flexpart.prepare_fp_files(c)
+
+    # check if in debugging mode, then store all files
+    # otherwise delete temporary files
+    if c.debug:
+        print('\nTemporary files left intact')
+    else:
+        clean_up(c)
+
+    return
+
+if __name__ == "__main__":
+    main()
Index: Source/Python/Mods/profiling.py
===================================================================
--- Source/Python/Mods/profiling.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Mods/profiling.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#************************************************************************
+# ToDo AP
+# - check license of book content
+#************************************************************************
+#*******************************************************************************
+#
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: March 2018
+#
+# @License:
+#    (C) Copyright 2018.
+#
+#    This software is licensed under the terms of the Apache Licence Version 2.0
+#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# @Program functionality:
+#    This module is not part of flex_extract. It is just used for testing and
+#    performance analysis of some functions.
+#
+# @Program Content:
+#    - timefn
+#
+#*******************************************************************************
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+from functools import wraps
+import time
+
+# ------------------------------------------------------------------------------
+# FUNCTION
+# ------------------------------------------------------------------------------
+def timefn(fn):
+    '''
+    @Description:
+        Decorator function. It takes the inner function as an argument.
+    '''
+    @wraps(fn)
+    def measure_time(*args, **kwargs):
+        '''
+        @Descripton:
+            Passes the arguments through fn for execution. Around the
+            execution of fn the time is captured to execute the fn function
+            and prints the result along with the function name.
+
+            This is taken from the book "High Performance Python" from
+            Micha Gorelick and Ian Ozsvald, O'Reilly publisher, 2014,
+            ISBN: 978-1-449-36159-4
+
+        @Input:
+            *args: undefined
+                A variable number of positional arguments.
+
+            **kwargs: undefined
+                A variable number of key/value arguments.
+
+        @Return:
+            <nothing>
+        '''
+
+        t1 = time.time()
+        result = fn(*args, **kwargs)
+        t2 = time.time()
+        print("@timefn:" + fn.__name__ + " took " + str(t2 - t1) + " seconds")
+
+        return result
+
+    return measure_time
Index: Source/Python/Mods/tools.py
===================================================================
--- Source/Python/Mods/tools.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/Mods/tools.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,905 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: May 2018
+#
+# @Change History:
+#    October 2014 - Anne Fouilloux (University of Oslo)
+#        - created functions silent_remove and product (taken from ECMWF)
+#
+#    November 2015 - Leopold Haimberger (University of Vienna)
+#        - created functions: interpret_args_and_control, clean_up
+#          my_error, normal_exit, init128, to_param_id
+#
+#    April - December 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - moved all non class methods from former file Flexparttools in here
+#        - seperated args and control interpretation
+#        - added functions get_list_as_string, read_ecenv, send_mail, make_dir,
+#          put_file_to_ecserver, submit_job_to_ecserver, get_informations,
+#          get_dimensions, execute_subprocess, none_or_int, none_or_str
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#
+# @Methods:
+#    none_or_str
+#    none_or_int
+#    get_cmdline_args
+#    read_ecenv
+#    clean_up
+#    my_error
+#    send_mail
+#    normal_exit
+#    product
+#    silent_remove
+#    init128
+#    to_param_id
+#    get_list_as_string
+#    make_dir
+#    put_file_to_ecserver
+#    submit_job_to_ecserver
+#    get_informations
+#    get_dimensions
+#    execute_subprocess
+#*******************************************************************************
+'''This module contains a collection of diverse tasks within flex_extract.
+'''
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+import errno
+import sys
+import glob
+import subprocess
+import traceback
+try:
+    import exceptions
+except ImportError:
+    import builtins as exceptions
+from datetime import datetime, timedelta
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+
+# ------------------------------------------------------------------------------
+# METHODS
+# ------------------------------------------------------------------------------
+
+def setup_controldata():
+    '''Collects, stores and checks controlling arguments from command line,
+    CONTROL file and ECMWF_ENV file.
+
+    Parameters
+    ----------
+
+    Return
+    ------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    ppid : str
+        Parent process id.
+
+    queue : str
+        Name of queue for submission to ECMWF (e.g. ecgate or cca )
+
+    job_template : str
+        Name of the job template file for submission to ECMWF server.
+    '''
+    import _config
+    from Classes.ControlFile import ControlFile
+
+    args = get_cmdline_args()
+    c = ControlFile(args.controlfile)
+    c.assign_args_to_control(args)
+    if os.path.isfile(_config.PATH_ECMWF_ENV):
+        env_parameter = read_ecenv(_config.PATH_ECMWF_ENV)
+        c.assign_envs_to_control(env_parameter)
+    c.check_conditions(args.queue)
+
+    return c, args.ppid, args.queue, args.job_template
+
+def none_or_str(value):
+    '''Converts the input string into pythons None-type if the string
+    contains string "None".
+
+    Parameters
+    ----------
+    value : str
+        String to be checked for the "None" word.
+
+    Return
+    ------
+    None or value:
+        Return depends on the content of the input value. If it was "None",
+        then the python type None is returned. Otherwise the string itself.
+    '''
+    if value == 'None':
+        return None
+    return value
+
+def none_or_int(value):
+    '''Converts the input string into pythons None-type if the string
+    contains string "None". Otherwise it is converted to an integer value.
+
+    Parameters
+    ----------
+    value : str
+        String to be checked for the "None" word.
+
+    Return
+    ------
+    None or int(value):
+        Return depends on the content of the input value. If it was "None",
+        then the python type None is returned. Otherwise the string is
+        converted into an integer value.
+    '''
+    if value == 'None':
+        return None
+    return int(value)
+
+def get_cmdline_args():
+    '''Decomposes the command line arguments and assigns them to variables.
+    Apply default values for non mentioned arguments.
+
+    Parameters
+    ----------
+
+    Return
+    ------
+    args : Namespace
+        Contains the commandline arguments from script/program call.
+    '''
+
+    parser = ArgumentParser(description='Retrieve FLEXPART input from \
+                                ECMWF MARS archive',
+                            formatter_class=ArgumentDefaultsHelpFormatter)
+
+    # control parameters that override control file values
+    parser.add_argument("--start_date", dest="start_date",
+                        type=none_or_str, default=None,
+                        help="start date YYYYMMDD")
+    parser.add_argument("--end_date", dest="end_date",
+                        type=none_or_str, default=None,
+                        help="end_date YYYYMMDD")
+    parser.add_argument("--date_chunk", dest="date_chunk",
+                        type=none_or_int, default=None,
+                        help="# of days to be retrieved at once")
+    parser.add_argument("--job_chunk", dest="job_chunk",
+                        type=none_or_int, default=None,
+                        help="# of days to be retrieved within a single job")
+    parser.add_argument("--controlfile", dest="controlfile",
+                        type=none_or_str, default='CONTROL_EA5',
+                        help="The file with all CONTROL parameters.")
+    parser.add_argument("--basetime", dest="basetime",
+                        type=none_or_int, default=None,
+                        help="base such as 0 or 12 (for half day retrievals)")
+    parser.add_argument("--step", dest="step",
+                        type=none_or_str, default=None,
+                        help="Forecast steps such as 00/to/48")
+    parser.add_argument("--levelist", dest="levelist",
+                        type=none_or_str, default=None,
+                        help="Vertical levels to be retrieved, e.g. 30/to/60")
+    parser.add_argument("--area", dest="area",
+                        type=none_or_str, default=None,
+                        help="area defined as north/west/south/east")
+
+    # some switches
+    parser.add_argument("--debug", dest="debug",
+                        type=none_or_int, default=None,
+                        help="debug mode - leave temporary files intact")
+    parser.add_argument("--oper", dest="oper",
+                        type=none_or_int, default=None,
+                        help='operational mode - prepares dates with '
+                        'environment variables')
+    parser.add_argument("--request", dest="request",
+                        type=none_or_int, default=None,
+                        help="list all mars requests in file mars_requests.dat")
+    parser.add_argument("--public", dest="public",
+                        type=none_or_int, default=None,
+                        help="public mode - retrieves the public datasets")
+    parser.add_argument("--rrint", dest="rrint",
+                        type=none_or_int, default=None,
+                        help='Selection of old or new precipitation '
+                        'interpolation:\n'
+                        '     0 - old method\n'
+                        '     1 - new method (additional subgrid points)')
+
+    # set directories
+    parser.add_argument("--inputdir", dest="inputdir",
+                        type=none_or_str, default=None,
+                        help='Path to the temporary directory for the '
+                        'retrieval grib files and other processing files.')
+    parser.add_argument("--outputdir", dest="outputdir",
+                        type=none_or_str, default=None,
+                        help='Path to the final directory where the final '
+                        'FLEXPART ready input files are stored.')
+
+    # this is only used by prepare_flexpart.py to rerun a postprocessing step
+    parser.add_argument("--ppid", dest="ppid",
+                        type=none_or_str, default=None,
+                        help='This is the specify parent process id of a '
+                        'single flex_extract run to identify the files. '
+                        'It is the second number in the GRIB files.')
+
+    # arguments for job submission to ECMWF, only needed by submit.py
+    parser.add_argument("--job_template", dest='job_template',
+                        type=none_or_str, default="job.temp",
+                        help='The job template file which are adapted to be '
+                        'submitted to the batch system on ECMWF server.')
+    parser.add_argument("--queue", dest="queue",
+                        type=none_or_str, default=None,
+                        help='The ECMWF server name for submission of the '
+                        'job script to the batch system '
+                        '(e.g. ecgate | cca | ccb)')
+
+    args = parser.parse_args()
+
+    return args
+
+def read_ecenv(filepath):
+    '''Reads the file into a dictionary where the key values are the parameter
+    names.
+
+    Parameters
+    ----------
+    filepath : str
+        Path to file where the ECMWF environment parameters are stored.
+
+    Return
+    ------
+    envs : dict
+        Contains the environment parameter ecuid, ecgid, gateway
+        and destination for ECMWF server environments.
+    '''
+    envs= {}
+    try:
+        with open(filepath, 'r') as f:
+            for line in f:
+                data = line.strip().split()
+                envs[str(data[0])] = str(data[1])
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... Error occured while trying to read ECMWF_ENV '
+                     'file: ' + str(filepath))
+
+    return envs
+
+def clean_up(c):
+    '''Remove files from the intermediate directory (inputdir).
+
+    It keeps the final FLEXPART input files if program runs without
+    ECMWF Api and keywords "ectrans" or "ecstorage" are set to "1".
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    Return
+    ------
+
+    '''
+
+    print("... clean inputdir!")
+
+    cleanlist = [file for file in glob.glob(os.path.join(c.inputdir, "*"))
+                 if not os.path.basename(file).startswith(c.prefix)]
+
+    if cleanlist:
+        for element in cleanlist:
+            silent_remove(element)
+        print("... done!")
+    else:
+        print("... nothing to clean!")
+
+    return
+
+
+def my_error(message='ERROR'):
+    '''Prints a specified error message which can be passed to the function
+    before exiting the program.
+
+    Parameters
+    ----------
+    message : str, optional
+        Error message. Default value is "ERROR".
+
+    Return
+    ------
+
+    '''
+
+    trace = '\n'.join(traceback.format_stack())
+    full_message = message + '\n\n' + trace
+
+    print(full_message)
+
+    sys.exit(1)
+
+    return
+
+
+def send_mail(users, success_mode, message):
+    '''Prints a specific exit message which can be passed to the function.
+
+    Parameters
+    ----------
+    users : list of str
+        Contains all email addresses which should be notified.
+        It might also contain just the ecmwf user name which wil trigger
+        mailing to the associated email address for this user.
+
+    success_mode : str
+        States the exit mode of the program to put into
+        the mail subject line.
+
+    message : str, optional
+        Message for exiting program. Default value is "Done!".
+
+    Return
+    ------
+
+    '''
+
+    for user in users:
+        if '${USER}' in user:
+            user = os.getenv('USER')
+        try:
+            p = subprocess.Popen(['mail', '-s flex_extract_v7.1 ' +
+                                  success_mode, os.path.expandvars(user)],
+                                 stdin=subprocess.PIPE,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE,
+                                 bufsize=1)
+            pout = p.communicate(input=message.encode() + '\n\n')[0]
+        except ValueError as e:
+            print('... ERROR: ' + str(e))
+            sys.exit('... Email could not be sent!')
+        except OSError as e:
+            print('... ERROR CODE: ' + str(e.errno))
+            print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+            sys.exit('... Email could not be sent!')
+        else:
+            print('Email sent to ' + os.path.expandvars(user))
+
+    return
+
+
+def normal_exit(message='Done!'):
+    '''Prints a specific exit message which can be passed to the function.
+
+    Parameters
+    ----------
+    message : str, optional
+        Message for exiting program. Default value is "Done!".
+
+    Return
+    ------
+
+    '''
+
+    print(str(message))
+
+    return
+
+
+def product(*args, **kwds):
+    '''Creates combinations of all passed arguments.
+
+    This method combines the single characters of the passed arguments
+    with each other. So that each character of each argument value
+    will be combined with each character of the other arguments as a tuple.
+
+    Note
+    ----
+    This method is taken from an example at the ECMWF wiki website.
+    https://software.ecmwf.int/wiki/display/GRIB/index.py; 2018-03-16
+
+    Example
+    -------
+    product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
+
+    product(range(2), repeat = 3) --> 000 001 010 011 100 101 110 111
+
+    Parameters
+    ----------
+    \*args : list or str
+        Positional arguments (arbitrary number).
+
+    \*\*kwds : dict
+        Contains all the keyword arguments from \*args.
+
+    Return
+    ------
+    prod : :obj:`tuple`
+        Return will be done with "yield". A tuple of combined arguments.
+        See example in description above.
+    '''
+    try:
+        pools = [tuple(arg) for arg in args] * kwds.get('repeat', 1)
+        result = [[]]
+        for pool in pools:
+            result = [x + [y] for x in result for y in pool]
+        for prod in result:
+            yield tuple(prod)
+    except TypeError as e:
+        sys.exit('... PRODUCT GENERATION FAILED!')
+
+    return
+
+
+def silent_remove(filename):
+    '''Remove file if it exists.
+    The function does not fail if the file does not exist.
+
+    Parameters
+    ----------
+    filename : str
+        The name of the file to be removed without notification.
+
+    Return
+    ------
+
+    '''
+    try:
+        os.remove(filename)
+    except OSError as e:
+        # errno.ENOENT  =  no such file or directory
+        if e.errno == errno.ENOENT:
+            pass
+        else:
+            raise  # re-raise exception if a different error occured
+
+    return
+
+
+def init128(filepath):
+    '''Opens and reads the grib file with table 128 information.
+
+    Parameters
+    ----------
+    filepath : str
+        Path to file of ECMWF grib table number 128.
+
+    Return
+    ------
+    table128 : dict
+        Contains the ECMWF grib table 128 information.
+        The key is the parameter number and the value is the
+        short name of the parameter.
+    '''
+    table128 = dict()
+    try:
+        with open(filepath) as f:
+            fdata = f.read().split('\n')
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... Error occured while trying to read parameter '
+                 'table file: ' + str(filepath))
+    else:
+        for data in fdata:
+            if data != '' and data[0] != '!':
+                table128[data[0:3]] = data[59:65].strip()
+
+    return table128
+
+
+def to_param_id(pars, table):
+    '''Transform parameter names to parameter ids with ECMWF grib table 128.
+
+    Parameters
+    ----------
+    pars : str
+        Addpar argument from CONTROL file in the format of
+        parameter names instead of ids. The parameter short
+        names are sepearted with "/" and they are passed as
+        one single string.
+
+    table : dict
+        Contains the ECMWF grib table 128 information.
+        The key is the parameter number and the value is the
+        short name of the parameter.
+
+    Return
+    ------
+    ipar : list of int
+        List of addpar parameters from CONTROL file transformed to
+        parameter ids in the format of integer.
+    '''
+    if not pars:
+        return []
+    if not isinstance(pars, str):
+        pars=str(pars)
+
+    cpar = pars.upper().split('/')
+    ipar = []
+    for par in cpar:
+        par = par.strip()
+        for k, v in table.items():
+            if par.isdigit():
+                par = str(int(par)).zfill(3)
+            if par == k or par == v:
+                ipar.append(int(k))
+                break
+        else:
+            print('Warning: par ' + par + ' not found in table 128')
+
+    return ipar
+
+def to_param_id_with_tablenumber(pars, table):
+    '''Transform parameter names to parameter ids and add table id.
+
+    Conversion with ECMWF grib table 128.
+
+    Parameters
+    ----------
+    pars : str
+        Addpar argument from CONTROL file in the format of
+        parameter names instead of ids. The parameter short
+        names are sepearted with "/" and they are passed as
+        one single string.
+
+    table : dict
+        Contains the ECMWF grib table 128 information.
+        The key is the parameter number and the value is the
+        short name of the parameter.
+
+    Return
+    ------
+    spar : str
+        List of addpar parameters from CONTROL file transformed to
+        parameter ids in the format of integer.
+    '''
+    if not pars:
+        return []
+    if not isinstance(pars, str):
+        pars=str(pars)
+
+    cpar = pars.upper().split('/')
+    spar = []
+    for par in cpar:
+        for k, v in table.items():
+            if par.isdigit():
+                par = str(int(par)).zfill(3)
+            if par == k or par == v:
+                spar.append(k + '.128')
+                break
+        else:
+            print('\n\n\t\tWarning: par ' + par + ' not found in table 128\n\n')
+
+    return '/'.join(spar)
+
+def get_list_as_string(list_obj, concatenate_sign=', '):
+    '''Converts a list of arbitrary content into a single string.
+
+    Parameters
+    ----------
+    list_obj : list of *
+        A list with arbitrary content.
+
+    concatenate_sign : str, optional
+        A string which is used to concatenate the single
+        list elements. Default value is ", ".
+
+    Return
+    ------
+    str_of_list : str
+        The content of the list as a single string.
+    '''
+
+    if not isinstance(list_obj, list):
+        list_obj = list(list_obj)
+    str_of_list = concatenate_sign.join(str(l) for l in list_obj)
+
+    return str_of_list
+
+def make_dir(directory):
+    '''Creates a directory.
+
+    It gives a warning if the directory already exists and skips process.
+    The program stops only if there is another problem.
+
+    Parameters
+    ----------
+    directory : str
+        The path to directory which should be created.
+
+    Return
+    ------
+
+    '''
+    try:
+        os.makedirs(directory)
+    except OSError as e:
+        # errno.EEXIST = directory already exists
+        if e.errno == errno.EEXIST:
+            print('WARNING: Directory {0} already exists!'.format(directory))
+        else:
+            raise # re-raise exception if a different error occured
+
+    return
+
+def put_file_to_ecserver(ecd, filename, target, ecuid, ecgid):
+    '''Uses the ecaccess-file-put command to send a file to the ECMWF servers.
+
+    Note
+    ----
+    The return value is just for testing reasons. It does not have
+    to be used from the calling function since the whole error handling
+    is done in here.
+
+    Parameters
+    ----------
+    ecd : str
+        The path were the file is stored.
+
+    filename : str
+        The name of the file to send to the ECMWF server.
+
+    target : str
+        The target queue where the file should be sent to.
+
+    ecuid : str
+        The user id on ECMWF server.
+
+    ecgid : str
+        The group id on ECMWF server.
+
+    Return
+    ------
+
+    '''
+
+    try:
+        subprocess.check_output(['ecaccess-file-put',
+                                 ecd + '/' + filename,
+                                 target + ':/home/ms/' +
+                                 ecgid + '/' + ecuid +
+                                 '/' + filename],
+                                stderr=subprocess.STDOUT)
+    except subprocess.CalledProcessError as e:
+        print('... ERROR CODE: ' + str(e.returncode))
+        print('... ERROR MESSAGE:\n \t ' + str(e))
+
+        print('\n... Do you have a valid ecaccess certification key?')
+        sys.exit('... ECACCESS-FILE-PUT FAILED!')
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        print('\n... Most likely the ECACCESS library is not available!')
+        sys.exit('... ECACCESS-FILE-PUT FAILED!')
+
+    return
+
+def submit_job_to_ecserver(target, jobname):
+    '''Uses ecaccess-job-submit command to submit a job to the ECMWF server.
+
+    Note
+    ----
+    The return value is just for testing reasons. It does not have
+    to be used from the calling function since the whole error handling
+    is done in here.
+
+    Parameters
+    ----------
+    target : str
+        The target where the file should be sent to, e.g. the queue.
+
+    jobname : str
+        The name of the jobfile to be submitted to the ECMWF server.
+
+    Return
+    ------
+    job_id : int
+        The id number of the job as a reference at the ecmwf server.
+    '''
+
+    try:
+        job_id = subprocess.check_output(['ecaccess-job-submit', '-queueName',
+                                          target, jobname])
+
+    except subprocess.CalledProcessError as e:
+        print('... ERROR CODE: ' + str(e.returncode))
+        print('... ERROR MESSAGE:\n \t ' + str(e))
+
+        print('\n... Do you have a valid ecaccess certification key?')
+        sys.exit('... ECACCESS-JOB-SUBMIT FAILED!')
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        print('\n... Most likely the ECACCESS library is not available!')
+        sys.exit('... ECACCESS-JOB-SUBMIT FAILED!')
+
+    return job_id.decode()
+
+
+def get_informations(filename):
+    '''Gets basic information from an example grib file.
+
+    These information are important for later use and the
+    initialization of numpy arrays for data storing.
+
+    Parameters
+    ----------
+    filename : str
+            Name of the file which will be opened to extract basic information.
+
+    Return
+    ------
+    data : dict
+        Contains basic informations of the ECMWF grib files, e.g.
+        'Ni', 'Nj', 'latitudeOfFirstGridPointInDegrees',
+        'longitudeOfFirstGridPointInDegrees', 'latitudeOfLastGridPointInDegrees',
+        'longitudeOfLastGridPointInDegrees', 'jDirectionIncrementInDegrees',
+        'iDirectionIncrementInDegrees', 'missingValue'
+    '''
+    from eccodes import codes_grib_new_from_file, codes_get, codes_release
+
+    data = {}
+
+    # --- open file ---
+    print("Opening file for getting information data --- %s" % filename)
+    with open(filename) as f:
+        # load first message from file
+        gid = codes_grib_new_from_file(f)
+
+        # information needed from grib message
+        keys = [
+                'Ni',
+                'Nj',
+                'latitudeOfFirstGridPointInDegrees',
+                'longitudeOfFirstGridPointInDegrees',
+                'latitudeOfLastGridPointInDegrees',
+                'longitudeOfLastGridPointInDegrees',
+                'jDirectionIncrementInDegrees',
+                'iDirectionIncrementInDegrees',
+                'missingValue',
+               ]
+
+        print('\nInformations are: ')
+        for key in keys:
+            # Get the value of the key in a grib message.
+            data[key] = codes_get(gid,key)
+            print("%s = %s" % (key,data[key]))
+
+        # Free the memory for the message referred as gribid.
+        codes_release(gid)
+
+    return data
+
+
+def get_dimensions(info, purefc, dtime, index_vals, start_date, end_date):
+    '''This function specifies the correct dimensions for x, y and t.
+
+    Parameters
+    ----------
+    info : dict
+        Contains basic informations of the ECMWF grib files, e.g.
+        'Ni', 'Nj', 'latitudeOfFirstGridPointInDegrees',
+        'longitudeOfFirstGridPointInDegrees', 'latitudeOfLastGridPointInDegrees',
+        'longitudeOfLastGridPointInDegrees', 'jDirectionIncrementInDegrees',
+        'iDirectionIncrementInDegrees', 'missingValue'
+
+    purefc : int
+        Switch for definition of pure forecast mode or not.
+
+    dtime : str
+        Time step in hours.
+
+    index_vals : list of list of str
+        Contains the values from the keys used for a distinct selection
+        of grib messages in processing  the grib files.
+        Content looks like e.g.:
+        index_vals[0]: ('20171106', '20171107', '20171108') ; date
+        index_vals[1]: ('0', '1200', '1800', '600') ; time
+        index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange
+
+    start_date : str
+        The start date of the retrieval job.
+
+    end_date : str
+        The end date of the retrieval job.
+
+    Return
+    ------
+    (ix, jy, it) : tuple of int
+        Dimension in x-direction, y-direction and in time.
+    '''
+
+    ix = info['Ni']
+
+    jy = info['Nj']
+
+    if not purefc:
+        it = ((end_date - start_date).days + 1) * 24 // int(dtime)
+    else:
+        # #no of step * #no of times * #no of days
+        it = len(index_vals[2]) * len(index_vals[1]) * len(index_vals[0])
+
+    return (ix, jy, it)
+
+
+def execute_subprocess(cmd_list, error_msg='SUBPROCESS FAILED!'):
+    '''Executes a command line instruction via a subprocess.
+
+    Error handling is done if an error occures.
+
+    Parameters
+    ----------
+    cmd_list : list of str
+        A list of the components for the command line execution. Each
+        list entry is a single part of the command which is seperated from
+        the rest by a blank space.
+        E.g. ['mv', file1, file2]
+
+    Return
+    ------
+    error_msg : str, optional
+        The possible error message if the subprocess failed.
+        By default it will just tell "SUBPROCESS FAILED!".
+    '''
+
+    try:
+        subprocess.check_call(cmd_list)
+    except subprocess.CalledProcessError as e:
+        print('... ERROR CODE: ' + str(e.returncode))
+        print('... ERROR MESSAGE:\n \t ' + str(e))
+
+        sys.exit('... ' + error_msg)
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('... ' + error_msg)
+
+    return
+
+
+def generate_retrieval_period_boundary(c):
+    '''Generates retrieval period boundary datetimes from CONTROL information.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    Return
+    ------
+    start_period : datetime
+        The first timestamp of the actual retrieval period disregarding
+        the temporary times which were used for processing reasons.
+
+    end_period : datetime
+        The last timestamp of the actual retrieval period disregarding
+        the temporary times which were used for processing reasons.
+    '''
+    # generate start and end timestamp of the retrieval period
+    start_period = datetime.strptime(c.start_date + c.time[0], '%Y%m%d%H')
+    start_period = start_period + timedelta(hours=int(c.step[0]))
+    end_period = datetime.strptime(c.end_date + c.time[-1], '%Y%m%d%H')
+    end_period = end_period + timedelta(hours=int(c.step[-1]))
+
+
+    return start_period, end_period
Index: Source/Python/__init__.py
===================================================================
--- Source/Python/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: September 2018
+#
+# @License:
+#    (C) Copyright 2015-2018.
+#
+#    This software is licensed under the terms of the Apache Licence Version 2.0
+#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+#
+#*******************************************************************************
Index: Source/Python/_config.py
===================================================================
--- Source/Python/_config.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/_config.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Philipp (University of Vienna)
+#
+# @Date: August 2018
+#
+# @Change History:
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+'''Configurations for flex_extract.
+
+Defines a couple of global constant parameters as well as filenames
+and pathes.
+'''
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+import os
+import sys
+import inspect
+import platform
+
+# ------------------------------------------------------------------------------
+# PARAMETERS
+# ------------------------------------------------------------------------------
+
+_VERSION_STR = '7.1'
+
+FLAG_ON_ECMWFSERVER = 'ecgb' in platform.node()
+
+QUEUES_LIST = ['ecgate', 'cca', 'ccb']
+
+INSTALL_TARGETS = ['local', 'ecgate', 'cca', 'ccb']
+
+CDS_DATASET = 'reanalysis-era5-complete'
+
+# up-to-date available maximum level numbers at ECMWF, 05.10.2018
+MAX_LEVEL_LIST = [16, 19, 31, 40, 50, 60, 62, 91, 137]
+
+# ------------------------------------------------------------------------------
+# FILENAMES
+# ------------------------------------------------------------------------------
+
+FILE_MARS_REQUESTS = 'mars_requests.csv'
+FORTRAN_EXECUTABLE = 'CONVERT2'
+TEMPFILE_USER_ENVVARS = 'ECMWF_ENV.template'
+FILE_USER_ENVVARS = 'ECMWF_ENV'
+TEMPFILE_INSTALL_COMPILEJOB = 'compilejob.template'
+FILE_INSTALL_COMPILEJOB = 'compilejob.ksh'
+TEMPFILE_INSTALL_JOB = 'job.template'
+TEMPFILE_JOB = 'job.temp'
+FILE_JOB_OD = 'job.ksh'
+FILE_JOB_OP = 'jopoper.ksh'
+TEMPFILE_NAMELIST = 'convert.nl'
+FILE_NAMELIST = 'fort.4'
+FILE_GRIB_INDEX = 'date_time_stepRange.idx'
+FILE_GRIBTABLE = 'ecmwf_grib1_table_128'
+
+# ------------------------------------------------------------------------------
+# DIRECTORY NAMES
+# ------------------------------------------------------------------------------
+
+FLEXEXTRACT_DIRNAME = 'flex_extract_v' + _VERSION_STR
+INPUT_DIRNAME_DEFAULT = 'Workspace'
+
+# ------------------------------------------------------------------------------
+#  PATHES
+# ------------------------------------------------------------------------------
+
+# path to the local python source files
+# first thing to get because the submitted python script starts in here
+PATH_LOCAL_PYTHON = os.path.dirname(os.path.abspath(
+    inspect.getfile(inspect.currentframe())))
+# add path to pythonpath
+if PATH_LOCAL_PYTHON not in sys.path:
+    sys.path.append(PATH_LOCAL_PYTHON)
+PATH_FLEXEXTRACT_DIR = os.path.normpath(os.path.dirname(os.path.abspath(
+    inspect.getfile(inspect.currentframe()))) + '/../../')
+PATH_RUN_DIR = os.path.join(PATH_FLEXEXTRACT_DIR, 'Run')
+PATH_SOURCES = os.path.join(PATH_FLEXEXTRACT_DIR, 'Source')
+PATH_TEMPLATES = os.path.join(PATH_FLEXEXTRACT_DIR, 'Templates')
+PATH_ECMWF_ENV = os.path.join(PATH_RUN_DIR, FILE_USER_ENVVARS)
+PATH_GRIBTABLE = os.path.join(PATH_TEMPLATES, FILE_GRIBTABLE)
+PATH_JOBSCRIPTS = os.path.join(PATH_RUN_DIR, 'Jobscripts')
+PATH_FORTRAN_SRC = os.path.join(PATH_SOURCES, 'Fortran')
+PATH_PYTHONTEST_SRC = os.path.join(PATH_SOURCES, 'Pythontest')
+PATH_INPUT_DIR = os.path.join(PATH_RUN_DIR, INPUT_DIRNAME_DEFAULT)
+PATH_TEST = os.path.join(PATH_FLEXEXTRACT_DIR, 'Test')
+if os.getenv('CONTROL'):
+    # this is only needed if (gateway) version with job script is used!
+    # because job is directly submitted from SCRATCH and because the
+    # CONTROL file is stored there, the normal path is not valid.
+    PATH_CONTROLFILES = '.'
+else:
+    PATH_CONTROLFILES = os.path.join(PATH_RUN_DIR, 'Control')
+#
+# ------------------------------------------------------------------------------
+# RELATIVE PATHES FOR INSTALLATION TAR-BALL
+# ------------------------------------------------------------------------------
+
+PATH_REL_PYTHON_SRC = os.path.relpath(PATH_LOCAL_PYTHON, PATH_FLEXEXTRACT_DIR)
+PATH_REL_PYTHONTEST_SRC = os.path.relpath(PATH_PYTHONTEST_SRC, PATH_FLEXEXTRACT_DIR)
+PATH_REL_CONTROLFILES = os.path.relpath(PATH_CONTROLFILES, PATH_FLEXEXTRACT_DIR)
+PATH_REL_TEMPLATES = os.path.relpath(PATH_TEMPLATES, PATH_FLEXEXTRACT_DIR)
+PATH_REL_ECMWF_ENV = os.path.relpath(PATH_ECMWF_ENV, PATH_FLEXEXTRACT_DIR)
+PATH_REL_RUN_DIR = os.path.relpath(PATH_RUN_DIR, PATH_FLEXEXTRACT_DIR)
+PATH_REL_JOBSCRIPTS = os.path.relpath(PATH_JOBSCRIPTS, PATH_FLEXEXTRACT_DIR)
+PATH_REL_FORTRAN_SRC = os.path.relpath(PATH_FORTRAN_SRC, PATH_FLEXEXTRACT_DIR)
+PATH_REL_TEST = os.path.relpath(PATH_TEST, PATH_FLEXEXTRACT_DIR)
Index: Source/Python/install.py
===================================================================
--- Source/Python/install.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/install.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,719 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Leopold Haimberger (University of Vienna)
+#
+# @Date: November 2015
+#
+# @Change History:
+#
+#    February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - moved install_args_and_control in here
+#        - splitted code in smaller functions
+#        - delete convert build files in here instead of compile job script
+#        - changed static path names to variables from config file
+#        - splitted install function into several smaller pieces
+#        - use of tarfile package in python
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#
+# @Methods:
+#    main
+#    get_install_cmdline_args
+#    install_via_gateway
+#    check_install_conditions
+#    mk_tarball
+#    un_tarball
+#    mk_env_vars
+#    mk_compilejob
+#    mk_job_template
+#    del_convert_build
+#    mk_convert_build
+#
+#*******************************************************************************
+'''This script installs the flex_extract program.
+
+Depending on the selected installation environment (locally or on the
+ECMWF server ecgate or cca) the program extracts the commandline
+arguments and the CONTROL file parameter and prepares the corresponding
+environment.
+The necessary files are collected in a tar-ball and placed
+at the target location. There its untared, the environment variables will
+be set and the Fortran code will be compiled.
+If the ECMWF environment is selected a job script is prepared and submitted
+for the remaining configurations after putting the tar-ball to the
+target ECMWF server.
+
+Type: install.py --help
+to get information about command line parameters.
+Read the documentation for usage instructions.
+'''
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+import sys
+import glob
+import subprocess
+import inspect
+import tarfile
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+
+# software specific classes and modules from flex_extract
+import _config
+from Classes.ControlFile import ControlFile
+from Classes.UioFiles import UioFiles
+from Mods.tools import (make_dir, put_file_to_ecserver, submit_job_to_ecserver,
+                        silent_remove, execute_subprocess, none_or_str)
+
+# ------------------------------------------------------------------------------
+# FUNCTIONS
+# ------------------------------------------------------------------------------
+def main():
+    '''Controls the installation process. Calls the installation function
+    if target is specified.
+
+    Parameters
+    ----------
+
+    Return
+    ------
+    '''
+
+    args = get_install_cmdline_args()
+    c = ControlFile(args.controlfile)
+    c.assign_args_to_control(args)
+    check_install_conditions(c)
+
+    if c.install_target.lower() != 'local': # ecgate or cca
+        install_via_gateway(c)
+    else: # local
+        install_local(c)
+
+    return
+
+def get_install_cmdline_args():
+    '''Decomposes the command line arguments and assigns them to variables.
+    Apply default values for non mentioned arguments.
+
+    Parameters
+    ----------
+
+    Return
+    ------
+    args : Namespace
+        Contains the commandline arguments from script/program call.
+    '''
+    parser = ArgumentParser(description='Install flex_extract software '
+                                        'locally or on ECMWF machines',
+                            formatter_class=ArgumentDefaultsHelpFormatter)
+
+    parser.add_argument('--target', dest='install_target',
+                        type=none_or_str, default=None,
+                        help="Valid targets: local | ecgate | cca , \
+                        the latter two are at ECMWF")
+    parser.add_argument("--makefile", dest="makefile",
+                        type=none_or_str, default=None,
+                        help='Name of Makefile to use for compiling the '
+                        'Fortran program')
+    parser.add_argument("--ecuid", dest="ecuid",
+                        type=none_or_str, default=None,
+                        help='The user id at ECMWF.')
+    parser.add_argument("--ecgid", dest="ecgid",
+                        type=none_or_str, default=None,
+                        help='The group id at ECMWF.')
+    parser.add_argument("--gateway", dest="gateway",
+                        type=none_or_str, default=None,
+                        help='The name of the local gateway server.')
+    parser.add_argument("--destination", dest="destination",
+                        type=none_or_str, default=None,
+                        help='The ecaccess association, e.g. '
+                        'myUser@genericSftp')
+
+    parser.add_argument("--installdir", dest="installdir",
+                        type=none_or_str, default=None,
+                        help='Root directory where '
+                        'flex_extract will be installed to.')
+
+    # arguments for job submission to ECMWF, only needed by submit.py
+    parser.add_argument("--job_template", dest='job_template',
+                        type=none_or_str, default="job.template",
+                        help='The rudimentary template file to create a batch '
+                        'job template for submission to ECMWF servers.')
+
+    parser.add_argument("--controlfile", dest="controlfile",
+                        type=none_or_str, default='CONTROL_EA5',
+                        help="The file with all CONTROL parameters.")
+
+    args = parser.parse_args()
+
+    return args
+
+
+def install_via_gateway(c):
+    '''Prepare data transfer to remote gate and submit a job script which will
+    install everything on the remote gate.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    Return
+    ------
+
+    '''
+    import tarfile
+
+    tarball_name = _config.FLEXEXTRACT_DIRNAME + '.tar'
+    tar_file = os.path.join(_config.PATH_FLEXEXTRACT_DIR, tarball_name)
+
+    mk_compilejob(c.makefile, c.install_target, c.ecuid, c.ecgid,
+                  c.installdir)
+
+    mk_job_template(c.ecuid, c.ecgid, c.gateway,
+                    c.destination, c.installdir)
+
+    mk_env_vars(c.ecuid, c.ecgid, c.gateway, c.destination)
+
+    mk_tarball(tar_file, c.install_target)
+
+    put_file_to_ecserver(_config.PATH_FLEXEXTRACT_DIR, tarball_name,
+                         c.install_target, c.ecuid, c.ecgid)
+
+    submit_job_to_ecserver(c.install_target,
+                           os.path.join(_config.PATH_REL_JOBSCRIPTS,
+                                        _config.FILE_INSTALL_COMPILEJOB))
+
+    silent_remove(tar_file)
+
+    print('Job compilation script has been submitted to ecgate for ' +
+          'installation in ' + c.installdir +
+           '/' + _config.FLEXEXTRACT_DIRNAME)
+    print('You should get an email with subject "flexcompile" within ' +
+          'the next few minutes!')
+
+    return
+
+def install_local(c):
+    '''Perform the actual installation on a local machine.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    Return
+    ------
+
+    '''
+    import tarfile
+
+    tar_file = os.path.join(_config.PATH_FLEXEXTRACT_DIR,
+                            _config.FLEXEXTRACT_DIRNAME + '.tar')
+
+    if c.installdir == _config.PATH_FLEXEXTRACT_DIR :
+        print('WARNING: installdir has not been specified')
+        print('flex_extract will be installed in here by compiling the ' +
+              'Fortran source in ' + _config.PATH_FORTRAN_SRC)
+        os.chdir(_config.PATH_FORTRAN_SRC)
+    else: # creates the target working directory for flex_extract
+        c.installdir = os.path.expandvars(os.path.expanduser(
+            c.installdir))
+        if os.path.abspath(_config.PATH_FLEXEXTRACT_DIR) != \
+           os.path.abspath(c.installdir):
+            mk_tarball(tar_file, c.install_target)
+            make_dir(os.path.join(c.installdir,
+                                   _config.FLEXEXTRACT_DIRNAME))
+            os.chdir(os.path.join(c.installdir,
+                                   _config.FLEXEXTRACT_DIRNAME))
+            un_tarball(tar_file)
+            os.chdir(os.path.join(c.installdir,
+                                   _config.FLEXEXTRACT_DIRNAME,
+                                  _config.PATH_REL_FORTRAN_SRC))
+
+    # Create Fortran executable - CONVERT2
+    print('Install ' +  _config.FLEXEXTRACT_DIRNAME + ' software at ' +
+          c.install_target + ' in directory ' +
+          os.path.abspath(c.installdir) + '\n')
+
+    del_convert_build('.')
+    mk_convert_build('.', c.makefile)
+
+    os.chdir(_config.PATH_FLEXEXTRACT_DIR)
+    if os.path.isfile(tar_file):
+        os.remove(tar_file)
+
+    return
+
+
+def check_install_conditions(c):
+    '''Checks a couple of necessary attributes and conditions
+    for the installation such as if they exist and contain values.
+    Otherwise set default values.
+
+    Parameters
+    ----------
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+
+    Return
+    ------
+
+    '''
+
+    if c.install_target and \
+       c.install_target not in _config.INSTALL_TARGETS:
+        print('ERROR: unknown or missing installation target ')
+        print('target: ', c.install_target)
+        print('please specify correct installation target ' +
+              str(INSTALL_TARGETS))
+        print('use -h or --help for help')
+        sys.exit(1)
+
+    if c.install_target and c.install_target != 'local':
+        if not c.ecgid or not c.ecuid or \
+           not c.gateway or not c.destination:
+            print('Please enter your ECMWF user id and group id as well ' +
+                  'as the \nname of the local gateway and the ectrans ' +
+                  'destination ')
+            print('with command line options --ecuid --ecgid \
+                   --gateway --destination')
+            print('Try "' + sys.argv[0].split('/')[-1] + \
+                  ' -h" to print usage information')
+            print('Please consult ecaccess documentation or ECMWF user \
+                   support for further details')
+            sys.exit(1)
+
+        if not c.installdir:
+            c.installdir = '${HOME}'
+    else: # local
+        if not c.installdir:
+            c.installdir = _config.PATH_FLEXEXTRACT_DIR
+
+    return
+
+
+def mk_tarball(tarball_path, target):
+    '''Creates a tarball with all necessary files which need to be sent to the
+    installation directory.
+    It does not matter if this is local or remote.
+    Collects all python files, the Fortran source and makefiles,
+    the ECMWF_ENV file, the CONTROL files as well as the
+    template files.
+
+    Parameters
+    ----------
+    tarball_path : str
+        The complete path to the tar file which will contain all
+        relevant data for flex_extract.
+
+    target : str
+        The queue where the job is submitted to.
+
+    Return
+    ------
+
+    '''
+    from glob import glob
+
+    print('Create tarball ...')
+
+    # change to FLEXEXTRACT directory so that the tar can contain
+    # relative pathes to the files and directories
+    ecd = _config.PATH_FLEXEXTRACT_DIR + '/'
+    os.chdir(ecd)
+
+    # get lists of the files to be added to the tar file
+    if target == 'local':
+        ECMWF_ENV_FILE = []
+        runfile = [os.path.relpath(x, ecd)
+                   for x in UioFiles(_config.PATH_REL_RUN_DIR,
+                                     'run_local.sh').files]
+    else:
+        ECMWF_ENV_FILE = [_config.PATH_REL_ECMWF_ENV]
+        runfile = [os.path.relpath(x, ecd)
+                       for x in UioFiles(_config.PATH_REL_RUN_DIR,
+                                         'run.sh').files]
+
+    pyfiles = [os.path.relpath(x, ecd)
+               for x in UioFiles(_config.PATH_REL_PYTHON_SRC, '*py').files]
+    pytestfiles = [os.path.relpath(x, ecd)
+               for x in UioFiles(_config.PATH_REL_PYTHONTEST_SRC, '*py').files]
+    controlfiles = [os.path.relpath(x, ecd)
+                    for x in UioFiles(_config.PATH_REL_CONTROLFILES,
+                                      'CONTROL*').files]
+    testfiles = [os.path.relpath(x, ecd)
+                 for x in UioFiles(_config.PATH_REL_TEST , '*').files]
+    tempfiles = [os.path.relpath(x, ecd)
+                 for x in UioFiles(_config.PATH_REL_TEMPLATES , '*.temp').files]
+    nlfiles = [os.path.relpath(x, ecd)
+                 for x in UioFiles(_config.PATH_REL_TEMPLATES , '*.nl').files]
+    gribtable = [os.path.relpath(x, ecd)
+                 for x in UioFiles(_config.PATH_REL_TEMPLATES , '*grib*').files]
+    ffiles = [os.path.relpath(x, ecd)
+              for x in UioFiles(_config.PATH_REL_FORTRAN_SRC, '*.f*').files]
+    hfiles = [os.path.relpath(x, ecd)
+              for x in UioFiles(_config.PATH_REL_FORTRAN_SRC, '*.h').files]
+    makefiles = [os.path.relpath(x, ecd)
+                 for x in UioFiles(_config.PATH_REL_FORTRAN_SRC, 'Makefile*').files]
+    jobdir = [_config.PATH_REL_JOBSCRIPTS]
+
+    # concatenate single lists to one for a better looping
+    filelist = pyfiles + pytestfiles + controlfiles + tempfiles + nlfiles + \
+               ffiles + gribtable + hfiles + makefiles + ECMWF_ENV_FILE + \
+               runfile + jobdir + testfiles +\
+               ['CODE_OF_CONDUCT.md', 'LICENSE.md', 'README.md']
+
+    # create installation tar-file
+    exclude_files = [".ksh"]
+    try:
+        with tarfile.open(tarball_path, "w:gz") as tar_handle:
+            for file in filelist:
+                tar_handle.add(file, recursive=False,
+                               filter=lambda tarinfo: None
+                                      if os.path.splitext(tarinfo.name)[1]
+                                         in exclude_files
+                                      else tarinfo)
+    except tarfile.TarError as e:
+        sys.exit('\n... error occured while trying to create the tar-file ' +
+                     str(tarball_path))
+
+    return
+
+
+def un_tarball(tarball_path):
+    '''Extracts the given tarball into current directory.
+
+    Parameters
+    ----------
+    tarball_path : str
+        The complete path to the tar file which will contain all
+        relevant data for flex_extract.
+
+    Return
+    ------
+
+    '''
+
+    print('Untar ...')
+
+    try:
+        with tarfile.open(tarball_path) as tar_handle:
+            tar_handle.extractall()
+    except tarfile.TarError as e:
+        sys.exit('\n... error occured while trying to read tar-file ' +
+                     str(tarball_path))
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to read tar-file ' +
+                 str(tarball_path))
+
+    return
+
+def mk_env_vars(ecuid, ecgid, gateway, destination):
+    '''Creates a file named ECMWF_ENV which contains the
+    necessary environmental variables at ECMWF servers.
+    It is based on the template ECMWF_ENV.template.
+
+    Parameters
+    ----------
+    ecuid : str
+        The user id on ECMWF server.
+
+    ecgid : str
+        The group id on ECMWF server.
+
+    gateway : str
+        The gateway server the user is using.
+
+    destination : str
+        The remote destination which is used to transfer files
+        from ECMWF server to local gateway server.
+
+    Return
+    ------
+
+    '''
+    from genshi.template.text import NewTextTemplate
+    from genshi.template import  TemplateLoader
+    from genshi.template.eval import UndefinedError
+
+    try:
+        loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
+        ecmwfvars_template = loader.load(_config.TEMPFILE_USER_ENVVARS,
+                                         cls=NewTextTemplate)
+
+        stream = ecmwfvars_template.generate(user_name = ecuid,
+                                             user_group = ecgid,
+                                             gateway_name = gateway,
+                                             destination_name = destination
+                                             )
+    except UndefinedError as e:
+        print('... ERROR ' + str(e))
+
+        sys.exit('\n... error occured while trying to generate template ' +
+                 _config.PATH_ECMWF_ENV)
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to generate template ' +
+                 _config.PATH_ECMWF_ENV)
+
+    try:
+        with open(_config.PATH_ECMWF_ENV, 'w') as f:
+            f.write(stream.render('text'))
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to write ' +
+                 _config.PATH_ECMWF_ENV)
+
+    return
+
+def mk_compilejob(makefile, target, ecuid, ecgid, fp_root):
+    '''Modifies the original job template file so that it is specified
+    for the user and the environment were it will be applied. Result
+    is stored in a new file "job.temp" in the python directory.
+
+    Parameters
+    ----------
+    makefile : str
+        Name of the makefile which should be used to compile FORTRAN
+        CONVERT2 program.
+
+    target : str
+        The target where the installation should be done, e.g. the queue.
+
+    ecuid : str
+        The user id on ECMWF server.
+
+    ecgid : str
+        The group id on ECMWF server.
+
+    fp_root : str
+       Path to the root directory of FLEXPART environment or flex_extract
+       environment.
+
+    Return
+    ------
+
+    '''
+    from genshi.template.text import NewTextTemplate
+    from genshi.template import  TemplateLoader
+    from genshi.template.eval import  UndefinedError
+
+    if fp_root == '../':
+        fp_root = '$HOME'
+
+    try:
+        loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
+        compile_template = loader.load(_config.TEMPFILE_INSTALL_COMPILEJOB,
+                                       cls=NewTextTemplate)
+
+        stream = compile_template.generate(
+            usergroup = ecgid,
+            username = ecuid,
+            version_number = _config._VERSION_STR,
+            fp_root_scripts = fp_root,
+            makefile = makefile,
+            fortran_program = _config.FORTRAN_EXECUTABLE
+        )
+    except UndefinedError as e:
+        print('... ERROR ' + str(e))
+
+        sys.exit('\n... error occured while trying to generate template ' +
+                 _config.TEMPFILE_INSTALL_COMPILEJOB)
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to generate template ' +
+                 _config.TEMPFILE_INSTALL_COMPILEJOB)
+
+    try:
+        compilejob = os.path.join(_config.PATH_JOBSCRIPTS,
+                                  _config.FILE_INSTALL_COMPILEJOB)
+
+        with open(compilejob, 'w') as f:
+            f.write(stream.render('text'))
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to write ' +
+                 compilejob)
+
+    return
+
+def mk_job_template(ecuid, ecgid, gateway, destination, fp_root):
+    '''Modifies the original job template file so that it is specified
+    for the user and the environment were it will be applied. Result
+    is stored in a new file.
+
+    Parameters
+    ----------
+    ecuid : str
+        The user id on ECMWF server.
+
+    ecgid : str
+        The group id on ECMWF server.
+
+    gateway : str
+        The gateway server the user is using.
+
+    destination : str
+        The remote destination which is used to transfer files
+        from ECMWF server to local gateway server.
+
+    fp_root : str
+       Path to the root directory of FLEXPART environment or flex_extract
+       environment.
+
+    Return
+    ------
+
+    '''
+    from genshi.template.text import NewTextTemplate
+    from genshi.template import  TemplateLoader
+    from genshi.template.eval import  UndefinedError
+
+    fp_root_path_to_python = os.path.join(fp_root,
+                                          _config.FLEXEXTRACT_DIRNAME,
+                                          _config.PATH_REL_PYTHON_SRC)
+    if '$' in fp_root_path_to_python:
+        ind = fp_root_path_to_python.index('$')
+        fp_root_path_to_python = fp_root_path_to_python[0:ind] + '$' + \
+                                 fp_root_path_to_python[ind:]
+
+    try:
+        loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
+        compile_template = loader.load(_config.TEMPFILE_INSTALL_JOB,
+                                       cls=NewTextTemplate)
+
+        stream = compile_template.generate(
+            usergroup = ecgid,
+            username = ecuid,
+            version_number = _config._VERSION_STR,
+            fp_root_path = fp_root_path_to_python,
+        )
+    except UndefinedError as e:
+        print('... ERROR ' + str(e))
+
+        sys.exit('\n... error occured while trying to generate template ' +
+                 _config.TEMPFILE_INSTALL_JOB)
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to generate template ' +
+                 _config.TEMPFILE_INSTALL_JOB)
+
+
+    try:
+        tempjobfile = os.path.join(_config.PATH_TEMPLATES,
+                                   _config.TEMPFILE_JOB)
+
+        with open(tempjobfile, 'w') as f:
+            f.write(stream.render('text'))
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to write ' +
+                 tempjobfile)
+
+    return
+
+def del_convert_build(src_path):
+    '''Clean up the Fortran source directory and remove all
+    build files (e.g. \*.o, \*.mod and CONVERT2)
+
+    Parameters
+    ----------
+    src_path : str
+        Path to the fortran source directory.
+
+    Return
+    ------
+
+    '''
+
+    modfiles = UioFiles(src_path, '*.mod')
+    objfiles = UioFiles(src_path, '*.o')
+    exefile = UioFiles(src_path, _config.FORTRAN_EXECUTABLE)
+
+    modfiles.delete_files()
+    objfiles.delete_files()
+    exefile.delete_files()
+
+    return
+
+def mk_convert_build(src_path, makefile):
+    '''Compiles the Fortran code and generates the executable.
+
+    Parameters
+    ----------
+    src_path : str
+        Path to the fortran source directory.
+
+    makefile : str
+        The name of the makefile which should be used.
+
+    Return
+    ------
+
+    '''
+
+    try:
+        print('Using makefile: ' + makefile)
+        p = subprocess.Popen(['make', '-f',
+                              os.path.join(src_path, makefile)],
+                             stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE,
+                             bufsize=1)
+        pout, perr = p.communicate()
+        print(pout.decode())
+        if p.returncode != 0:
+            print(perr.decode())
+            print('Please edit ' + makefile +
+                  ' or try another Makefile in the src directory.')
+            print('Most likely GRIB_API_INCLUDE_DIR, GRIB_API_LIB '
+                  'and EMOSLIB must be adapted.')
+            print('Available Makefiles:')
+            print(UioFiles(src_path, 'Makefile*'))
+            sys.exit('Compilation failed!')
+    except ValueError as e:
+        print('ERROR: Makefile call failed:')
+        print(e)
+    else:
+        execute_subprocess(['ls', '-l', os.path.join(src_path,
+                            _config.FORTRAN_EXECUTABLE)], error_msg=
+                           'FORTRAN EXECUTABLE COULD NOT BE FOUND!')
+
+    return
+
+
+if __name__ == "__main__":
+    main()
Index: Source/Python/submit.py
===================================================================
--- Source/Python/submit.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Python/submit.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,268 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Anne Fouilloux (University of Oslo)
+#
+# @Date: October 2014
+#
+# @Change History:
+#
+#    November 2015 - Leopold Haimberger (University of Vienna):
+#        - job submission on ecgate and cca
+#        - job templates suitable for twice daily operational dissemination
+#
+#    February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#        - minor changes in programming style (for consistence)
+#        - changed path names to variables from config file
+#        - added option for writing mars requests to extra file
+#          additionally, as option without submitting the mars jobs
+#        - splitted submit function to use genshi templates for the
+#          job script and avoid code duplication
+#
+# @License:
+#    (C) Copyright 2014-2019.
+#    Anne Philipp, Leopold Haimberger
+#
+#    This work is licensed under the Creative Commons Attribution 4.0
+#    International License. To view a copy of this license, visit
+#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
+#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+#*******************************************************************************
+'''This script allows the user to extract meteorological fields from the ECMWF.
+
+It prepares the settings for retrieving the data from ECMWF servers and
+process the resulting files to prepare them for the use with FLEXPART or
+FLEXTRA.
+
+If it is supposed to work locally then it works through the necessary
+functions get_mars_data and prepare_flexpart. Otherwise it prepares
+a job script (korn shell) which will do the necessary work on the
+ECMWF server. This script will de submitted via the ecaccess command
+ecaccess-job-submit.
+
+This file can also be imported as a module which then contains the following
+functions:
+
+    * main - the main function of the script
+    * submit - calls mk_jobscript depending on operation mode and submits its
+    * mk_jobscript - creates the job script from a template
+
+Type: submit.py --help
+to get information about command line parameters.
+Read the documentation for usage instructions.
+'''
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+from __future__ import print_function
+
+import os
+import sys
+import subprocess
+import inspect
+import collections
+from datetime import datetime, timedelta
+
+# software specific classes and modules from flex_extract
+import _config
+from Mods.tools import (setup_controldata, normal_exit, get_cmdline_args,
+                         submit_job_to_ecserver, read_ecenv)
+from Mods.get_mars_data import get_mars_data
+from Mods.prepare_flexpart import prepare_flexpart
+from Classes.ControlFile import ControlFile
+
+
+# ------------------------------------------------------------------------------
+# METHODS
+# ------------------------------------------------------------------------------
+
+def main():
+    '''Get the arguments from script call and from CONTROL file.
+    Decides from the argument "queue" if the local version
+    is done "queue=None" or the gateway version with "queue=ecgate"
+    or "queue=cca".
+
+    Parameters
+    ----------
+
+
+    Return
+    ------
+
+    '''
+
+    c, ppid, queue, job_template = setup_controldata()
+
+    # on local side
+    # starting from an ECMWF server this would also be the local side
+    called_from_dir = os.getcwd()
+    if queue is None:
+        if c.inputdir[0] != '/':
+            c.inputdir = os.path.join(called_from_dir, c.inputdir)
+        if c.outputdir[0] != '/':
+            c.outputdir = os.path.join(called_from_dir, c.outputdir)
+        get_mars_data(c)
+        if c.request == 0 or c.request == 2:
+            prepare_flexpart(ppid, c)
+            exit_message = 'FLEX_EXTRACT IS DONE!'
+        else:
+            exit_message = 'PRINTING MARS_REQUESTS DONE!'
+    # send files to ECMWF server
+    else:
+        submit(job_template, c, queue)
+        exit_message = 'FLEX_EXTRACT JOB SCRIPT IS SUBMITED!'
+
+    normal_exit(exit_message)
+
+    return
+
+def submit(jtemplate, c, queue):
+    '''Prepares the job script and submits it to the specified queue.
+
+    Parameters
+    ----------
+    jtemplate : str
+        Job template file from sub-directory "_templates" for
+        submission to ECMWF. It contains all necessary
+        module and variable settings for the ECMWF environment as well as
+        the job call and mail report instructions.
+        Default is "job.temp".
+
+    c : ControlFile
+        Contains all the parameters of CONTROL file and
+        command line.
+
+    queue : str
+        Name of queue for submission to ECMWF (e.g. ecgate or cca )
+
+    Return
+    ------
+
+    '''
+
+    if not c.oper:
+    # --------- create on demand job script ------------------------------------
+        if c.purefc:
+            print('---- Pure forecast mode! ----')
+        else:
+            print('---- On-demand mode! ----')
+
+        job_file = os.path.join(_config.PATH_JOBSCRIPTS,
+                                jtemplate[:-5] + '.ksh')
+
+        # divide time periode into specified number of job chunks
+        # to have multiple job scripts
+        if c.job_chunk:
+            start = datetime.strptime(c.start_date, '%Y%m%d')
+            end = datetime.strptime(c.end_date, '%Y%m%d')
+            chunk = timedelta(days=c.job_chunk)
+            oneday = timedelta(days=1)
+
+            while start <= end:
+                if (start + chunk) <= end:
+                    c.end_date = (start + chunk - oneday).strftime("%Y%m%d")
+                else:
+                    c.end_date = end.strftime("%Y%m%d")
+
+                clist = c.to_list()
+                mk_jobscript(jtemplate, job_file, clist)
+
+                job_id = submit_job_to_ecserver(queue, job_file)
+                print('The job id is: ' + str(job_id.strip()))
+
+                start = start + chunk
+                c.start_date = start.strftime("%Y%m%d")
+        # submit a single job script
+        else:
+            clist = c.to_list()
+
+            mk_jobscript(jtemplate, job_file, clist)
+
+            job_id = submit_job_to_ecserver(queue, job_file)
+            print('The job id is: ' + str(job_id.strip()))
+
+    else:
+    # --------- create operational job script ----------------------------------
+        print('---- Operational mode! ----')
+
+        job_file = os.path.join(_config.PATH_JOBSCRIPTS,
+                                jtemplate[:-5] + 'oper.ksh')
+
+        c.start_date = '${MSJ_YEAR}${MSJ_MONTH}${MSJ_DAY}'
+        c.end_date = '${MSJ_YEAR}${MSJ_MONTH}${MSJ_DAY}'
+        c.basetime = '${MSJ_BASETIME}'
+        if c.maxstep > 24:
+            c.time = '${MSJ_BASETIME} {MSJ_BASETIME}'
+
+        clist = c.to_list()
+
+        mk_jobscript(jtemplate, job_file, clist)
+
+        job_id = submit_job_to_ecserver(queue, job_file)
+        print('The job id is: ' + str(job_id.strip()))
+
+    print('You should get an email per job with subject flex.hostname.pid')
+
+    return
+
+def mk_jobscript(jtemplate, job_file, clist):
+    '''Creates the job script from template.
+
+    Parameters
+    ----------
+    jtemplate : str
+        Job template file from sub-directory "_templates" for
+        submission to ECMWF. It contains all necessary
+        module and variable settings for the ECMWF environment as well as
+        the job call and mail report instructions.
+        Default is "job.temp".
+
+    job_file : str
+        Path to the job script file.
+
+    clist : list of str
+        Contains all necessary parameters for ECMWF CONTROL file.
+
+    Return
+    ------
+
+    '''
+    from genshi.template.text import NewTextTemplate
+    from genshi.template import  TemplateLoader
+    from genshi.template.eval import UndefinedError
+
+    # load template and insert control content as list
+    try:
+        loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
+        control_template = loader.load(jtemplate,
+                                       cls=NewTextTemplate)
+
+        stream = control_template.generate(control_content=clist)
+    except UndefinedError as e:
+        print('... ERROR ' + str(e))
+
+        sys.exit('\n... error occured while trying to generate jobscript')
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to generate jobscript')
+
+    # create jobscript file
+    try:
+        with open(job_file, 'w') as f:
+            f.write(stream.render('text'))
+    except OSError as e:
+        print('... ERROR CODE: ' + str(e.errno))
+        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
+
+        sys.exit('\n... error occured while trying to write ' + job_file)
+
+    return
+
+
+if __name__ == "__main__":
+    main()
Index: Source/Pythontest/.coverage
===================================================================
--- Source/Pythontest/.coverage	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/.coverage	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,1 @@
+!coverage.py: This is a private format, don't read it directly!{"lines":{"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/test_suite.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/_config.py":[32,33,37,24,25,26,28,31],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/MarsRetrieval.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/install.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/get_mars_data.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/testecmwfapi.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/EcFlexpart.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/profiling.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/pythontest/TestInput.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/plot_retrieved.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/ControlFile.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/submit.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/pythontest/__init__.py":[13],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/pythontest/TestTools.py":[4,5,6,7,8,10,11,12,18,20,22,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,59,61,63,64,65,67,68,70,71,72,73,74,76,77,78,79,80,82,83,84,85,86,88,90,92,94,96,97,99,100,102,103,104,105,106,107,108,110,111,112,113,114,115,117,126,127,128,129,131,139,140,141,142,143,145,146,147,148,149,151,152,153,154,155,157,158,159,160,161,162,163,164,166,167,168,169,170,171,173,175,176,177,178,182,183,184,185,186,188,189,190,193],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/disaggregation.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/pythontest/TestPathes.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/GribTools.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/prepare_flexpart.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/tools.py":[517,518,519,520,521,522,523,524,526,527,529,50,51,52,53,54,55,56,62,76,77,78,81,82,83,84,85,86,89,90,91,92,93,94,95,96,99,100,101,102,103,104,107,110,112,115,116,117,118,120,121,122,123,124,125,127,129,131,144,145,146,147,148,149,151,153,191,210,213,214,215,216,217,218,219,220,221,222,223,224,229,230,232,237,279,315,328,329,330,331,335,338,353,354,355,356,357,358,360,363,386,387,388,389,390,391,392,396,398,416,418,420,433,434,435,436,438,440,442,444,476,477,478,481,482,483,484,485,486,488,489,491,493],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/pythontest/TestEcFlexpart.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/pythontest/TestUIOFiles.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/pythontest/TestInstall.py":[],"/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/UioFiles.py":[]}}
Index: Source/Pythontest/TestEcFlexpart.py
===================================================================
--- Source/Pythontest/TestEcFlexpart.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/TestEcFlexpart.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+import sys
+import os
+import inspect
+import pytest
+
+sys.path.append('../python')
+import _config
+from classes.EcFlexpart import EcFlexpart
+from classes.ControlFile import ControlFile
+from mods.tools import silent_remove
+
+class TestEcFlexpart():
+    '''
+    '''
+
+    def test_write_namelist(self):
+        import filecmp
+
+        control_file = os.path.join(_config.PATH_TEST_DIR,
+                                        'TestData',
+                                        'CONTROL.temp')
+        c = ControlFile(control_file)
+        flexpart = EcFlexpart(c)
+
+        c.inputdir = 'TestData'
+
+        # comparison file
+        testfile = os.path.join(_config.PATH_TEST_DIR,
+                                'TestData',
+                                'convert.nl.test')
+
+        # create
+        flexpart.write_namelist(c)
+
+        finalfile = os.path.join(c.inputdir, _config.FILE_NAMELIST)
+        assert filecmp.cmp(testfile, finalfile, shallow=False)
+
+        # delete test file
+        silent_remove(finalfile)
+
+
+
+
+# these functions should test the output and compare the results with an output
+# of the old version and check if there are no differences!!!
+# also check for errors?! Or check if it works for alle datasets and private public etc
+
+    #    - process_output
+    #    - create
+    #    - deacc_fluxes
+    #    - retrieve
+
Index: Source/Pythontest/TestInput.py
===================================================================
--- Source/Pythontest/TestInput.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/TestInput.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import pytest
+
+sys.path.append('../python')
+from classes.ControlFile import ControlFile
+from mods.tools import get_cmdline_arguments
+
+
+class TestInput():
+    '''
+    Test class to test the reading of commandline arguments and
+    control file.
+    '''
+    # ToDo
+    # create more tests for input
+    # 1. nur controlfile reading
+    # 2. check of parameter
+
+    def __init__(self):
+        # Default values for ArgumentParser
+        self.args = {'start_date':None,
+                     'end_date':None,
+                     'date_chunk':None,
+                     'basetime':None,
+                     'step':None,
+                     'levelist':None,
+                     'area':None,
+                     'inputdir':None,
+                     'outputdir':None,
+                     'flexpart_root_scripts':None,
+                     'ppid':None,
+                     'job_template':'job.temp',
+                     'queue':None,
+                     'controlfile':'CONTROL.temp',
+                     'debug':0,
+                     }
+        #sys.argv = ['dummy.py', '--start_date=20180101', '--debug=1',
+        #            '--step=0/to/11/BY/3', '--area=20./20./0./90.']
+        sys.argv = ['dummy.py', '--start_date=20180101']
+
+        self.args = tools.get_commandline_arguments()
+
+        self.c = ControlFile('TestData/CONTROL.temp')
+
+        self.c.assign_args_to_control(self.args)
+
+        self.c.check_conditions()
+
+
+    def test_args_reading(self):
+
+        sys.argv = ['dummy.py', '--start_date=20180101', '--debug=1',
+                    '--step=0/to/11/BY/3', '--area=20./20./0./90.']
+
+        arguments = tools.get_cmdline_arguments()
+
+        args_exp = {'start_date':'20180101',
+                    'end_date':None,
+                    'date_chunk':None,
+                    'basetime':None,
+                    'step':'0/to/11/BY/3',
+                    'levelist':None,
+                    'area':'20./20./0./90.',
+                    'inputdir':None,
+                    'outputdir':None,
+                    'flexpart_root_scripts':None,
+                    'ppid':None,
+                    'job_template':'job.temp',
+                    'queue':None,
+                    'controlfile':'CONTROL.temp',
+                    'debug':1,
+                    }
+
+        assert vars(arguments) == args_exp
+
+
+    def test_args_assignment(self):
+
+        import collections
+
+        # expected parametervalue:
+        exp_dict = {
+                    'accuracy': '16',
+                    'addpar': ['186', '187', '188', '235', '139', '39'],
+                    'area': None,
+                    'basetime': None,
+                    'controlfile': 'CONTROL.temp',
+                    'cwc': 0,
+                    'date_chunk': 3,
+                    'debug': 0,
+                    'destination': None,
+                    'dpdeta': '1',
+                    'dtime': '3',
+                    'ecfsdir': 'ectmp:/${USER}/econdemand/',
+                    'ecgid': None,
+                    'ecmwfdatadir': '/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/../',
+                    'ecstorage': '0',
+                    'ectrans': '1',
+                    'ecuid': None,
+                    'end_date': '20180101',
+                    'eta': '0',
+                    'etadiff': '0',
+                    'etapar': 77,
+                    'exedir': '/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/../src/',
+                    'expver': '1',
+                    'flexpart_root_scripts': '/raid60/nas/tmc/Anne/Interpolation/flexextract/flexextract/python/../',
+                    'format': 'GRIB1',
+                    'gateway': None,
+                    'gauss': '1',
+                    'grib2flexpart': '0',
+                    'grid': '5000',
+                    'inputdir': '../work',
+                    'job_template': 'job.temp',
+                    'left': '-15000',
+                    'level': '60',
+                    'levelist': '55/to/60',
+                    'lower': '30000',
+                    'mailfail': ['${USER}'],
+                    'mailops': ['${USER}'],
+                    'makefile': None,
+                    'marsclass': 'EI',
+                    'maxstep': 11,
+                    'number': 'OFF',
+                    'omega': '0',
+                    'omegadiff': '0',
+                    'outputdir': '../work',
+                    'prefix': 'EI',
+                    'resol': '63',
+                    'right': '45000',
+                    'smooth': '0',
+                    'start_date': '20180101',
+                    'step': ['00', '01', '02', '03', '04', '05', '00', '07', '08', '09', '10', '11', '00', '01', '02', '03', '04', '05', '00', '07', '08', '09', '10', '11'],
+                    'stream': 'OPER',
+                    'target': None,
+                    'time': ['00', '00', '00', '00', '00', '00', '06', '00', '00', '00', '00', '00', '12', '12', '12', '12', '12', '12', '18', '12', '12', '12', '12', '12'],
+                    'type': ['AN', 'FC', 'FC', 'FC', 'FC', 'FC', 'AN', 'FC', 'FC', 'FC', 'FC', 'FC', 'AN', 'FC', 'FC', 'FC', 'FC', 'FC', 'AN', 'FC', 'FC', 'FC', 'FC', 'FC'],
+                    'upper': '75000',
+                    'wrf': 0}
+
+        exp_dict = collections.OrderedDict(sorted(exp_dict.items()))
+        cdict = collections.OrderedDict(sorted(vars(self.c).items()))
+
+        # remove content which isn't comparable for different users
+        # or different operating systems
+        del cdict['ecfsdir_expanded']
+        del cdict['mailops_expanded']
+        del cdict['mailfail_expanded']
+
+        #print 'cdict\n', cdict
+        #print 'exp_dict\n', exp_dict
+
+        #assert cdict == exp_dict
+        assert cdict == exp_dict
+
+        return
Index: Source/Pythontest/TestInstall.py
===================================================================
--- Source/Pythontest/TestInstall.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/TestInstall.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,330 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+import sys
+import os
+import inspect
+import subprocess
+import tarfile
+import errno
+import shutil
+from genshi.template import TemplateLoader
+from genshi.template.eval import  UndefinedError
+from exceptions import OSError
+import pytest
+from mock import patch
+
+sys.path.append('../python')
+import _config
+import _config_test
+from install import (mk_tarball, un_tarball, mk_env_vars, mk_compilejob,
+                     mk_job_template)
+
+from mods.tools import make_dir, silent_remove
+
+#    - main
+#    - get_install_cmdline_arguments
+#    - install_via_gateway
+#    - delete_convert_build
+#    - make_convert_build
+
+class TestInstall():
+    '''
+    '''
+    @classmethod
+    def setup_class(self):
+        self.testdir = _config_test.PATH_TEST_DIR
+        self.testfilesdir = _config_test.PATH_TESTFILES_DIR
+        self.testinstalldir = _config_test.PATH_TESTINSTALL_DIR
+
+        # make test tarballs from shell script
+        subprocess.check_output([os.path.join(self.testinstalldir,
+                                              'mk_install_tar.sh')])
+
+        # un tar the test tarballs from shell script
+        subprocess.check_output([os.path.join(self.testinstalldir,
+                                              'un_install_tar.sh')])
+
+
+
+    @patch('tarfile.open', side_effect=[subprocess.CalledProcessError(1,'test'),
+                                        OSError(errno.EEXIST)])
+    def test_fail_mk_tarball_local(self, mock_open):
+        ecd = _config.PATH_FLEXEXTRACT_DIR + os.path.sep
+        # create test tarball and list its content files
+        tarballname = _config.FLEXEXTRACT_DIRNAME + '_localtest.tar'
+
+        with pytest.raises(SystemExit):
+            mk_tarball(ecd + tarballname, 'local')
+        with pytest.raises(SystemExit):
+            mk_tarball(ecd + tarballname, 'local')
+
+    def test_success_mk_tarball_local(self):
+        ecd = _config.PATH_FLEXEXTRACT_DIR + os.path.sep
+
+        # list comparison files for tarball content
+        tar_test_dir = os.path.join(self.testdir, 'InstallTar')
+        tar_test_file = os.path.join(tar_test_dir,
+                                     'flex_extract_v7.1_local.tar')
+        with tarfile.open(tar_test_file, 'r') as tar_handle:
+            comparison_list = tar_handle.getnames()
+
+        # create test tarball and list its content files
+        tarballname = _config.FLEXEXTRACT_DIRNAME + '_localtest.tar'
+        mk_tarball(ecd + tarballname, 'local')
+        with tarfile.open(ecd + tarballname, 'r') as tar_handle:
+            tar_content_list = tar_handle.getnames()
+
+        # remove test tar file from flex_extract directory
+        os.remove(ecd + tarballname)
+
+        # test if comparison filelist is equal to the
+        # filelist of tarball content
+        assert sorted(comparison_list) == sorted(tar_content_list)
+
+    def test_success_mk_tarball_ecgate(self):
+        ecd = _config.PATH_FLEXEXTRACT_DIR + os.path.sep
+
+        # list comparison files for tarball content
+        tar_test_dir = os.path.join(self.testdir, 'InstallTar')
+        tar_test_file = os.path.join(tar_test_dir,
+                                     'flex_extract_v7.1_ecgate.tar')
+        with tarfile.open(tar_test_file, 'r') as tar_handle:
+            comparison_list = tar_handle.getnames()
+
+        # create test tarball and list its content files
+        tarballname = _config.FLEXEXTRACT_DIRNAME + '_ecgatetest.tar'
+        mk_tarball(ecd + tarballname, 'ecgate')
+        with tarfile.open(ecd + tarballname, 'r') as tar_handle:
+            tar_content_list = tar_handle.getnames()
+
+        # remove test tar file from flex_extract directory
+        os.remove(ecd + tarballname)
+
+        # test if comparison filelist is equal to the
+        # filelist of tarball content
+        assert sorted(comparison_list) == sorted(tar_content_list)
+
+    @patch('tarfile.open', side_effect=[tarfile.TarError, OSError])
+    def test_fail_un_tarball(self, mock_open):
+        with pytest.raises(SystemExit):
+            un_tarball('testpath')
+
+    def test_success_ecgate_un_tarball(self):
+        ecd = _config.PATH_FLEXEXTRACT_DIR + os.path.sep
+
+        # list comparison files for tarball content
+        tar_test_dir = os.path.join(self.testdir, 'InstallTar')
+        cmp_dir = _config.FLEXEXTRACT_DIRNAME + '_ecgate'
+        tar_test_fedir = os.path.join(tar_test_dir, cmp_dir)
+        comparison_list = []
+        for path, subdirs, files in os.walk(tar_test_fedir):
+            for name in files:
+                if 'tar' not in name:
+                    comparison_list.append(os.path.relpath(
+                        os.path.join(path, name), tar_test_fedir))
+
+        # untar in test directory
+        test_dir = os.path.join(tar_test_dir, 'test_ecgate')
+        make_dir(test_dir)
+        os.chdir(test_dir)
+        tarballname = _config.FLEXEXTRACT_DIRNAME + '_ecgate.tar'
+        un_tarball(os.path.join(tar_test_dir, tarballname))
+        tarfiles_list = []
+        for path, subdirs, files in os.walk(test_dir):
+            for name in files:
+                tarfiles_list.append(os.path.relpath(
+                    os.path.join(path, name), test_dir))
+
+        # test for equality
+        assert sorted(tarfiles_list) == sorted(comparison_list)
+
+    def test_success_local_un_tarball(self):
+        ecd = _config.PATH_FLEXEXTRACT_DIR + os.path.sep
+
+        # list comparison files for tarball content
+        tar_test_dir = os.path.join(self.testdir, 'InstallTar')
+        cmp_dir = _config.FLEXEXTRACT_DIRNAME + '_local'
+        tar_test_fedir = os.path.join(tar_test_dir, cmp_dir)
+        comparison_list = []
+        for path, subdirs, files in os.walk(tar_test_fedir):
+            for name in files:
+                if 'tar' not in name:
+                    comparison_list.append(os.path.relpath(
+                        os.path.join(path, name), tar_test_fedir))
+
+        # untar in test directory
+        test_dir = os.path.join(tar_test_dir, 'test_local')
+        make_dir(test_dir)
+        os.chdir(test_dir)
+        tarballname = _config.FLEXEXTRACT_DIRNAME + '_local.tar'
+        un_tarball(os.path.join(tar_test_dir, tarballname))
+        tarfiles_list = []
+        for path, subdirs, files in os.walk(test_dir):
+            for name in files:
+                tarfiles_list.append(os.path.relpath(
+                    os.path.join(path, name), test_dir))
+
+        # test for equality
+        assert sorted(tarfiles_list) == sorted(comparison_list)
+
+    @patch('_config.PATH_ECMWF_ENV', _config_test.PATH_TESTFILES_DIR+'/ecmwf_test')
+    def test_success_mk_env_vars(self):
+        import filecmp
+
+        cmp_file = os.path.join(self.testfilesdir,
+                                'ECMWF_ENV.test')
+
+        mk_env_vars('testuser',
+                    'testgroup',
+                    'gateway.test.ac.at',
+                    'user@destination')
+
+        assert filecmp.cmp(cmp_file, _config.PATH_ECMWF_ENV, shallow=False)
+
+        silent_remove(_config.PATH_ECMWF_ENV)
+
+    @patch('genshi.template.TemplateLoader', side_effect=[OSError])
+    def test_fail_load_mk_env_vars(self, mock_generate):
+        with pytest.raises(SystemExit):
+            mk_env_vars('testuser',
+                        'testgroup',
+                        'gateway.test.ac.at',
+                        'user@destination')
+
+    def test_fail_generate_mk_env_vars(self):
+        with patch('genshi.template.TemplateLoader.load') as MockHelper:
+            MockHelper.return_value.generate.side_effect = UndefinedError('undefined')
+            with pytest.raises(SystemExit):
+                mk_env_vars('testuser',
+                            'testgroup',
+                            'gateway.test.ac.at',
+                            'user@destination')
+
+    @patch('__builtin__.open', side_effect=[OSError(errno.EPERM)])
+    def test_fail_open_mk_env_vars(self, mock_open):
+        with pytest.raises(SystemExit):
+            mk_env_vars('testuser',
+                        'testgroup',
+                        'gateway.test.ac.at',
+                        'user@destination')
+
+    @patch('_config.FILE_INSTALL_COMPILEJOB', _config_test.PATH_TESTFILES_DIR+'/compilejob_test.ksh')
+    def test_success_mk_compilejob(self):
+        import filecmp
+
+        testfile = os.path.join(self.testfilesdir,
+                                'compilejob.test')
+
+        mk_compilejob('Makefile.TEST',
+                      '',
+                      'testuser',
+                      'testgroup',
+                      'fp_root_test_path')
+
+        finalfile = os.path.join(_config.PATH_JOBSCRIPTS,
+                                 _config.FILE_INSTALL_COMPILEJOB)
+        assert filecmp.cmp(testfile, finalfile, shallow=False)
+
+        silent_remove(finalfile)
+
+    @patch('genshi.template.TemplateLoader', side_effect=[OSError])
+    def test_fail_load_mk_compilejob(self, mock_generate):
+        with pytest.raises(SystemExit):
+            mk_compilejob('Makefile.TEST',
+                          '',
+                          'testuser',
+                          'testgroup',
+                          'fp_root_test_path')
+
+    def test_fail_generate_mk_compilejob(self):
+        with patch('genshi.template.TemplateLoader.load') as MockHelper:
+            MockHelper.return_value.generate.side_effect = UndefinedError('undefined')
+            with pytest.raises(SystemExit):
+                mk_compilejob('Makefile.TEST',
+                              '',
+                              'testuser',
+                              'testgroup',
+                              'fp_root_test_path')
+
+    @patch('__builtin__.open', side_effect=[OSError(errno.EPERM)])
+    def test_fail_open_mk_compilejob(self, mock_open):
+        with pytest.raises(SystemExit):
+            mk_compilejob('Makefile.TEST',
+                          '',
+                          'testuser',
+                          'testgroup',
+                          'fp_root_test_path')
+
+    @patch('_config.TEMPFILE_JOB', _config_test.PATH_TESTFILES_DIR+'/job_temp.test_test')
+    def test_success_mk_job_template(self):
+        import filecmp
+
+        testfile = os.path.join(self.testfilesdir,
+                                'job.temp.test')
+
+        mk_job_template('testuser',
+                        'testgroup',
+                        'gateway.test.ac.at',
+                        'dest@generic',
+                        'fp_root_test_path')
+
+        finalfile = os.path.join(_config.PATH_TEMPLATES,
+                                 _config.TEMPFILE_JOB)
+        assert filecmp.cmp(testfile, finalfile, shallow=False)
+
+        silent_remove(finalfile)
+
+    @patch('genshi.template.TemplateLoader', side_effect=[OSError])
+    def test_fail_load_mk_job_template(self, mock_generate):
+        with pytest.raises(SystemExit):
+            mk_job_template('testuser',
+                            'testgroup',
+                            'gateway.test.ac.at',
+                            'dest@generic',
+                            'fp_root_test_path')
+
+    def test_fail_generate_mk_job_template(self):
+        with patch('genshi.template.TemplateLoader.load') as MockHelper:
+            MockHelper.return_value.generate.side_effect = UndefinedError('undefined')
+            with pytest.raises(SystemExit):
+                mk_job_template('testuser',
+                                'testgroup',
+                                'gateway.test.ac.at',
+                                'dest@generic',
+                                'fp_root_test_path')
+
+    @patch('__builtin__.open', side_effect=[OSError(errno.EPERM)])
+    def test_fail_open_mk_job_template(self, mock_open):
+        with pytest.raises(SystemExit):
+            mk_job_template('testuser',
+                            'testgroup',
+                            'gateway.test.ac.at',
+                            'dest@generic',
+                            'fp_root_test_path')
+
+    @classmethod
+    def teardown_class(self):
+
+        test_dir = os.path.join(self.testinstalldir,
+                                _config.FLEXEXTRACT_DIRNAME + '_local')
+        shutil.rmtree(test_dir)
+        test_dir = os.path.join(self.testinstalldir,
+                                _config.FLEXEXTRACT_DIRNAME + '_ecgate')
+        shutil.rmtree(test_dir)
+
+        test_dir = os.path.join(self.testinstalldir,
+                                'test_local')
+        shutil.rmtree(test_dir)
+        test_dir = os.path.join(self.testinstalldir,
+                                'test_ecgate')
+        shutil.rmtree(test_dir)
+
+        tar_file = os.path.join(self.testinstalldir,
+                     _config.FLEXEXTRACT_DIRNAME + '_local.tar')
+        os.remove(tar_file)
+        tar_file = os.path.join(self.testinstalldir,
+                                _config.FLEXEXTRACT_DIRNAME + '_ecgate.tar')
+        os.remove(tar_file)
+        pass
Index: Source/Pythontest/TestPathes.py
===================================================================
--- Source/Pythontest/TestPathes.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/TestPathes.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import pytest
+
+sys.path.append("../python")
+import _config
+
+def test_path_localpython():
+    assert os.path.exists(_config.PATH_LOCAL_PYTHON) == 1
+
+def test_path_flexextract():
+    assert os.path.exists(_config.PATH_FLEXEXTRACT_DIR) == 1
+
+def test_path_flexextract_name():
+    version = _config._VERSION_STR
+    flexextract_name = 'flex_extract_v' + version
+    assert os.path.basename(_config.PATH_FLEXEXTRACT_DIR) == flexextract_name
+
+def test_path_templates():
+    assert os.path.exists(_config.PATH_TEMPLATES) == 1
+
+def test_path_vtable():
+    assert os.path.exists(_config.PATH_GRIBTABLE) == 1
+
+def test_file_vtable():
+    assert os.path.isfile(_config.PATH_GRIBTABLE) == 1
+
+def test_path_run():
+    assert os.path.exists(_config.PATH_RUN_DIR) == 1
+
+def test_path_control():
+    assert os.path.exists(_config.PATH_CONTROLFILES) == 1
Index: Source/Pythontest/TestTools.py
===================================================================
--- Source/Pythontest/TestTools.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/TestTools.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,372 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# for the gateway tests, the env vars of ECUID and ECGID have to be set upfront
+
+import os
+import sys
+import errno
+from exceptions import OSError
+import subprocess
+import pipes
+import pytest
+from mock import patch, call
+#from mock import PropertyMock
+
+
+import _config
+import _config_test
+from classes.ControlFile import ControlFile
+from mods.tools import (none_or_str, none_or_int, get_cmdline_arguments,
+                        read_ecenv, clean_up, my_error, send_mail,
+                        normal_exit, product, silent_remove,
+                        init128, to_param_id, get_list_as_string, make_dir,
+                        put_file_to_ecserver, submit_job_to_ecserver)
+
+class TestTools(object):
+    '''
+    '''
+
+    def setup_method(self):
+        self.testdir = _config_test.PATH_TEST_DIR
+        self.testfilesdir = _config_test.PATH_TESTFILES_DIR
+        self.c = ControlFile(self.testdir+'/Controls/CONTROL.test')
+
+    def test_nonestr_none_or_int(self):
+        assert None == none_or_int('None')
+
+    def test_intstr_none_or_int(self):
+        assert 42 == none_or_int('42')
+
+    def test_nonestr_none_or_str(self):
+        assert None == none_or_str('None')
+
+    def test_anystr_none_or_str(self):
+        assert 'test' == none_or_str('test')
+
+    def test_fail_get_cmdline_arguments(self):
+        sys.argv = ['dummy.py', '--wrong=1']
+        with pytest.raises(SystemExit):
+            results = get_cmdline_arguments()
+
+    def test_default_get_cmdline_arguments(self):
+        cmd_dict_control = {'start_date':None,
+                            'end_date':None,
+                            'date_chunk':None,
+                            'basetime':None,
+                            'step':None,
+                            'levelist':None,
+                            'area':None,
+                            'inputdir':None,
+                            'outputdir':None,
+                            'flexpart_root_scripts':None,
+                            'ppid':None,
+                            'job_template':'job.temp',
+                            'queue':None,
+                            'controlfile':'CONTROL.temp',
+                            'debug':None,
+                            'public':None,
+                            'request':None}
+
+        sys.argv = ['dummy.py']
+
+        results = get_cmdline_arguments()
+
+        assert cmd_dict_control == vars(results)
+
+    def test_input_get_cmdline_arguments(self):
+        cmd_dict_control = {'start_date':'20180101',
+                            'end_date':'20180101',
+                            'date_chunk':3,
+                            'basetime':12,
+                            'step':'1',
+                            'levelist':'1/to/10',
+                            'area':'50/10/60/20',
+                            'inputdir':'../work',
+                            'outputdir':None,
+                            'flexpart_root_scripts':'../',
+                            'ppid':1234,
+                            'job_template':'job.sh',
+                            'queue':'ecgate',
+                            'controlfile':'CONTROL.WORK',
+                            'debug':1,
+                            'public':None,
+                            'request':0}
+
+        sys.argv = ['dummy.py',
+                    '--start_date=20180101',
+                    '--end_date=20180101',
+                    '--date_chunk=3',
+                    '--basetime=12',
+                    '--step=1',
+                    '--levelist=1/to/10',
+                    '--area=50/10/60/20',
+                    '--inputdir=../work',
+                    '--outputdir=None',
+                    '--flexpart_root_scripts=../',
+                    '--ppid=1234',
+                    '--job_template=job.sh',
+                    '--queue=ecgate',
+                    '--controlfile=CONTROL.WORK',
+                    '--debug=1',
+                    '--public=None',
+                    '--request=0']
+
+        results = get_cmdline_arguments()
+
+        assert cmd_dict_control == vars(results)
+
+    def test_success_init128(self):
+        table128 = init128(_config.PATH_GRIBTABLE)
+        expected_sample = {'078': 'TCLW', '130': 'T', '034': 'SST'}
+        # check a sample of parameters which must have been read in
+        assert all((k in table128 and table128[k]==v)
+                   for k,v in expected_sample.iteritems())
+
+    @patch('__builtin__.open', side_effect=[OSError(errno.EEXIST)])
+    def test_fail_open_init128(self, mock_openfile):
+        with pytest.raises(SystemExit):
+            table128 = init128(_config.PATH_GRIBTABLE)
+
+    @pytest.mark.parametrize(
+        'ipar_str, opar_listint',
+        [('SP/LSP/SSHF', [134, 142, 146]),
+         ('T', [130]),
+         ('', []),
+         (None, []),
+         ('testtest', []),
+         ('130/142', [130, 142]),
+         (130, [130]),
+         (50.56, [])])
+    def test_to_param_id(self, ipar_str, opar_listint):
+        table128 = init128(_config.PATH_GRIBTABLE)
+        ipars = to_param_id(ipar_str, table128)
+        assert sorted(ipars) == sorted(opar_listint)
+
+    @patch('traceback.format_stack', return_value='empty trace')
+    @patch('mods.tools.send_mail', return_value=0)
+    def test_success_my_error(self, mock_mail, mock_trace, capfd):
+        with pytest.raises(SystemExit):
+            my_error(['any_user'], 'Failed!')
+            out, err = capfd.readouterr()
+            assert out == "Failed!\n\nempty_trace\n"
+
+    @patch('subprocess.Popen')
+    @patch('os.getenv', return_value='user')
+    @patch('os.path.expandvars', return_value='user')
+    def test_success_userenv_twouser_send_mail(self, mock_os, mock_env, mock_popen, capfd):
+        mock_popen.return_value = subprocess.Popen(["echo", "Hello Test!"],
+                                                   stdout=subprocess.PIPE)
+        send_mail(['${USER}', 'any_user'], 'ERROR', message='error mail')
+        out, err = capfd.readouterr()
+        assert out == b'Email sent to user\nEmail sent to user\n'
+
+    @patch('subprocess.Popen')
+    @patch('os.path.expandvars', return_value='any_user')
+    def test_success_send_mail(self, mock_os,  mock_popen, capfd):
+        mock_popen.return_value = subprocess.Popen(["echo", "Hello Test!"],
+                                                   stdout=subprocess.PIPE)
+        send_mail(['any-user'], 'ERROR', message='error mail')
+        out, err = capfd.readouterr()
+        assert out == b'Email sent to any_user\n'
+
+    @patch('subprocess.Popen', side_effect=[ValueError, OSError])
+    @patch('os.path.expandvars', return_value='any_user')
+    def test_fail_valueerror_send_mail(self, mock_osvar, mock_popen):
+        with pytest.raises(SystemExit): # ValueError
+            send_mail(['any-user'], 'ERROR', message='error mail')
+        with pytest.raises(SystemExit): # OSError
+            send_mail(['any-user'], 'ERROR', message='error mail')
+
+    def test_success_read_ecenv(self):
+        envs_ref = {'ECUID': 'testuser',
+                    'ECGID': 'testgroup',
+                    'GATEWAY': 'gateway.test.ac.at',
+                    'DESTINATION': 'user@destination'
+                   }
+        envs = read_ecenv(self.testfilesdir + '/ECMWF_ENV.test')
+
+        assert envs_ref == envs
+
+    @patch('__builtin__.open', side_effect=[OSError(errno.EPERM)])
+    def test_fail_read_ecenv(self, mock_open):
+        with pytest.raises(SystemExit):
+            read_ecenv('any_file')
+
+    @patch('glob.glob', return_value=[])
+    @patch('mods.tools.silent_remove')
+    def test_empty_clean_up(self, mock_rm, mock_clean):
+        clean_up(self.c)
+        mock_rm.assert_not_called()
+
+    @patch('glob.glob', return_value=['any_file','EIfile'])
+    @patch('os.remove', return_value=0)
+    def test_success_clean_up(self, mock_rm, mock_glob):
+        # ectrans=0; ecstorage=0; ecapi=None; prefix not in filename
+        clean_up(self.c)
+        mock_rm.assert_has_calls([call('any_file'), call('EIfile')])
+        mock_rm.reset_mock()
+
+        # ectrans=0; ecstorage=0; ecapi=False; prefix in filename
+        self.c.prefix = 'EI'
+        self.c.ecapi = False
+        clean_up(self.c)
+        mock_rm.assert_has_calls([call('any_file')])
+        mock_rm.reset_mock()
+
+        # ectrans=0; ecstorage=0; ecapi=True; prefix in filename
+        self.c.prefix = 'EI'
+        self.c.ecapi = True
+        clean_up(self.c)
+        mock_rm.assert_has_calls([call('any_file')])
+        mock_rm.reset_mock()
+
+        # ectrans=1; ecstorage=0; ecapi=True; prefix in filename
+        self.c.prefix = 'EI'
+        self.c.ecapi = True
+        self.c.ectrans = 1
+        clean_up(self.c)
+        mock_rm.assert_has_calls([call('any_file')])
+        mock_rm.reset_mock()
+
+        # ectrans=1; ecstorage=0; ecapi=False; prefix in filename
+        self.c.prefix = 'EI'
+        self.c.ecapi = False
+        self.c.ectrans = 1
+        clean_up(self.c)
+        mock_rm.assert_has_calls([call('any_file'), call('EIfile')])
+        mock_rm.reset_mock()
+
+        # ectrans=1; ecstorage=1; ecapi=False; prefix in filename
+        self.c.prefix = 'EI'
+        self.c.ecapi = False
+        self.c.ectrans = 1
+        self.c.ecstorage = 1
+        clean_up(self.c)
+        mock_rm.assert_has_calls([call('any_file'), call('EIfile')])
+        mock_rm.reset_mock()
+
+    def test_default_normal_exit(self, capfd):
+        normal_exit()
+        out, err = capfd.readouterr()
+        assert out == 'Done!\n'
+
+    def test_message_normal_exit(self, capfd):
+        normal_exit('Hi there!')
+        out, err = capfd.readouterr()
+        assert out == 'Hi there!\n'
+
+    def test_int_normal_exit(self, capfd):
+        normal_exit(42)
+        out, err = capfd.readouterr()
+        assert out == '42\n'
+
+    @pytest.mark.parametrize(
+        'input1, input2, output_list',
+        [('ABC','xy',[('A','x'),('A','y'),('B','x'),('B','y'),('C','x'),('C','y')]),
+         (range(1), range(1), [(0,0),(0,1),(1,0),(1,1)])])
+    def test_success_product(self, input1, input2, output_list):
+        index = 0
+        for prod in product(input1, input2):
+            assert isinstance(prod, tuple)
+            assert prod == output_list[index]
+            index += 1
+
+    @pytest.mark.parametrize(
+        'input1, input2, output_list',
+        [(1,1,(1,1))])
+    def test_fail_product(self, input1, input2, output_list):
+        index = 0
+        with pytest.raises(SystemExit):
+            for prod in product(input1, input2):
+                assert isinstance(prod, tuple)
+                assert prod == output_list[index]
+                index += 1
+
+    def test_success_silent_remove(self):
+        testfile = self.testfilesdir + 'test.txt'
+        open(testfile, 'w').close()
+        silent_remove(testfile)
+        assert os.path.isfile(testfile) == False
+
+    @patch('os.remove', side_effect=OSError(errno.ENOENT))
+    def test_fail_notexist_silent_remove(self, mock_rm):
+        with pytest.raises(OSError) as pytest_wrapped_e:
+            silent_remove('any_dir')
+            assert pytest_wrapped_e.e.errno == errno.ENOENT
+
+    @patch('os.remove', side_effect=OSError(errno.EEXIST))
+    def test_fail_any_silent_remove(self, mock_rm):
+        with pytest.raises(OSError):
+            silent_remove('any_dir')
+
+    @pytest.mark.parametrize(
+        'input_list, output_list',
+        [([],''),
+         ([1, 2, 3.5, '...', 'testlist'], '1, 2, 3.5, ..., testlist'),
+         ('2', '2')])
+    def test_success_get_list_as_string(self, input_list, output_list):
+        assert output_list == get_list_as_string(input_list)
+
+    @patch('os.makedirs', side_effect=[OSError(errno.EEXIST)])
+    def test_warning_exist_make_dir(self, mock_make):
+        with pytest.raises(OSError) as pytest_wrapped_e:
+            make_dir('existing_dir')
+            assert pytest_wrapped_e.e.errno == errno.EEXIST
+
+    @patch('os.makedirs', side_effect=OSError)
+    def test_fail_any_make_dir(self, mock_makedir):
+        with pytest.raises(OSError):
+            make_dir('any_dir')
+
+    def test_fail_empty_make_dir(self):
+        with pytest.raises(OSError):
+            make_dir('')
+
+    def test_success_make_dir(self):
+        testdir = '/testing_mkdir'
+        make_dir(self.testdir + testdir)
+        assert os.path.exists(self.testdir + testdir) == True
+        os.rmdir(self.testdir + testdir)
+
+
+    @patch('subprocess.check_output', side_effect=[subprocess.CalledProcessError(1,'test')])
+    def test_fail_put_file_to_ecserver(self, mock_co):
+        with pytest.raises(SystemExit):
+            put_file_to_ecserver(self.testfilesdir, 'test_put_to_ecserver.txt',
+                                 'ecgate', 'ex_ECUID', 'ex_ECGID')
+
+    @patch('subprocess.check_output', return_value=0)
+    def test_general_success_put_file_to_ecserver(self, mock_co):
+        result = put_file_to_ecserver(self.testfilesdir, 'test_put_to_ecserver.txt',
+                                      'ecgate', 'ex_ECUID', 'ex_ECGID')
+        assert result == None
+
+    @pytest.mark.msuser_pw
+    @pytest.mark.gateway
+    @pytest.mark.skip(reason="easier to ignore for now - implement in final version")
+    def test_full_success_put_file_to_ecserver(self):
+        ecuid = os.environ['ECUID']
+        ecgid = os.environ['ECGID']
+        put_file_to_ecserver(self.testfilesdir, 'test_put_to_ecserver.txt',
+                             'ecgate', ecuid, ecgid)
+        assert subprocess.call(['ssh', ecuid+'@ecaccess.ecmwf.int' ,
+                                'test -e ' +
+                                pipes.quote('/home/ms/'+ecgid+'/'+ecuid)]) == 0
+
+    @patch('subprocess.check_output', side_effect=[subprocess.CalledProcessError(1,'test'),
+                                                   OSError])
+    def test_fail_submit_job_to_ecserver(self, mock_co):
+        with pytest.raises(SystemExit):
+            job_id = submit_job_to_ecserver('ecgate', 'job.ksh')
+
+    @pytest.mark.msuser_pw
+    @pytest.mark.gateway
+    @pytest.mark.skip(reason="easier to ignore for now - implement in final version")
+    def test_success_submit_job_to_ecserver(self):
+        job_id = submit_job_to_ecserver('ecgate',
+                                        os.path.join(self.testfilesdir,
+                                                     'test_put_to_ecserver.txt'))
+        assert job_id.strip().isdigit() == True
+
+
Index: Source/Pythontest/TestUIOFiles.py
===================================================================
--- Source/Pythontest/TestUIOFiles.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/TestUIOFiles.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import pytest
+
+sys.path.append('../python')
+from classes.UioFiles import UioFiles
+
+
+class TestUioFiles():
+    '''
+    Test class to test the UIOFiles methods.
+    '''
+
+    def test_listFiles(self):
+        '''
+        @Description:
+            Test the listFiles method from class UIOFiles.
+
+        @Input:
+            self: instance of TestClass
+                Class to test the UIOFiles methods.
+
+        @Return:
+            <nothing>
+        '''
+        # set comparison information
+        self.testpath = os.path.join(os.path.dirname(__file__), 'TestDir')
+        self.expected = ['FCGG__SL.20160410.40429.16424.grb',
+                             'FCOG__ML.20160410.40429.16424.grb',
+                             'FCSH__ML.20160410.40429.16424.grb',
+                             'OG_OROLSM__SL.20160410.40429.16424.grb',
+                             'FCOG_acc_SL.20160409.40429.16424.grb',
+                             'FCOG__SL.20160410.40429.16424.grb',
+                             'FCSH__SL.20160410.40429.16424.grb']
+
+        # Initialise and collect filenames
+        files = UioFiles(self.testpath, '*.grb')
+        # get the basename to just check for equality of filenames
+        filelist = [os.path.basename(f) for f in files.files]
+        # comparison of expected filenames against the collected ones
+        assert sorted(self.expected) == sorted(filelist)
+
+        return
+
Index: Source/Pythontest/__init__.py
===================================================================
--- Source/Pythontest/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/__init__.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+@Author: Anne Philipp (University of Vienna)
+
+@Date: March 2018
+
+@License:
+    (C) Copyright 2014 UIO.
+
+    This software is licensed under the terms of the Apache Licence Version 2.0
+    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+"""
Index: Source/Pythontest/_config_test.py
===================================================================
--- Source/Pythontest/_config_test.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/_config_test.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+'''
+*******************************************************************************
+ @Author: Anne Philipp (University of Vienna)
+
+ @Date: August 2018
+
+ @Change History:
+
+ @License:
+    (C) Copyright 2014-2018.
+
+    This software is licensed under the terms of the Apache Licence Version 2.0
+    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+
+ @Description:
+    Contains constant value parameter for flex_extract.
+
+*******************************************************************************
+'''
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+import os
+import sys
+
+sys.path.append('../python')
+import _config
+
+# ------------------------------------------------------------------------------
+# FILENAMES
+# ------------------------------------------------------------------------------
+
+
+# ------------------------------------------------------------------------------
+# DIRECTORY NAMES
+# ------------------------------------------------------------------------------
+TEST_DIR = 'test/Unit'
+TESTFILES_DIR = 'Testfiles'
+TESTINSTALL_DIR = 'InstallTar'
+# ------------------------------------------------------------------------------
+#  PATHES
+# ------------------------------------------------------------------------------
+PATH_TEST_DIR = os.path.join(_config.PATH_FLEXEXTRACT_DIR, TEST_DIR)
+PATH_TESTFILES_DIR = os.path.join(PATH_TEST_DIR, TESTFILES_DIR)
+PATH_TESTINSTALL_DIR = os.path.join(PATH_TEST_DIR, TESTINSTALL_DIR)
Index: Source/Pythontest/conftest.py
===================================================================
--- Source/Pythontest/conftest.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/conftest.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,10 @@
+import sys
+import pytest
+
+sys.path.append('../python')
+import _config
+
+#@pytest.fixture
+#def prep_test_env():
+#    testdir = _config.PATH_TEST_DIR
+print ''
Index: Source/Pythontest/leos_test_suit.py
===================================================================
--- Source/Pythontest/leos_test_suit.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/leos_test_suit.py	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#*******************************************************************************
+# @Author: Leopold Haimberger (University of Vienna)
+#
+# @Date: December 2015
+#
+# @Change History:
+#
+#    February 2018 - Anne Philipp (University of Vienna):
+#        - applied PEP8 style guide
+#        - added documentation
+#
+# @License:
+#    (C) Copyright 2015-2018.
+#
+#    This software is licensed under the terms of the Apache Licence Version 2.0
+#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# @Program Functionality:
+#    This script triggers the flex_extract test suite. Call with
+#    test_suite.py [test group]
+#
+# @Program Content:
+#
+#*******************************************************************************
+
+# ------------------------------------------------------------------------------
+# MODULES
+# ------------------------------------------------------------------------------
+import os
+import sys
+import json
+import subprocess
+
+# ------------------------------------------------------------------------------
+# PROGRAM
+# ------------------------------------------------------------------------------
+try:
+    taskfile = open('test_suite.json')
+except IOError:
+    print 'could not open suite definition file test_suite.json'
+    exit()
+
+if not os.path.isfile('../src/CONVERT2'):
+    print '../src/CONVERT2 could not be found'
+    print 'please run "install.py --target=local" first'
+    exit()
+
+fprs = os.getenv('FLEXPART_ROOT_SCRIPTS')
+if fprs is None:
+    print 'FLEXPART_ROOT_SCRIPTS not set .. some test jobs may fail'
+
+tasks = json.load(taskfile, encoding='latin-1')
+taskfile.close()
+if not os.path.exists('../test'):
+    os.makedirs('../test')
+if len(sys.argv) > 1:
+    groups = sys.argv[1:]
+else:
+    groups = ['xinstall', 'default', 'ops', 'work', 'cv', 'fc']#,'hires']
+jobcounter = 0
+jobfailed = 0
+for g in groups:
+    try:
+        tk, tv = g, tasks[g]
+    finally:
+        pass
+    garglist = []
+    for ttk, ttv in tv.iteritems():
+        if isinstance(ttv, basestring):
+            if ttk != 'script':
+                garglist.append('--' + ttk)
+                if ttv[0] == '$':
+                    garglist.append(os.path.expandvars(ttv))
+                else:
+                    garglist.append(ttv)
+    for ttk, ttv in tv.iteritems():
+        if isinstance(ttv, dict):
+            arglist = []
+            for tttk, tttv in ttv.iteritems():
+                if isinstance(tttv, basestring):
+                    arglist.append('--' + tttk)
+                    if '$' in tttv[0]:
+                        arglist.append(os.path.expandvars(tttv))
+                    else:
+                        arglist.append(tttv)
+            print 'Command: ', ' '.join([tv['script']] + garglist + arglist)
+            o = '../test/' + tk + '_' + ttk + '_' + '_'.join(ttv.keys())
+            print 'Output will be sent to ', o
+            f = open(o, 'w')
+            try:
+                p = subprocess.check_call([tv['script']] + garglist + arglist,
+                                          stdout=f, stderr=f)
+            except subprocess.CalledProcessError as e:
+                f.write('\nFAILED\n')
+                print 'FAILED'
+                jobfailed += 1
+            jobcounter += 1
+            f.close()
+
+print 'Test suite tasks completed'
+print str(jobcounter-jobfailed) + ' successful, ' + str(jobfailed) + ' failed'
+print 'If tasks have been submitted via ECACCESS please check emails'
Index: Source/Pythontest/leos_testsuite.json
===================================================================
--- Source/Pythontest/leos_testsuite.json	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/leos_testsuite.json	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,79 @@
+{
+"install": {
+"script": "install.py",
+"control": null,
+"inputdir": null,
+"ecuid":"lh0",
+"ecgid":"spatlh00",
+"gateway":"srvx7.img.univie.ac.at",
+"destination":"leo@genericSftp",
+"local": {"target":"local"},
+"local_flexpart": {"target":"local","flexpart_root_scripts":"$FLEXPART_ROOT_SCRIPTS"},
+"ecgate": {"target":"ecgate"},
+"cca": {"target":"cca"}
+},
+"default": {
+"script": "submit.py",
+"control": null,
+"inputdir": null,
+"start_date": "20131107",
+"local": {},
+"local_flexpart": {"flexpart_root_scripts":"$FLEXPART_ROOT_SCRIPTS"},
+"ecgate": {"queue":"ecgate"},
+"cca": {"queue":"cca"}
+},
+"work": {
+"script": "submit.py",
+"control": null,
+"start_date": "20131107",
+"inputdir": "$SCRATCH/work",
+"local_flexpart": {"flexpart_root_scripts":"$FLEXPART_ROOT_SCRIPTS"}
+},
+"fc": {
+"script": "submit.py",
+"control": "CONTROL_FC",
+"inputdir": "$SCRATCH/workfc",
+"local_flexpart": {"flexpart_root_scripts":"$FLEXPART_ROOT_SCRIPTS"}
+},
+"cv": {
+"script": "submit.py",
+"control": "CONTROL_CV",
+"inputdir": "$SCRATCH/workcv",
+"start_date": "20131107",
+"local_flexpart": {"flexpart_root_scripts":"$FLEXPART_ROOT_SCRIPTS"}
+},
+"hires": {
+"script": "submit.py",
+"control": "CONTROL_HIRES",
+"inputdir": "$SCRATCH/workhires",
+"local_flexpart": {"flexpart_root_scripts":"$FLEXPART_ROOT_SCRIPTS"},
+"ecgate": {"queue":"ecgate"}
+},
+"hiresgauss": {
+"script": "submit.py",
+"control": "CONTROL_HIRESGAUSS",
+"inputdir": "$SCRATCH/workhiresgauss",
+"basetime": "00",
+"local_flexpart": {"flexpart_root_scripts":"$FLEXPART_ROOT_SCRIPTS"},
+"ecgate": {"queue":"ecgate"},
+"cca": {"queue":"cca"}
+},
+"ops": {
+"script": "submit.py",
+"start_date": "20131108",
+"control": "CONTROL_OPS_V6.0_4V.temp",
+"inputdir": "$SCRATCH/workops2",
+"local_flexpart": {"basetime":"00"},
+"local_flexpart12": {"basetime":"12"},
+"ecgate": {"queue":"ecgate","basetime":"00"}
+},
+"opsfc": {
+"script": "submit.py",
+"start_date": "20131108",
+"control": "CONTROL_OPS_V6.0",
+"inputdir": "$SCRATCH/workopsfc",
+"local_flexpart": {"basetime":"00"},
+"local_flexpart12": {"basetime":"12"},
+"ecgate": {"queue":"ecgate","basetime":"00"}
+}
+}
Index: Source/Pythontest/pytest.ini
===================================================================
--- Source/Pythontest/pytest.ini	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
+++ Source/Pythontest/pytest.ini	(revision ba9923062b4af98cbdd20c4101c305766fc728f5)
@@ -0,0 +1,5 @@
+# content of pytest.ini
+[pytest]
+markers =
+    msuser_pw: Test that can be executed only as a member-state user. Password required.
+    gateway: Test that can be executed only in the gateway mode.
