Wednesday, August 31, 2011

Structure of the TELEMAC Selafin 3D File

Blue Kenue is a great tool useful for pre and post processing TELEMAC models. However there are times were you want to do some more in depth analysis. I have been looking into developing velocity profiles from my model runs, something which is not possible from Blue Kenue.(EDIT: Blue Kenue does allow the extraction of a vertical profile, simply select a node of your velocity grid (in a 2D view is best) and to bring up the context menu and choose “Extract Vertical Profile” - Thanks Martin Serrer for alerting me to that!) It does have tools to export model runs to ASCII files however some of these tools are limited. There was this discussed on the Open Telemac Forum related to his issue that got me started.

Fortan

The first thing that you will need to do is extract your model results from the Selafin file. The Selafin file is in Binary format which makes things a little complex. The structure of a Selafin file is well described in Appendix 3 of the TELEMAC-2D User Manual. I have not programmed in Fortran since 1990, but I thought I would give it a try.


PROGRAM ReadSelaphin 
! http://www.opentelemac.org/index.php?option=com_kunena&func=view&catid=20&id=1162&Itemid=62&lang=en
INTEGER IERR
CHARACTER(LEN=80) :: strTitle 
CHARACTER(LEN=32) :: strVar
INTEGER I, NBV1, NBV2, NELEM,NPOIN,NDP, TEMP, ERROR
INTEGER IPARAM(10)
INTEGER, allocatable :: IKLE(:,:) 
INTEGER, allocatable :: IPOBO(:)
REAL*4, allocatable :: X(:)
REAL*4, allocatable :: Y(:)
REAL*4  TIME
REAL*4 , allocatable :: RES(:)


OPEN(1, FILE="C:\Telemac\bin\Yu_3d_sm_240940_5.slf", FORM="UNFORMATTED",STATUS='OLD',IOSTAT=IERR) 
IF(IERR.NE.0) PRINT*,'ERROR ',IERR


OPEN(2, FILE="C:\Telemac\bin\Sta2.txt", FORM="FORMATTED")


READ(1, END=5, ERR=7) strTitle(1:80)
WRITE(2,*) strTitle(1:80)
!1 record containing the two integers NBV(1) and NBV(2) (NBV(1) the number of variables, NBV(2) with the value of 0),
READ(1, END=5, ERR=7) NBV1, NBV2
WRITE(2,*) NBV1, NBV2
! NBV(1) records containing the names and units of each variable (over 32 characters),
DO 20 j=1, NBV1
    READ(1, END=5, ERR=7) strVar(1:32)
    WRITE(2,*) strVar(1:32)
20 continue
! 1 record containing the integers table IPARAM (10 integers, of which only 4 are currently being used). 
READ(1, END=5, ERR=7) IPARAM(1:10)
WRITE(2,*) IPARAM(1:10)
READ(1, END=5, ERR=7) NELEM,NPOIN,NDP, TEMP
WRITE(2,*) NELEM,NPOIN,NDP, TEMP


