#ifndef INCLUDE_NASTSTAGGEREDGRID2D_H
#define INCLUDE_NASTSTAGGEREDGRID2D_H
//-----------------------------------------------------------------------------
// NastStaggeredGrid2d.h
//-----------------------------------------------------------------------------
//
//  Copyright (C) 1998 Technische Universitaet Muenchen, Germany
//                   written by Bernhard Brueck
//
//  This file is part of Nast++
//
//-----------------------------------------------------------------------------
//  CNastStaggered Grid enthaelt die Werte fuer Druck und Geschwindigkeit
//  und ermoeglicht den Zugriff auf die geometrischen Daten.
//  Wenn Werte in das Gitter geschrieben werden, dann erfolgt immer automatisch
//  auch eine evtl. notwendige Anpassung der Randwerte.
//  Es lassen sich auch Werte erfrage, die nicht direkt auf den Gitter-
//  punkten liegen. Es wird dann das interpolierte Ergebnis zurueckgeliefert.
//  Nachdem die Geometrie veraendert wurde muss die Funktion update()
//  aufgerufen werden, damit die Raender neu berechnet werden koennen
//-----------------------------------------------------------------------------
//  Aenderungen:
//     


#include "NastConfig.h"
#include "NastObject.h"
#include "NastBox2d.h"
#include "NastGrid2d.h"
#include "NastCellField2d.h"

class CNastGeometry2d;

class CNastStaggeredGrid2d : public CNastObject
{
public:
    //-------------------------------------------------------------------------
    //                         Konstruktor + Destruktor
    //-------------------------------------------------------------------------
        
    CNastStaggeredGrid2d( const CNastGeometry2d &geom,
        		  const CNastBox2d &box,
        		  int nSizeI,
        		  int nSizeJ );

    virtual ~CNastStaggeredGrid2d();	
    
    //-------------------------------------------------------------------------
    // Zugriff auf die Werte
    //-------------------------------------------------------------------------
    //  Beim Lesen kann zu jedem Zeitpunkt davon ausgegangen werden, dass die
    //  Werte auch am Rand richtig gesetzt sind. Dazu muss der Wert am Rand
    //  von mind. einer Fluidzelle liegen. Werte die nicht am Rand des Fluids
    //  sondern komplett ausserhalb liegen (wird bei Differenzenquot. benoetig)
    //  lassen sich aber ueber westV(i,j), eastV(i,j), north(i,j) und southV(i,j)
    //  erfragen. Dazu wird im Gitter die Geometrie befragt un bei Bedarf der
    //  Wert so zurueckgeliefert, dass die Randbedungungen erfuellt sind
    //
    //  lesen von Werten:
    //    U(i,j)   horizontale Geschwindigkeitskomp. am Gitterpunkt i,j
    //    V(i,j)   vertikale Geschwindigkeitskomp. am Gitterpunkt i,j
    //    P(i,j)   Druckwert am Gitterpunkt i,j
    //
    //    westV(i,j)  V-Wert westlich von V(i,j) (auch wenn er nicht im Fluid ist)
    //    eastV(i,j)
    //
    //    northU(i,j)
    //    southU(i,j)
    //
    //  Beim Schreiben der Werte werden automatisch betroffene Randwerte
    //  angepasst.
    //
    //  schreiben von Werten:
    //    setU( int i, int j, double u );
    //    setV( int i, int j, double v );
    //    setP( int i, int j, double p );
    //
    //  sizeI()  horizontale Anzahl der Zellen (ohne Randzellen)
    //  sizeJ()  vertikale   Anzahl der Zellen (ohne Randzellen)
    //
    //  interpolierte Werte
    //    velocity(pnt)  liefert interpolierte Geschwindigkeit an beliebigen Ort
    //    pressure(pnt)  liefert interpolierten Druck  an beliebigen Ort
    //

    //  lesender und schreibender Zugriff ueber das versetzte Gitter
    double U( int i, int j ) const;
    double V( int i, int j ) const;
    double P( int i, int j ) const;

    double westV( int i, int j ) const;
    double eastV( int i, int j ) const;

    double northU( int i, int j ) const;
    double southU( int i, int j ) const;

    void setU ( int i, int j, double u );
    void setV ( int i, int j, double v );
    void setP ( int i, int j, double p );


    int sizeI() const;
    int sizeJ() const;

    // lesender Zugriff auf die Geschwindigkeitswerte
    const CNastGrid2d& gridV() const;
    const CNastGrid2d& gridU() const;
    const CNastGrid2d& gridP() const;

    CNastVector2d velocity( const CNastPoint2d &pnt ) const;
    double        pressure( const CNastPoint2d &pnt ) const;

    void update();

    //-------------------------------------------------------------------------
    //  Zeitpunkt fue die Gueltigkeit der Werte
    //-------------------------------------------------------------------------
    double time() const;
    void time( double t);