WRITE(2,*) "=================IKLE================"
!1 record containing table IKLE (integer array of dimension (NDP,NELEM) which is the connectivity table
 allocate(IKLE(NDP,NELEM),stat=ERROR)
 if(ERROR.ne.0) THEN 
    PRINT*, "Could not allocate memory IKLE"
    GO TO 1000
 end if
READ(1, END=5, ERR=7) IKLE(1:NDP,1:NELEM)
WRITE(2,*) IKLE(1:NDP,1:NELEM)


WRITE(2,*) "=================IPOBO================"
!1 record containing table IPOBO (integer array of dimension NPOIN); 
allocate(IPOBO(NPOIN),stat=ERROR)
 if(ERROR.ne.0) THEN 
    PRINT*, "Could not allocate memory IPOBO"
    GO TO 1000
 end if
READ(1, END=5, ERR=7) IPOBO(1:NPOIN)
WRITE(2,*) IPOBO(1:NPOIN)


WRITE(2,*) "=================X================"
!1 record containing table X (real array of dimension NPOIN containing the abscissas of the points),
allocate(X(NPOIN),stat=ERROR)
 if(ERROR.ne.0) THEN 
    PRINT*, "Could not allocate memory IPOBO"
    GO TO 1000
 end if
READ(1, END=5, ERR=7) X(1:NPOIN)
WRITE(2,*) X(1:NPOIN)


WRITE(2,*) "=================Y================"
!1 record containing table Y (real array of dimension NPOIN containing the ordinates of the points),
allocate(Y(NPOIN),stat=ERROR)
 if(ERROR.ne.0) THEN 
    PRINT*, "Could not allocate memory IPOBO"
    GO TO 1000
 end if
READ(1, END=5, ERR=7) Y(1:NPOIN)
WRITE(2,*) Y(1:NPOIN)
! FOR EACH TIMESTEP
allocate(RES(NPOIN),stat=ERROR)
if(ERROR.ne.0) THEN 
    PRINT*, "Could not allocate memory RES"
    GO TO 1000
end if
do 
    READ(1, END=5, ERR=7) TIME
    WRITE(2,*) TIME
    ! For each record
    DO 30 j=1, NBV1
        READ(1, END=5, ERR=7) RES(1:NPOIN)
        WRITE(2,*) RES(1:NPOIN)
    30 continue
end do


5 PRINT*,'END OF fILE 1'
GO TO 1000
7 PRINT*,'ERROR IN FILE 1'
GO TO 1000
999 WRITE(2,*) strTitle(1:80)
1000 CONTINUE
CLOSE(1)
CLOSE(2)
STOP 
END


Please excuse any errors with this file - I have run it successfully but have not had the chance to verify the results. This is because I ended up using MATLAB. I found a nice set of scripts for reading and writing Selafin files on the MATLAB website. It has a set of m-files used to read and write the Selafin file. The main ones that I was using were:


  • telheadr.m - reads the header info from a Selafin file.
  • telstepr.m - reads a specified time step
  • telmean.m - calculates the mean from all time-steps.



MATLAB
I needed to generate the mean flow velocities over a specific timespan so I modified telmean.m to take in a start and end timestep. Next I developed my own script to parse the m.RESULT variable and plot the profiles.

The variables for each time step are stored in an (NPOIN * NBV x 1) array- where NPOIN is the number of points and NBV is the number of variables (e.g. elevation, U, V, W for my model runs). This is a very long array and parsing it into the variables is a somewhat complex task.

In the MATAB scripts, the array is accessible through the variable m.RESULT. The RESULT file for a 3D Selafin file is written out by the vertical layers. Each layer has NPOIN/NPLAN points, where NPLAN is the number of vertical layers. The parser that I wrote ends up creating and populating X,Y,Z,U,V,W arrays which are much more easy to access. It then generates a velocity profile along one of my "channels". There is the code:


function m = telplotprofile(INFILE,CHANNEL,NCCHAN,vectorScale,INTERVAL)
%****M* Telemac/telplotprofile.m
% NAME
% telplotprofile.m

% PURPOSE
% Reads a processed Telemac file (e.g. mean of set of time steps) and plots
% the velocity profile. Assumes that the channel mesher has been used.
%
% USAGE
%       m = telplotprofile(INFILE,CHANNEL,vectorScale,INTERVAL)

% INPUTS
%       INFILE - the input file
%       CHANNEL - the channel number
%       vectorScale - scaling factor for the 
%       INTERVAL - spacing of the velocity profiles (1 = all, 2 = every
%       second)
%       NCCHAN - number of cross-channel elements

% Example Usage:
%   inputFile = 'C:\Working\Civil850\Analysis\Yu_3d_sm_240940_5';
%   meanFile = 'C:\Working\Civil850\Analysis\Yu_3d_sm_240940_5_mean.slf';
%   telmean2(inputFile,41, 100, meanFile);
%   telplotprofile(meanFile,2,9,0.02,3);
%
% author: Patrick Grover
% email: patrick.grover@queensu.ca
% release date: 31-Aug-2011


% For testing run telplotprofile('') from the command line
if isempty(INFILE)
    INFILE ='C:\Working\Civil850\Analysis\Yu_3d_sm_240940_5_mean.slf';
    CHANNEL = 9; % The channel to plot INPUT
    vectorScale = 0.02;
    INTERVAL = 1;
    NCCHAN = 9; % number of cross-channel elements
end


% Read the output file which now has the averaged values
m = telheadr(INFILE);


m.NPOINCHAN = m.NPOIN / (m.NPLAN*NCCHAN);  % the number of points along a profile (downstream
m.NPOINLAY = m.NPOIN / (m.NPLAN);         % the number of points on a vertical layer


m.X = zeros(m.NPOINCHAN,1); 
m.Y = zeros(m.NPOINCHAN,1); 
m.Z = zeros(m.NPOINCHAN,m.NPLAN); 
m.U = zeros(m.NPOINCHAN,m.NPLAN);
m.V = zeros(m.NPOINCHAN,m.NPLAN); 
m.W = zeros(m.NPOINCHAN,m.NPLAN);


m = telstepr(m,1); %since is an average file - read only the first timestep


% Read in the X and Y values
for i=1 : m.NPOINCHAN
    POINT = ((CHANNEL-1)*m.NPOINCHAN) + i;
    m.X(i) =m.XYZ(POINT,1);
    m.Y(i) =m.XYZ(POINT,2);
    %fprintf('\nX = %d\n',m.X(i));
    %fprintf('\nY = %d\n',m.Y(i));


end


% now read in the z values
for layer=1:m.NPLAN
    for i=1 : m.NPOINCHAN
        POINT = ((CHANNEL-1)*m.NPOINCHAN) + i;
        %fprintf('\nPOINT = %d\n',POINT);
        POINT = POINT + ((layer-1)*m.NPOINLAY);
        %fprintf('\nPOINT = %d\n',POINT);
        m.Z(i,layer) =m.RESULT(POINT);
        %fprintf('\nZ = %d\n',m.Z(i));


    end
end


% now read in the U,V,W values
for layer=1:m.NPLAN
    for i=1 : m.NPOINCHAN
        POINT = ((CHANNEL-1)*m.NPOINCHAN) + i;
        %fprintf('\nPOINT = %d\n',POINT);
        POINT = POINT + ((layer-1)*m.NPOINLAY);
        
        POINTU = POINT + m.NPOIN;        
        POINTV = POINT + m.NPOIN*2;
        POINTW = POINT + m.NPOIN*3;
        
        m.U(i,layer) = m.RESULT(POINTU);
        m.V(i,layer) = m.RESULT(POINTV);
        m.W(i,layer) = m.RESULT(POINTW);


    end
end


umax = max(max(m.U()));
m.U = m.U ./ umax; 
figure; hold on; 
for i=1 : INTERVAL: m.NPOINCHAN
    bFlag = 1; % a simple flag that indicates if it is the first time through
   for layer=1:m.NPLAN
        X0 = m.X(i);
        Z0 = m.Z(i,layer);
        X1 = X0 + (m.U(i,layer))*vectorScale;
        Z1 = Z0;
        VX = [X0 X1];
        VY = [Z0 Z1];
        if m.U(i,layer) > 0
            plot(VX,VY,'k');
        else
            plot(VX,VY,'r');
        end
        if bFlag~=1
            BackX = [X0OLD0 X0];
            BackZ = [Z0OLD0 Z0];
            plot(BackX,BackZ,'k');
            FrontX = [X0OLD1 X1];
            FrontZ = [Z0OLD1 Z1];
            plot(FrontX,FrontZ,'k');
            
        end
        bFlag = 0;
        X0OLD0 = X0;
        X0OLD1 = X1;
        Z0OLD0 = Z0;
        Z0OLD1 = Z1;
   end
end


% Finally plot the surface.
bFlag = 1; % a simple flag that indicates if it is the first time through
for i=1: m.NPOINCHAN
   X1 = m.X(i);
   Z1 = m.Z(i,1);
   if bFlag~=1;
       BottomX = [X0 X1];
       BottomZ = [Z0 Z1];
       fprintf('\nZ = %d\n',BottomZ);
       plot(BottomX,BottomZ,'k');
   end
   X0 =X1;
   Z0 = Z1;
   bFlag = 0;
end
hold off;

This generates the following velocity profile graph:






Wednesday, August 24, 2011

Specifying the Velocities at the Boundaries

Often it is necessary to specify the velocity direction for the boundary condition using PRESCRIBED VELOCITIES=2. Below is an example showing the velocity component for flow in a 45 degree direction.


4 5 5 0.0 0.625 0.625 0.0 4 0.0 0.0 0.0 429 1
4 5 5 0.0 0.625 0.625 0.0 4 0.0 0.0 0.0 643 2
4 5 5 0.0 0.625 0.625 0.0 4 0.0 0.0 0.0 857 3
4 5 5 0.0 0.625 0.625 0.0 4 0.0 0.0 0.0 1071 4
2 2 2 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 1072 5
2 2 2 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 1073 6
2 2 2 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 1074 7
2 2 2 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 1075 8
2 2 2 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 1076 9
2 2 2 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 1077 10
2 2 2 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 1078 11

5 4 4 0.0 0.625 0.625 0.0 4 0.0 0.0 0.0 1284 217
5 4 4 0.0 0.625 0.625 0.0 4 0.0 0.0 0.0 1070 218


LIHBOR, LIUBOR, LIVBOR, HBOR, UBOR, VBOR, AUBOR, LITBOR, TBOR
ATBOR, BTBOR, N, K



LIHBOR, LIUBOR, LIVBOR, and LITBOR are the boundary type codes for each of
the variables


For most of our work the first three values will be

2 2 2  Solid boundary.
4 5 5  Free H, prescribed Q
5 4 4  Prescribed H, free velocities.



HBOR (real) represents the prescribed depth if LIHBOR = 5.
UBOR (real) represents the prescribed velocity U if LIUBOR = 6.
VBOR (real) represents the prescribed velocity V if LIVBOR = 6.

However if PRESCRIBED VELOCITIES=2,  In the case of a prescribed flowrate, then UBOR and VBOR are multiplied by a constant in order to reach the prescribed flowrate.




Section 4.2.2/4.2.3 of the Telemac2D manual provides additional information.




Compiling TELEMAC System v6p1

With the latest release of Telemac v6p1, I decided it was time to figure out how to compile it from the source code. Checked out a copy from Subversion (http://svn.opentelemac.org/svn/opentelemac/) from /tags/v6p1 to my local Telemac folder. Note that I already have a version of Telemac running and am running the Intel Fortran compiler.


  1. Add the following to the PATH User variable: C:\Telemac\v6p1\bin
  2. Create the following User Variable: SYSTELCFG with value: C:\Telemac\v6p1\config
  3. Under C:\Telemac\v6p1\config, I made a copy of systel-all.ini and renamed it to systel.ini. 
  4. In the systel.ini file I set the following parameters: PROJECT=C:\Telemac\v6p1, HOSTTYPE=wintel32s, PERLPATH=C:\Telemac\perl\bin, PERL5LIB=C:\Telemac\perl\lib.
  5. Ran cfgmak from C:\Telemac\v6p1\bin
  6. Opened the command window from my Intel Fortran Compiler. Navigate to C:\Telemac\v6p1\bin and run MAKEALL90. This should compile all of the libraries. Did not no anything special such as to compile parallel sources. 
  7. Once I finished compiling I found that I needed to edit the runtel.pl located in the same directory as in the previous step. Find the line: $command=join "","./", "delete_",$PARA,$WORKING,$fileToDelete; and remove the ./  .

Saturday, August 13, 2011

Channel Mesh - Setting the name for the mesh

TELEMAC requires that the mesh be named 'BOTTOM'. This is edited in Blue Kenue. When using the T3 Channel Mesher, it is sometimes not obvious how to set the name. Here is the process:

  1. Generate the mesh.
  2. Open the properties of the new mesh.
  3. Select the Mesh tab.
  4. Select the Meta Data sub-tab.
  5. Set the Name keyword to 'BOTTOM'

Tuesday, August 9, 2011

Velocity Profiles in Matlab

I had tried using the built-in function, quiver,  included in MATLAB for generating vector plots but did not really do what I wanted. I desired a little more control over the style. I wrote some of my own code to build it up manually.


%======================================
% Script for plotting velocity profiles
%
%
% Created: August 8, 2011
%======================================
clear;
clf;

mdl = importdata('C:\Working\Civil850\Analysis\qryProfile_ke_4.csv',',',1);

x = mdl.data(:,1);
y = mdl.data(:,2);
z = mdl.data(:,3);
u = mdl.data(:,4);
v = mdl.data(:,5);

vmax = max(v);
umax = max(u);

u = u / umax;
v = v / umax;

vectorScale = 0.05;

figure; hold on;

XOld = x(1);
YOld = y(1);

X1Old = x(1);
Y1Old = y(1);

for i = 1:length(x)
X0 = x(i);
Y0 = y(i);
X1 = X0 + (u(i))*vectorScale;
%Y1 = Y0 + (v(i))*vectorScale;
Y1 = Y0;
VX = [X0 X1];
VY = [Y0 Y1];
if u(i) > 0
plot(VX,VY,'k');
else
plot(VX,VY,'r');
end

if X0 ~= XOld
aX = [XOld XOld];
aY = [YOld y(i-1)];
plot(aX,aY,'k');
YOld = Y0;
else
bX = [X1Old, X1];
bY = [Y1Old, Y1];
plot(bX,bY,'k');
end

XOld = x(i);
X1Old = X1;
Y1Old = Y1;
end

% Finally the last vertical line
aX = [XOld XOld];
aY = [YOld y(length(y))];
plot(aX,aY,'k');

hold off;