    //-------------------------------------------------------------------------
    //  Zugriff auf die Geometrie und das Flagfeld       NastVisualContext.cpp
    //-------------------------------------------------------------------------
    //
    //  geometry()   liefert die zum Gitter gehoerende Geometrie
    //  box()        Box um das gesammte versetzte Gitter
    //  cell(...)    liefert eine bestimmte Zelle
    //
    //  dx()         horizontale Gitterweite
    //  dy()         horizontale Gitterweite
    //

    const CNastGeometry2d& geometry() const;
    const CNastBox2d& box() const;
    const CNastCell2d& cell( int i, int j ) const; 
    int countFluidCells() const;

    double dx() const;
    double dy() const;

    //-------------------------------------------------------------------------
    //                                  Debug
    //-------------------------------------------------------------------------
    //  debugInfo    gibt Information ueber den Zustand des Objekts aus
    //  assertValid  testet das Objekt auf Integritaet
    //
    virtual void debugDump( CNastDumpContext &dumpContext ) const;
    virtual void assertValid() const;
    
protected:
    CNastStaggeredGrid2d( const CNastStaggeredGrid2d &other);	            // nicht impl.
    const CNastStaggeredGrid2d& operator=( const CNastStaggeredGrid2d &other );  // nicht impl.

    //-------------------------------------------------------------------------
    //                            Hilfsfunktionen
    //-------------------------------------------------------------------------

    //  Wenn die Werte ausserhalb des Fluids liegen, dann muessen sie in Ab-
    //  haengikeit der jeweiligen Randbedingung bestimmt werden
    //
    double calcWestV( int i, int j ) const;
    double calcEastV( int i, int j ) const;

    double calcNorthU( int i, int j ) const;
    double calcSouthU( int i, int j ) const;

    //  Bestimmung der Werte am Rand
    //   updateU( int i, int j)  Berechnung eine U-Werts am Rand
    //   updateV( int i, int j)  Berechnung eine V-Werts am Rand
    //   updateAll()             Flagfeld auf den neuen Stand bringen
    //                           und alle Randwerte neu berechnen
    //                           ( nur im Konstruktor und nach einer
    //                             Veraenderung der Geometrie noetig )

    void updateU( int i, int j);
    void updateV( int i, int j);
    void updateAll();

    //-------------------------------------------------------------------------
    //                            Membervariablen
    //-------------------------------------------------------------------------

    const double m_dx;
    const double m_dy;
    const int m_sizeI;
    const int m_sizeJ;

    CNastGrid2d m_gridU;	// Gitter mit den Geschwindigkeiten in x-Richtung
    CNastGrid2d m_gridV;	// Gitter mit den Geschwindigkeiten in y-Richtung
    CNastGrid2d m_gridP;	// Gitter mit den Druckwerten

    CNastCellField2d  m_cells;  // ehem. Flagfeld
    const CNastGeometry2d &m_geom;   // Referenz auf die Geometrie
    
    //  Box um alle Zellen herum
    const CNastBox2d  m_box;
    double m_time;              // Zeitpunkt, zu dem die Werte gueltig sind
};

//-------------------------------------------------------------------------
//                            inline
//-------------------------------------------------------------------------


// Zelle i,j
inline const CNastCell2d& 
CNastStaggeredGrid2d::cell( int i, int j ) const
{
    return m_cells.cell( i, j );
}

inline int 
CNastStaggeredGrid2d::countFluidCells() const
{
    return m_cells.countFluid();
}

// Wert fuer die horizontale Geschwindigkeitskomponente
inline double 
CNastStaggeredGrid2d::U( int i, int j ) const
{
    // Zugriff auf die Werte am Rand nur fuer die parallele Version
    // um die Werte auszutauschen. Bei der normalen Version sollte
    // auf diese Werte nie zugriffen werden muessen.
    NAST_ASSERT_INDEX( i > 0 );
    NAST_ASSERT_INDEX( i < m_sizeI+2 );
    NAST_ASSERT_INDEX( j > 0 );
    NAST_ASSERT_INDEX( j < m_sizeJ+1 );

    // es muss mind. eine Nachbarzelle Fluid enthalten
    NAST_ASSERT( cell(i,j).isFluid( CNastCell2d::THIS ) || 
        	 cell(i,j).isFluid( CNastCell2d::WEST ) ||
                 cell(i,j).isFluid( CNastCell2d::EAST ));

    return m_gridU( i, j );
}

// Wert fuer die vertikale Geschwindigkeitskomponente
inline double 
CNastStaggeredGrid2d::V( int i, int j ) const
{
    // Zugriff auf die Werte am Rand nur fuer die parallele Version
    // um die Werte auszutauschen. Bei der normalen Version sollte
    // auf diese Werte nie zugriffen werden muessen.
    NAST_ASSERT_INDEX( i > 0 );
    NAST_ASSERT_INDEX( i < m_sizeI+1 );
    NAST_ASSERT_INDEX( j > 0 );
    NAST_ASSERT_INDEX( j < m_sizeJ+2 );

    // es muss mind. eine Nachbarzelle Fluid entahalten
    NAST_ASSERT( cell(i,j).isFluid( CNastCell2d::THIS  ) || 
        	 cell(i,j).isFluid( CNastCell2d::SOUTH ) ||
                 cell(i,j).isFluid( CNastCell2d::NORTH ));

    return m_gridV( i, j );
}

// Wert fuer den Druck 
inline double 
CNastStaggeredGrid2d::P( int i, int j ) const
{
    // Zugriff auf die Werte am Rand nur fuer die parallele Version
    // um die Werte auszutauschen. Bei der normalen Version sollte
    // auf diese Werte nie zugriffen werden muessen.
    NAST_ASSERT_INDEX( i > 0 );
    NAST_ASSERT_INDEX( i < m_sizeI+1 );
    NAST_ASSERT_INDEX( j > 0 );
    NAST_ASSERT_INDEX( j < m_sizeJ+1 );

    // Zelle muss Fluid enthalten
    NAST_ASSERT( cell(i,j).isFluid( CNastCell2d::THIS ));

    return m_gridP( i, j );
}

//  Anzahl der Zellen in x-Richtung
inline int 
CNastStaggeredGrid2d::sizeI() const
{
    return m_sizeI;
}

//  Anzahl der Zellen in y-Richtung
inline int 
CNastStaggeredGrid2d::sizeJ() const
{
    return m_sizeJ;
}

//  Zellweite in x-Richtung
inline double 
CNastStaggeredGrid2d::dx() const
{
    return m_dx;
}

//  Zellweite in y-Richtung
inline double 
CNastStaggeredGrid2d::dy() const
{
    return m_dy;
}


// lesender Zugriff auf die Gitter direkt
inline const CNastGrid2d& 
CNastStaggeredGrid2d::gridU() const
{
    return m_gridU;
}

inline const CNastGrid2d& 
CNastStaggeredGrid2d::gridV() const
{
    return m_gridV;
}

inline const CNastGrid2d& 
CNastStaggeredGrid2d::gridP() const
{
    return m_gridP;
}

inline const CNastBox2d& 
CNastStaggeredGrid2d::box() const
{
    return m_box;
}

inline double 
CNastStaggeredGrid2d::westV( int i, int j ) const
{
    NAST_ASSERT( i >= 1);

    // wenn der Wert an einer Fluidzelle liegt braucht er 
    // nicht extra berechnet werden
    if( cell( i, j  ).isFluid( CNastCell2d::WEST  ) ||
        cell( i, j-1).isFluid( CNastCell2d::WEST ))
        return m_gridV(i-1,j);
    
    // ansonsten Berechnung durchfuehren
    return calcWestV(i,j);
}

inline double 
CNastStaggeredGrid2d::eastV( int i, int j ) const
{
    NAST_ASSERT( i+1 < m_gridV.sizeI() );

    // wenn der Wert an einer Fluidzelle liegt braucht er 
    // nicht extra berechnet werden
    if( cell( i, j  ).isFluid( CNastCell2d::EAST  ) ||
        cell( i, j-1).isFluid( CNastCell2d::EAST ))
        return m_gridV(i+1,j);
    
    // ansonsten Berechnung durchfuehren
    return calcEastV(i,j);
}

inline double 
CNastStaggeredGrid2d::northU( int i, int j ) const
{
    NAST_ASSERT( j+1 < m_gridU.sizeJ() );

    // wenn der Wert an einer Fluidzelle liegt braucht er 
    // nicht extra berechnet werden
    if( cell( i, j  ).isFluid( CNastCell2d::NORTH  ) ||
        cell( i, j+1).isFluid( CNastCell2d::WEST ))
        return m_gridU(i,j+1);

    // ansonsten Berechnung durchfuehren
    return calcNorthU(i,j);
}

inline double 
CNastStaggeredGrid2d::southU( int i, int j ) const
{
    NAST_ASSERT( j >= 1 );

    // wenn der Wert an einer Fluidzelle liegt braucht er 
    // nicht extra berechnet werden
    if( cell( i, j  ).isFluid( CNastCell2d::SOUTH  ) ||
        cell( i, j-1).isFluid( CNastCell2d::WEST ))
        return m_gridU(i,j-1);
    
    // ansonsten Berechnung durchfuehren
    return calcSouthU(i,j);
}

// setzen eines Druckwerts
inline void 
CNastStaggeredGrid2d::setP ( int i, int j, double p )
{
    NAST_ASSERT_VALID( this );

    // keine Druckwerte ausserhalb des Fluids
    NAST_ASSERT( cell(i,j).isFluid() );	
    m_gridP( i, j) = p;       // dann schreiben wie ihn
}

// die Routinen zum Setzen der Geschwindigkeitswerte sind aufwendiger
// und deshalb nicht als inline  definiert

#endif // INCLUDE_NASTSTAGGEREDGRID2D_H

