Esempio n. 1
0
void EQS_KL2D::Execute( PROJECT* project, int steadyFlow )
{
  MODEL* model    = project->M2D;
  GRID*  rg       = project->M2D->region;

  NODE** node     = model->node;
  ELEM** elem     = model->elem;

  int np          = model->np;
  int ne          = model->ne;

  int diverged_cg = 0;

  double  theNorm = -1.0;

  double cm = project->KD.cm * project->KD.cd;

  model->Incinit();

  // print information on actual iteration -----------------------------------------------
  project->PrintTheCycle( 1 );
  REPORT::rpt.PrintTime( 1 );

  // set parameters according to time integration and relaxation -------------------------
  double th = project->timeint.thetaTurb;
  double dt = project->timeint.incTime.Getsec();

  if( fabs(th) < 1.0e-10  ||  fabs(dt) < 1.0e-10 )
  {
    REPORT::rpt.Error( kParameterFault, "theta or timeInterval too small (EQS_KL2D::execute - 1)" );
  }

  double thdt = 1.0 / dt / th;

  double dt_K;
  double relaxDt_KD = project->timeint.relaxTimeTurb.Getsec();

  int relaxMethod = project->relaxMethod;

  if( steadyFlow )
  {
    if( relaxMethod >= 3 ) // stationary flow time relaxed computation
    {
      dt_K = relaxDt_KD;
      relaxThdt_KD = 1.0 / dt_K;
    }
    else
    {
      dt_K = dt;
      relaxThdt_KD = 0.0;
    }

    for( int i=0; i<np; i++ )  node[i]->v.dKdt = 0.0;
  }

  else // instationary flow
  {
    if( relaxMethod >= 3 ) // instationary flow time relaxed computation
    {
      dt_K = relaxDt_KD;
    }
    else
    {
      dt_K = dt;
    }

    relaxThdt_KD = 1.0 / dt_K / th;

    // time prediction -------------------------------------------------------------------

    for( int i=0; i<np; i++ )
    {
      VARS* v = &(node[i]->v);
/*
      VARS* vo = &(node[i]->vo);

      v->K = vo->K  +  vo->dKdt * dt;

      v->dKdt = (1.0 - 1.0/th)*vo->dKdt + thdt*(v->K - vo->K);
*/
      v->dKdt = 0.0;
    }
  }


  if( model->Getinit() != modelInit )
  {
    initStructure = true;
    modelInit = model->Getinit();
  }


  // determine friction coefficients -----------------------------------------------------

  model->DoFriction( project );


  // set KD boundary conditions ----------------------------------------------------------

  model->SetBoundKD( project );


  // set up equation numbers -------------------------------------------------------------

  project->fix[0] = BCON::kFixK;

  project->elemKind = ELEM::kRegion;

  SetEqno( model, 1, 0, 0, project->fix, project->elemKind );

  double* B = (double*) MEMORY::memo.Array_eq( neq );
  double* X = (double*) MEMORY::memo.Array_eq( neq );

  if( !B || !X )
    REPORT::rpt.Error( kMemoryFault, "can not allocate memory (EQS_KL2D::execute - 2)" );


  // -------------------------------------------------------------------------------------

  for( int it=0; it<project->actualCycit; it++ )
  {
    double relax;

    int conv = true;


    // print information on actual iteration ---------------------------------------------

    if( it )
    {
      project->PrintTheCycle( it+1 );
      REPORT::rpt.PrintTime( 2 );
    }


    // compute dissipation ---------------------------------------------------------------

    Dissipation( project );


    // initialize Reynolds stresses and eddy viscosity -----------------------------------

    rg->Turbulence( project );


    // solve equations with frontal solving algorithm ------------------------------------

    for( int i=0; i<neq; i++ ) X[i] = 0.0;

    diverged_cg = Solve( model, neq, B, X, project );


    // statistics ------------------------------------------------------------------------

    int    noAbs, noPer;
    double maxAbs, maxPer, avAbs, avPer;

    REPORT::rpt.Message( 1, "\n\n %s\n\n %s\n\n",
                            "(KLCycle)       convergence parameters ...",
                            " variable   node          average        maximum" );


    Update( model, &project->subdom,
            X, 0, kVarK, &maxAbs, &maxPer, &avAbs, &avPer, &noAbs, &noPer );

    REPORT::rpt.Message( 1, "      %1c     %5d       %12.5le   %12.5le %s\n",
                            'K', noAbs, avAbs, maxAbs, "     (abs)" );

    REPORT::rpt.Message( 1, "      %1c     %5d       %12.5lf   %12.5lf %s\n\n",
                            ' ', noPer, avPer, maxPer, "     ( % )" );

    if( fabs(maxAbs) > project->convKD )  conv = false;


    // determine relaxation parameter for NEWTON-RAPHSON ---------------------------------

    double minK = 0.0;
    double maxK = 0.0;

    switch( relaxMethod )
    {
      case 2:
        theNorm = 0.0;
        maxK    = 0.0;

        for( int i=0; i<np; i++ )
        {
          int    eqno;
          double dK = 0.0;

          eqno = GetEqno( node[i], 0 );
          if( eqno >= 0 )  dK = fabs( X[eqno] );

          theNorm += dK*dK;

          if( dK > maxK )  maxK = dK;
        }

        theNorm = sqrt( theNorm );

        relax = project->maxDeltaKD / maxK;

        if( relax > 1.0 )  relax = 1.0;

        REPORT::rpt.Message( 1, "\n %s %12.4le\n %s %12.4le\n\n",
                                "                relaxation:   norm  =", theNorm,
                                "                              relax =", relax );
        break;

      case 3:
      case 4:
        REPORT::rpt.Message( 1, "\n %s %12.4le\n",
                                "(KLCycle)       relaxed time: dt_K =", dt_K );

        if( dt_K < dt )  conv = false;

        theNorm = 0.0;
        maxK    = 0.0;

        for( int i=0; i<np; i++ )
        {
          int    eqno;
          double dK = 0.0;

          eqno = GetEqno( node[i], 0 );
          if( eqno >= 0 )  dK = fabs( X[eqno] );

          theNorm += dK*dK;

          if( dK > maxK )  maxK = dK;
        }

        theNorm = sqrt( theNorm );

        relax = project->maxDeltaKD / maxK;

        dt_K *= relax;

        if( dt_K > dt )          dt_K = dt;
        if( dt_K < relaxDt_KD )  dt_K = relaxDt_KD;

        if( steadyFlow ) relaxThdt_KD = 1.0 / dt_K;
        else             relaxThdt_KD = 1.0 / dt_K / th;

        if( relax > 1.0 )  relax = 1.0;

        REPORT::rpt.Message( 1, "\n %s %12.4le\n %s %12.4le\n\n",
                                "                relaxation:   norm  =", theNorm,
                                "                              relax =", relax );
        break;

      default:
        relax = 1.0;

        theNorm = 0.0;

        for( int i=0; i<np; i++ )
        {
          int    eqno;
          double dK = 0.0;

          eqno = GetEqno( node[i], 0 );
          if( eqno >= 0 )  dK = fabs( X[eqno] );

          theNorm += dK*dK;
        }

        theNorm = sqrt( theNorm );

        REPORT::rpt.Message( 1, "\n %s %12.4le\n %s %12.4le\n\n",
                                "                relaxation:   norm  =", theNorm,
                                "                              relax =", relax );
        break;
    }


    // update ----------------------------------------------------------------------------

    for( int i=0; i<np; i++ )
    {
      int n = GetEqno( node[i], 0 );
      if( n >= 0 )  node[i]->v.K += relax * X[n];
    }


    // check for range of values ---------------------------------------------------------

    int first = true;
    int jk = 0;

    for( int i=0; i<np; i++ )
    {
      if( first )
      {
        first = false;
        minK =
        maxK = node[i]->v.K;
      }
      else
      {
        if( node[i]->v.K < minK )  minK = node[i]->v.K;
        if( node[i]->v.K > maxK )  maxK = node[i]->v.K;
      }

      if( node[i]->v.K <= 0.0 )
      {
//        node[i]->v.K = project->minK;
        node[i]->v.K = sqrt( node[i]->vt * node[i]->v.D / cm );

        if( GetEqno(node[i],0) >= 0 )  jk++;
      }
    }


    if( jk )
    {
      REPORT::rpt.Message( 1, " (KLCycle)       %d %s\n\n", jk, "nodes with K out of range");
    }


    REPORT::rpt.Message( 1, " (KLCycle)       minimum of K: %le\n", minK );
    REPORT::rpt.Message( 1, "                 maximum of K: %le\n", maxK );


    // compute midside values (linear interpolation) -------------------------------------

    for( int e=0; e<ne; e++ )
    {
      ELEM* el = elem[e];

      int ncn = el->Getncn();
      int nnd = el->Getnnd();

      for( int i=ncn; i<nnd; i++ )
      {
        // get left and right corner node to midside node i

        int    il, ir;
        double left, rght;

        el->GetQShape()->getCornerNodes( i, &il, &ir );

        left = el->nd[il]->v.K;
        rght = el->nd[ir]->v.K;
        el->nd[i]->v.K = 0.5 * (left + rght);
      }
    }


    // compute time derivatives ----------------------------------------------------------

    rg->ReportCuPe( dt, project->vk );


    if( !steadyFlow )
    {
      double iTheta;

      iTheta  = 1.0  -  1.0 / project->timeint.thetaTurb;

      for( int i=0; i<np; i++ )
      {
        if(     isFS(node[i]->flag, NODE::kDry)
            ||  isFS(node[i]->flag, NODE::kMarsh) )
        {
          node[i]->v.dKdt = 0.0;
        }

        else
        {
          double K, pK, pdKdt;
          VARS *v, *vo;

          v  = &(node[i]->v);
          vo = &(node[i]->vo);

          K     = v->K;
          pK    = vo->K;
          pdKdt = vo->dKdt;


          // compute derivatives at actual time step

          node[i]->v.dKdt = iTheta*pdKdt + thdt*(K - pK);
        }
      }
    }


    if( conv )  break;
  }


  MEMORY::memo.Detach( B );
  MEMORY::memo.Detach( X );


  if( diverged_cg )  project->errLevel |= diverged_cg | kErr_no_conv_cg;
}
Esempio n. 2
0
void EQS_KD2D::Execute( PROJECT* project, int steadyFlow, int shape )
{
  switch( shape )
  {
    case 0:
      linearShape  = false;
      quarterShape = false;
      break;
    case 1:
      linearShape  = true;
      quarterShape = false;
      break;
    case 2:
      linearShape  = false;
      quarterShape = true;
      break;
  }

  MODEL* model    = project->M2D;
  GRID*  rg       = project->M2D->region;

  NODE** node     = model->node;
  ELEM** elem     = model->elem;

  int np          = model->np;
  int ne          = model->ne;

  int diverged_cg = 0;

  double* B    = NULL;
  double* xKD  = NULL;
  double* xKDo = NULL;
  double* cxKo = NULL;
  double* cxDo = NULL;

  model->Incinit();

  // print information on actual iteration -----------------------------------------------
  project->PrintTheCycle( 1 );
  REPORT::rpt.PrintTime( 1 );

  // set parameters according to time integration and relaxation -------------------------
  double th = project->timeint.thetaTurb;
  double dt = project->timeint.incTime.Getsec();

  if( fabs(th) < 1.0e-10  &&  fabs(dt) < 1.0e-10 )
  {
    REPORT::rpt.Error( kParameterFault,
                       "theta and timeInterval too small (EQS_KD2D::execute - 1)" );
  }

  double thdt = 1.0 / dt / th;

  double dt_KD;
  double relaxDt_KD = project->timeint.relaxTimeTurb.Getsec();

  int relaxMethod = project->relaxMethod;


  if( steadyFlow )
  {
    if( relaxMethod >= 3 )
    {
      dt_KD = relaxDt_KD;
      relaxThdt_KD = 1.0 / dt_KD;
    }
    else
    {
      dt_KD = dt;
      relaxThdt_KD = 0.0;
    }

    for( int i=0; i<np; i++ )
    {
      node[i]->v.dKdt = 0.0;
      node[i]->v.dDdt = 0.0;
    }
  }
  else
  {
    if( relaxMethod >= 3 )
    {
      dt_KD = relaxDt_KD;
    }
    else
    {
      dt_KD = dt;
    }

    relaxThdt_KD = 1.0 / dt_KD / th;

    // time prediction -------------------------------------------------------------------
    for( int i=0; i<np; i++ )
    {
      VARS* v = &(node[i]->v);

      //VARS* vo = &(node[i]->vo);

      //v->K = vo->K  +  vo->dKdt * dt;
      //v->D = vo->D  +  vo->dDdt * dt;

      //v->dKdt = (1.0 - 1.0/th)*vo->dKdt + thdt*(v->K - vo->K);
      //v->dDdt = (1.0 - 1.0/th)*vo->dDdt + thdt*(v->D - vo->D);

      v->dKdt = 0.0;
      v->dDdt = 0.0;
    }
  }

  // check KD-values for validity (>= 0) -------------------------------------------------
  Validate( project, np, node );

  // determine friction coefficients -----------------------------------------------------
  model->DoFriction( project );

  // initialize Reynolds stresses and eddy viscosity -------------------------------------
  rg->Turbulence( project );

  // set KD boundary conditions ----------------------------------------------------------
  model->SetBoundKD( project );

  // -------------------------------------------------------------------------------------
  // in case of quartered elements:
  // compute averaged values of U, V, S, K and D for the virtual center node in quads

  int     nq   = 0;
  double* cxK  = NULL;
  double* cxD  = NULL;

  if( quarterShape )
  {
    // count number of quadrilaterals
    for( int e=0; e<ne; e++ )
    {
      if( elem[e]->Getncn() == 4 )  nq++;
    }

    // allocate memory for nq center nodes
    if( !cbuf )  cbuf = new NODE [nq];
    if( !cbuf )
    {
      REPORT::rpt.Error( kMemoryFault, "cannot allocate memory (EQS_KD2D::execute - 2)" );
    }

    cent = (NODE**)  MEMORY::memo.Array_el( ne );
    cxK  = (double*) MEMORY::memo.Array_el( ne );
    cxD  = (double*) MEMORY::memo.Array_el( ne );

    nq = 0;
    for( int e=0; e<ne; e++ )
    {
      ELEM* el  = elem[e];

      if( isFS(el->flag, ELEM::kRegion) )
      {
        int  no  = el->Getno();
        int  ncn = el->Getncn();

        cent[no] = NULL;

        if( ncn == 4 )
        {
          cent[no] = &cbuf[nq++];

          cent[no]->Setno( no );

          cent[no]->x      = 0.0;
          cent[no]->y      = 0.0;
          cent[no]->z      = 0.0;
          cent[no]->cf     = 0.0;
          cent[no]->v.U    = 0.0;
          cent[no]->v.V    = 0.0;
          cent[no]->v.S    = 0.0;
          cent[no]->v.K    = 0.0;
          cent[no]->v.D    = 0.0;
          cent[no]->v.dKdt = 0.0;
          cent[no]->v.dDdt = 0.0;

          for( int i=0; i<ncn; i++ )
          {
            cent[no]->x      += el->nd[i]->x;
            cent[no]->y      += el->nd[i]->y;
            cent[no]->z      += el->nd[i]->z;
            cent[no]->cf     += el->nd[i]->cf;
            cent[no]->v.U    += el->nd[i]->v.U;
            cent[no]->v.V    += el->nd[i]->v.V;
            cent[no]->v.S    += el->nd[i]->v.S;
            cent[no]->v.K    += el->nd[i]->v.K;
            cent[no]->v.D    += el->nd[i]->v.D;
            cent[no]->v.dKdt += el->nd[i]->v.dKdt;
            cent[no]->v.dDdt += el->nd[i]->v.dDdt;
          }

          cent[no]->x      /= ncn;
          cent[no]->y      /= ncn;
          cent[no]->z      /= ncn;
          cent[no]->cf     /= ncn;
          cent[no]->v.U    /= ncn;
          cent[no]->v.V    /= ncn;
          cent[no]->v.S    /= ncn;
          cent[no]->v.K    /= ncn;
          cent[no]->v.D    /= ncn;
          cent[no]->v.dKdt /= ncn;
          cent[no]->v.dDdt /= ncn;
        }
      }
    }
  }


  // -------------------------------------------------------------------------------------
  // iteration loop

  double dt_KDo =  0.0;
  double relaxo =  1.0;
  double maxKDo = -1.0;

  int conv;

  for( int it=0; it<project->actualCycit; it++ )
  {
    conv = true;

    // print information on actual iteration ---------------------------------------------
    if( it > 0 )
    {
      project->PrintTheCycle( it+1 );
      REPORT::rpt.PrintTime( 1 );
    }

    // initialize Reynolds stresses and eddy viscosity -----------------------------------
    if( it > 0  &&  isFS(project->actualTurb, BCONSET::kVtIterat)
                && !isFS(project->actualTurb, BCONSET::kVtPrandtlKol) )
    {
      rg->Turbulence( project );
    }

    // set up equation numbers -----------------------------------------------------------
    if( model->Getinit() != modelInit )
    {
      initStructure = true;
      modelInit = model->Getinit();

      project->fix[0] = BCON::kFixK;
      project->fix[1] = BCON::kFixD;

      project->elemKind = ELEM::kRegion;

      if( linearShape )
      {
        SetEqno( model, 2, 0, 0, project->fix, project->elemKind );
      }
      else
      {
        SetEqno( model, 2, 2, 0, project->fix, project->elemKind );
      }

      if( B )   MEMORY::memo.Detach( B );
      if( xKD ) MEMORY::memo.Detach( xKD );

      B   = (double*) MEMORY::memo.Array_eq( neq );
      xKD = (double*) MEMORY::memo.Array_eq( neq );

      // allocate memory for relaxed Newton-Rahpson
      if( relaxMethod >= 3 )
      {
        xKDo = (double*) MEMORY::memo.Array_eq( neq );

        if( quarterShape )
        {
          cxKo = (double*) MEMORY::memo.Array_el( ne );
          cxDo = (double*) MEMORY::memo.Array_el( ne );
        }
      }
    }

    // solve equations with frontal solving algorithm ------------------------------------
    for( int i=0; i<neq; i++ )  xKD[i] = 0.0;

    diverged_cg = Solve( model, neq, B, xKD, project );


    // -----------------------------------------------------------------------------------
    // determine new values for K and D at virtual center nodes

    if( quarterShape )
    {
      for( int e=0; e<ne; e++ )
      {
        ELEM* el = elem[e];

        if( isFS(el->flag, ELEM::kRegion) )
        {
          int  no  = el->Getno();
          int  ncn = el->Getncn();

          if( ncn == 4 )
          {
            Coefs( el, project, estifm, force );

            cxK[no] = force[16];
            cxD[no] = force[17];

            for( int i=0; i<8; i++ )
            {
              int eqK = GetEqno( el->nd[i], 0 );
              int eqD = GetEqno( el->nd[i], 1 );

              cxK[no] -= estifm[16][i] * xKD[eqK] + estifm[16][i+8] * xKD[eqD];
              cxD[no] -= estifm[17][i] * xKD[eqK] + estifm[17][i+8] * xKD[eqD];
            }

            cxK[no] /= estifm[16][16];

            cxD[no] -= estifm[17][16] * cxK[no];
            cxD[no] /= estifm[17][17];
          }
        }
      }
    }


    // -----------------------------------------------------------------------------------
    // statistics

    REPORT::rpt.Message( 2, "\n\n%-25s%s\n\n %s\n\n",
                            " (EQS_KD2D::Execute)", "convergence parameters ...",
                            " variable   node          average        maximum" );


    // compute averaged changes and standard deviation of K and D ------------------------

    double stdevK  = 0.0;
    double stdevD  = 0.0;

    double aveAbsK = 0.0;
    double aveAbsD = 0.0;

    for( int i=0; i<np; i++ )
    {
      int eqno;

      double dK = 0.0;
      double dD = 0.0;

      eqno = GetEqno( node[i], 0 );
      if( eqno >= 0 )  dK = xKD[eqno];

      eqno = GetEqno( node[i], 1 );
      if( eqno >= 0 )  dD = xKD[eqno];

      aveAbsK += dK;
      stdevK  += dK*dK;

      aveAbsD += dD;
      stdevD  += dD*dD;
    }

    if( quarterShape )
    {
      for( int e=0; e<ne; e++ )
      {
        ELEM* el = elem[e];

        if( isFS(el->flag, ELEM::kRegion) )
        {
          int  no  = el->Getno();
          int  ncn = el->Getncn();

          if( ncn == 4 )
          {
            double dK = cxK[no];
            double dD = cxD[no];

            aveAbsK += dK;
            stdevK  += dK*dK;

            aveAbsD += dD;
            stdevD  += dD*dD;
          }
        }
      }
    }


    // -----------------------------------------------------------------------------------

    int nptot = 0;

    for( int i=0; i<np; i++ )
    {
      NODE* nd = rg->Getnode(i);
      if( !isFS(nd->flag, NODE::kInface_DN) )  nptot++;
    }

    //////////////////////////////////////////////////////////////////////////////////////
    // MPI: broadcast statistic
#   ifdef _MPI_
    aveAbsK = project->subdom.Mpi_sum( aveAbsK );
    stdevK  = project->subdom.Mpi_sum( stdevK );

    aveAbsD = project->subdom.Mpi_sum( aveAbsD );
    stdevD  = project->subdom.Mpi_sum( stdevD );

    nptot   = project->subdom.Mpi_sum( nptot );
#   endif
    //////////////////////////////////////////////////////////////////////////////////////

    aveAbsK /= nptot;
    stdevK   = sqrt( stdevK / nptot );

    aveAbsD /= nptot;
    stdevD   = sqrt( stdevD / nptot );

    double norm = stdevK + stdevD;

    double fractK = 2.0 * sqrt( stdevK );
    double fractD = 2.0 * sqrt( stdevD );


    // compute maximum changes of K and D limited to ~95% fractile -----------------------

    int    cntK    = 0;
    int    cntD    = 0;

    int    noPerK  = 0;
    double avePerK = 0.0;
    double maxPerK = 0.0;

    int    noAbsK  = 0;
    double maxAbsK = 0.0;

    int    noPerD  = 0;
    double avePerD = 0.0;
    double maxPerD = 0.0;

    int    noAbsD  = 0;
    double maxAbsD = 0.0;

    for( int i=0; i<np; i++ )
    {
      int eqno;
      double dK = 0.0;
      double dD = 0.0;

      eqno = GetEqno( node[i], 0 );
      if( eqno >= 0 )
      {
        dK = xKD[eqno];
        if( fabs(dK) > project->maxDeltaKD  &&  fabs(dK) > fractK )
        {
          xKD[eqno] = dK/fabs(dK) * fractK;
        }
      }

      eqno = GetEqno( node[i], 1 );
      if( eqno >= 0 )
      {
        dD = xKD[eqno];
        if( fabs(dD) > project->maxDeltaKD  &&  fabs(dD) > fractD )
        {
          xKD[eqno] = dD/fabs(dD) * fractD;
        }
      }

      // maximum changes and percentage
      if( node[i]->v.K + dK > 0.0 )
      {
        if( fabs(dK) > fabs(maxAbsK) )
        {
          maxAbsK = dK;
          noAbsK = node[i]->Getname();
        }

        if( node[i]->v.K > 0.0 )
        {
          double per = dK / node[i]->v.K;

          avePerK += fabs(per);
          cntK++;

          if( fabs(per) > fabs(maxPerK) )
          {
            maxPerK = per;
            noPerK = node[i]->Getname();
          }
        }
      }

      if( node[i]->v.D + dD > 0.0 )
      {
        if( fabs(dD) > fabs(maxAbsD) )
        {
          maxAbsD = dD;
          noAbsD = node[i]->Getname();
        }

        if( node[i]->v.D > 0.0 )
        {
          double per = dD / node[i]->v.D;

          avePerD += fabs(per);
          cntD++;

          if( fabs(per) > fabs(maxPerD) )
          {
            maxPerD = per;
            noPerD = node[i]->Getname();
          }
        }
      }
    }

    if( quarterShape )
    {
      for( int e=0; e<ne; e++ )
      {
        ELEM* el = elem[e];

        if( isFS(el->flag, ELEM::kRegion) )
        {
          int  no  = el->Getno();
          int  ncn = el->Getncn();

          if( ncn == 4 )
          {
            double dK = cxK[no];
            double dD = cxD[no];

            if( fabs(dK) > project->maxDeltaKD  &&  fabs(dK) > fractK )
            {
              cxK[no] = dK/fabs(dK) * fractK;
            }

            if( fabs(dD) > project->maxDeltaKD  &&  fabs(dD) > fractD )
            {
              cxD[no] = dD/fabs(dD) * fractD;
            }

            // maximum changes and percentage
            if( cent[no]->v.K + dK > 0.0 )
            {
              if( fabs(dK) > fabs(maxAbsK) )
              {
                maxAbsK = dK;
                noAbsK  = -(no+1);
              }

              if( cent[no]->v.K > 0.0 )
              {
                double per = dK / cent[no]->v.K;

                avePerK += fabs(per);
                cntK++;

                if( fabs(per) > fabs(maxPerK) )
                {
                  maxPerK = per;
                  noPerK  = -(no+1);
                }
              }
            }

            if( cent[no]->v.D + dD > 0.0 )
            {
              if( fabs(dD) > fabs(maxAbsD) )
              {
                maxAbsD = dD;
                noAbsD  = -(no+1);
              }

              if( cent[no]->v.D > 0.0 )
              {
                double per = dD / cent[no]->v.D;

                avePerD += fabs(per);
                cntD++;

                if( fabs(per) > fabs(maxPerD) )
                {
                  maxPerD = per;
                  noPerD  = -(no+1);
                }
              }
            }
          }
        }
      }
    }

    //////////////////////////////////////////////////////////////////////////////////////
    // MPI: broadcast statistic
#   ifdef _MPI_
    cntK    = project->subdom.Mpi_sum( cntK );
    maxAbsK = project->subdom.Mpi_maxabs( maxAbsK );
    avePerK = project->subdom.Mpi_sum( avePerK );
    maxPerK = project->subdom.Mpi_maxabs( maxPerK );

    cntD    = project->subdom.Mpi_sum( cntD );
    maxAbsD = project->subdom.Mpi_maxabs( maxAbsD );
    avePerD = project->subdom.Mpi_sum( avePerD );
    maxPerD = project->subdom.Mpi_maxabs( maxPerD );
#   endif
    //////////////////////////////////////////////////////////////////////////////////////

    avePerK /= cntK;
    avePerD /= cntD;

    REPORT::rpt.Message( 2, "      %1c     %5d       %12.5le   %12.5le %s\n",
                            'K', noAbsK+1, aveAbsK, maxAbsK, "     (abs)" );

    REPORT::rpt.Message( 2, "      %1c     %5d       %12.5lf   %12.5lf %s\n\n",
                            ' ', noPerK+1, avePerK, maxPerK, "     ( % )" );


    REPORT::rpt.Message( 2, "      %1c     %5d       %12.5le   %12.5le %s\n",
                            'D', noAbsD+1, aveAbsD, maxAbsD, "     (abs)" );

    REPORT::rpt.Message( 2, "      %1c     %5d       %12.5lf   %12.5lf %s\n\n",
                            ' ', noPerD+1, avePerD, maxPerD, "     ( % )" );


    if( fabs(maxAbsK) > project->convKD )  conv = false;
    if( fabs(maxAbsD) > project->convKD )  conv = false;


    // determine relaxation parameter for NEWTON-RAPHSON ---------------------------------

    double relax;

    double maxKD = fabs(maxAbsK);
    if( fabs(maxAbsD) > maxKD )  maxKD = fabs(maxAbsD);

    switch( relaxMethod )
    {
      default:
        REPORT::rpt.Warning( kParameterFault,
                             "relaxation method %d not supported", relaxMethod );

      case 0:
        relax = 1.0;

        REPORT::rpt.Message( 2, "\n%-25s%s %12.4le\n%-25s%s %12.4le\n\n",
                                " ", "relaxation (0): norm  =", norm,
                                " ", "                relax =", relax );
        break;

      case 2:
        relax = project->maxDeltaKD / maxKD;

        if( relax > 1.0 )  relax = 1.0;

        REPORT::rpt.Message( 2, "\n%-25s%s %12.4le\n%-25s%s %12.4le\n\n",
                                " ", "relaxation (2): norm  =", norm,
                                " ", "                relax =", relax );
        break;

      case 3:
        REPORT::rpt.Message( 2, "\n%-25s%s %12.4le\n",
                                " ", "relaxed time:   dt_KD =", dt_KD );

        if( dt_KD < dt )  conv = false;

        relax = project->maxDeltaKD / maxKD;

        dt_KD *= relax;

        if( dt_KD > dt )          dt_KD = dt;
        if( dt_KD < relaxDt_KD )  dt_KD = relaxDt_KD;

        if( steadyFlow ) relaxThdt_KD = 1.0 / dt_KD;
        else             relaxThdt_KD = 1.0 / dt_KD / th;

        if( relax > 1.0 )  relax = 1.0;

        REPORT::rpt.Message( 2, "\n%-25s%s %12.4le\n%-25s%s %12.4le\n\n",
                                " ", "relaxation (3): norm  =", norm,
                                " ", "                relax =", relax );
        break;

      case 4:
        REPORT::rpt.Message( 2, "\n%-25s%s %12.4le\n",
                                " ", "relaxed time:   dt_KD =", dt_KD );

        if( dt_KD < dt )  conv = false;

        relax = project->maxDeltaKD / maxKD;

        if( relax < project->relaxMax )
        {
          if( maxKDo < 0.0  ||  maxKD < maxKDo )  // initialisation: maxKDo = -1
          {
            if( relax < project->relaxMin ) relax = project->relaxMin;

            dt_KDo = dt_KD;
            maxKDo = maxKD;
          }
          else
          {
            if( relaxo > 1.01 * project->relaxMin )
            {
              // relaxed Newton-Raphson: restore K and D from previous iteration
              for( int i=0; i<np; i++ )
              {
                int n;
                n = GetEqno( node[i], 0 );
                if( n >= 0 )
                {
                  node[i]->v.K -= relaxo * xKDo[n];
                  xKD[n] = xKDo[n];
                }
                n = GetEqno( node[i], 1 );
                if( n >= 0 )
                {
                  node[i]->v.D -= relaxo * xKDo[n];
                  xKD[n] = xKDo[n];
                }
              }

              if( quarterShape )
              {
                for( int e=0; e<ne; e++ )
                {
                  ELEM* el = elem[e];

                  if( isFS(el->flag, ELEM::kRegion) )
                  {
                    if( el->Getncn() == 4 )
                    {
                      int no = el->Getno();
                      cent[no]->v.K -= relaxo * cxKo[no];
                      cent[no]->v.D -= relaxo * cxDo[no];
                      cxK[no] = cxKo[no];
                      cxD[no] = cxDo[no];
                    }
                  }
                }
              }
            }

            if( relax < project->relaxMin ) relax = project->relaxMin;

            dt_KDo = dt_KD;
            dt_KD *= relax;           // decrease dt_KD
            if( dt_KD < relaxDt_KD ) dt_KD = relaxDt_KD;

            maxKDo = maxKD;
          }
        }
        else
        {
          dt_KDo = dt_KD;

          if( relax > 1.0 ) dt_KD *= relax;       // increase dt_KD
          if( dt_KD > dt ) dt_KD = dt;

          relax  = 1.0;

          maxKDo = maxKD;

//          if( relax > relaxo )
//          {
//            relax = 0.1 * relaxo;

//            if( relax >= project->relaxMin )
//            {
//              // relaxed Newton-Raphson: restore K and D from previous iteration

//              for( int i=0; i<np; i++ )
//              {
//                int n;
//                n = GetEqno( node[i], 0 );
//                if( n >= 0 )
//                {
//                  node[i]->v.K -= relaxo * xKDo[n];
//                  xKD[n] = xKDo[n];
//                }
//                n = GetEqno( node[i], 1 );
//                if( n >= 0 )
//                {
//                  node[i]->v.D -= relaxo * xKDo[n];
//                  xKD[n] = xKDo[n];
//                }
//              }

//              if( quarterShape )
//              {
//                for( int e=0; e<ne; e++ )
//                {
//                  ELEM* el = elem[e];

//                  if( isFS(el->flag, ELEM::kRegion) )
//                  {
//                    if( el->Getncn() == 4 )
//                    {
//                      int no = el->Getno();
//                      cent[no]->v.K -= relaxo * cxKo[no];
//                      cent[no]->v.D -= relaxo * cxDo[no];
//                      cxK[no] = cxKo[no];
//                      cxD[no] = cxDo[no];
//                    }
//                  }
//                }
//              }

//              maxKDo = maxKD;
//            }
//            else
//            {
//              maxKDo = -1.0;
//            }
//          }
//          else
//          {
//            relax  = project->relaxMax;
//            maxKDo = maxKD;
//          }

//          if( relax > project->relaxMax )  relax = project->relaxMax;
//          if( relax < project->relaxMin )  relax = project->relaxMin;

//          if( relax < 0.999 * relaxo )  conv = false;
        }

        if( steadyFlow ) relaxThdt_KD = 1.0 / dt_KD;
        else             relaxThdt_KD = 1.0 / dt_KD / th;

        REPORT::rpt.Message( 2, "\n%-25s%s %12.4le\n%-25s%s %12.4le\n\n",
                                " ", "relaxation (4): norm  =", norm,
                                " ", "                relax =", relax );

        // relaxed Newton-Raphson: store xKD, cxK and cxD
        for( int i=0; i<np; i++ )
        {
          int n;
          n = GetEqno( node[i], 0 );
          if( n >= 0 )  xKDo[n] = xKD[n];

          n = GetEqno( node[i], 1 );
          if( n >= 0 )  xKDo[n] = xKD[n];
        }

        if( quarterShape )
        {
          for( int e=0; e<ne; e++ )
          {
            ELEM* el = elem[e];

            if( isFS(el->flag, ELEM::kRegion) )
            {
              if( el->Getncn() == 4 )
              {
                int no = el->Getno();
                cxKo[no] = cxK[no];
                cxDo[no] = cxD[no];
              }
            }
          }
        }
        break;
    }


    // update ----------------------------------------------------------------------------

    relaxo = relax;

    for( int i=0; i<np; i++ )
    {
      int n;
      n = GetEqno( node[i], 0 );
      if( n >= 0 )  node[i]->v.K += relax * xKD[n];

      n = GetEqno( node[i], 1 );
      if( n >= 0 )  node[i]->v.D += relax * xKD[n];
    }


    if( quarterShape )
    {
      for( int e=0; e<ne; e++ )
      {
        ELEM* el = elem[e];

        if( isFS(el->flag, ELEM::kRegion) )
        {
          if( el->Getncn() == 4 )
          {
            int no = el->Getno();
            cent[no]->v.K += relax * cxK[no];
            cent[no]->v.D += relax * cxD[no];
          }
        }
      }
    }


    // check for range of values ---------------------------------------------------------

    double minK = 0.0;
    double minD = 0.0;
    double maxK = 0.0;
    double maxD = 0.0;

    int first = true;

    int jk = 0;
    int jd = 0;

    for( int i=0; i<np; i++ )
    {
      if( first )
      {
        first = false;
        minK =
        maxK = node[i]->v.K;

        minD =
        maxD = node[i]->v.D;
      }
      else
      {
        if( node[i]->v.K < minK )  minK = node[i]->v.K;
        if( node[i]->v.K > maxK )  maxK = node[i]->v.K;

        if( node[i]->v.D < minD )  minD = node[i]->v.D;
        if( node[i]->v.D > maxD )  maxD = node[i]->v.D;
      }

      if( node[i]->v.K <= 0.0 )
      {
        if( GetEqno(node[i],0) >= 0 )  jk++;
      }

      if( node[i]->v.D <= 0.0 )
      {
        if( GetEqno(node[i],1) >= 0 )  jd++;
      }
    }

    //////////////////////////////////////////////////////////////////////////////////////
    // MPI: broadcast statistic
#   ifdef _MPI_
    jk   = project->subdom.Mpi_sum( jk );
    minK = project->subdom.Mpi_min( minK );
    maxK = project->subdom.Mpi_max( maxK );

    jd   = project->subdom.Mpi_sum( jd );
    minD = project->subdom.Mpi_min( minD );
    maxD = project->subdom.Mpi_max( maxD );
#   endif
    //////////////////////////////////////////////////////////////////////////////////////

    // compute useful values for K and D where they are negative -------------------------
    if( jk || jd )
    {
      REPORT::rpt.Message( 3, "%-25s%d %s\n\n", " ", jk, "nodes with K out of range" );
      REPORT::rpt.Message( 3, "%-25s%d %s\n\n", " ", jd, "nodes with D out of range" );
    }

    REPORT::rpt.Message( 3, "%-25sminimum of K: %le\n", " ", minK );
    REPORT::rpt.Message( 3, "%-25smaximum of K: %le\n", " ", maxK );
    REPORT::rpt.Message( 3, "%-25sminimum of D: %le\n", " ", minD );
    REPORT::rpt.Message( 3, "%-25smaximum of D: %le\n", " ", maxD );


    Validate( project, np, node, ne, cent, elem );


    // compute midside values (linear interpolation) -------------------------------------

    if( linearShape )
    {
      for( int e=0; e<ne; e++ )
      {
        ELEM* el = elem[e];

        int ncn = el->Getncn();
        int nnd = el->Getnnd();

        for( int i=ncn; i<nnd; i++ )
        {
          // get left and right corner node to midside node i

          int    il, ir;
          double left, rght;

          el->GetQShape()->getCornerNodes( i, &il, &ir );

          left = el->nd[il]->v.K;
          rght = el->nd[ir]->v.K;
          el->nd[i]->v.K = 0.5 * (left + rght);

          left = el->nd[il]->v.D;
          rght = el->nd[ir]->v.D;
          el->nd[i]->v.D = 0.5 * (left + rght);
        }
      }
    }


    // compute time derivatives ----------------------------------------------------------

    rg->ReportCuPe( dt, project->vk );


    if( !steadyFlow )
    {
      double iTheta = 1.0  -  1.0 / project->timeint.thetaTurb;

      for( int i=0; i<np; i++ )
      {
        if(     isFS(node[i]->flag, NODE::kDry)
            ||  isFS(node[i]->flag, NODE::kMarsh) )
        {
          node[i]->v.dKdt =
          node[i]->v.dDdt = 0.0;
        }
        else
        {
          double K, pK, D, pD, pdKdt, pdDdt;
          VARS *v, *vo;

          v  = &(node[i]->v);
          vo = &(node[i]->vo);

          K     = v->K;
          pK    = vo->K;
          pdKdt = vo->dKdt;

          D     = v->D;
          pD    = vo->D;
          pdDdt = vo->dDdt;

          // compute derivatives at actual time step
          node[i]->v.dKdt = iTheta*pdKdt + thdt*(K - pK);
          node[i]->v.dDdt = iTheta*pdDdt + thdt*(D - pD);
        }
      }
    }


    // -----------------------------------------------------------------------------------

    if( conv  ||  it == project->actualCycit-1 )
    {
      if( REPORT::rpt.level == 1  &&  it == project->actualCycit-1 )
      {
        REPORT::rpt.Message( 1, "\n\n%-25s%s: %d\n\n",
                                " (EQS_KD2D::Execute)", "finished in iteration step", it+1 );

        REPORT::rpt.Message( 1, "\n\n%-25s%s\n\n %s\n\n",
                                " (EQS_KD2D::Execute)", "convergence parameters ...",
                                " variable   node          average        maximum" );

        REPORT::rpt.Message( 1, "      %1c     %5d       %12.5le   %12.5le %s\n",
                                'K', noAbsK+1, aveAbsK, maxAbsK, "     (abs)" );

        REPORT::rpt.Message( 1, "      %1c     %5d       %12.5lf   %12.5lf %s\n\n",
                                ' ', noPerK+1, avePerK, maxPerK, "     ( % )" );

        REPORT::rpt.Message( 1, "      %1c     %5d       %12.5le   %12.5le %s\n",
                                'D', noAbsD+1, aveAbsD, maxAbsD, "     (abs)" );

        REPORT::rpt.Message( 1, "      %1c     %5d       %12.5lf   %12.5lf %s\n\n",
                                ' ', noPerD+1, avePerD, maxPerD, "     ( % )" );
      }

      break;
    }
  }


  // -------------------------------------------------------------------------------------
  // finally:
  // compute eddy viscosity from revised turbulence parameters

  rg->Turbulence( project );


  // -------------------------------------------------------------------------------------

  MEMORY::memo.Detach( B );
  MEMORY::memo.Detach( xKD );

  if( cxK )  MEMORY::memo.Detach( cxK );
  if( cxD )  MEMORY::memo.Detach( cxD );
  if( cent ) MEMORY::memo.Detach( cent );
  if( xKDo ) MEMORY::memo.Detach( xKDo );
  if( cxKo ) MEMORY::memo.Detach( cxKo );
  if( cxDo ) MEMORY::memo.Detach( cxDo );


  // -------------------------------------------------------------------------------------

  if( !conv )        project->errLevel |= kErr_some_errors | kErr_no_conv_nr;
  if( diverged_cg )  project->errLevel |= diverged_cg | kErr_no_conv_cg;
}
Esempio n. 3
0
void EQS_D2D::Execute( PROJECT* project, int steadyFlow, int linearShape )
{
  MODEL* model = project->M2D;
  GRID*  rg    = project->M2D->region;

  NODE** node  = model->node;
  ELEM** elem  = model->elem;

  int np       = model->np;
  int ne       = model->ne;

  int div_cg   = 0;

  double* B    = NULL;
  double* X    = NULL;

  model->Incinit();

  // print information on actual iteration -----------------------------------------------
  project->PrintTheCycle( 1 );
  REPORT::rpt.PrintTime( 1 );

  // set parameters according to time integration and relaxation -------------------------
  double th = project->timeint.thetaTurb;
  double dt = project->timeint.incTime.Getsec();

  if( fabs(th) < 1.0e-10  &&  fabs(dt) < 1.0e-10 )
  {
    REPORT::rpt.Error( kParameterFault, "theta and timeInterval too small (EQS_D2D::execute - 1)" );
  }

  double thdt = 1.0 / dt / th;

  double dt_KD;
  double relaxDt_KD = project->timeint.relaxTimeTurb.Getsec();

  int relaxMethod = project->relaxMethod;

  if( steadyFlow )
  {
    if( relaxMethod >= 3 )
    {
      dt_KD = relaxDt_KD;
      relaxThdt_KD = 1.0 / dt_KD;
    }
    else
    {
      dt_KD = dt;
      relaxThdt_KD = 0.0;
    }

    for( int i=0; i<np; i++ )
    {
      node[i]->v.dDdt = 0.0;
    }
  }
  else
  {
    if( relaxMethod >= 3 )
    {
      dt_KD = relaxDt_KD;
    }
    else
    {
      dt_KD = dt;
    }

    relaxThdt_KD = 1.0 / dt_KD / th;

    // time prediction -------------------------------------------------------------------
    for( int i=0; i<np; i++ )
    {
      VARS* v = &node[i]->v;

      //VARS* vo = &(node[i]->vo);

      //v->D    = vo->D  +  vo->dDdt * dt;
      //v->dDdt = (1.0 - 1.0/th)*vo->dDdt + thdt*(v->D - vo->D);

      v->dDdt = 0.0;
    }
  }

  // check KD-values for validity (>= 0) -------------------------------------------------
  Validate( np, node, project );

  // determine friction coefficients -----------------------------------------------------
  model->DoFriction( project );

  // initialize Reynolds stresses and eddy viscosity -------------------------------------
  rg->Turbulence( project );

  // set KD boundary conditions ----------------------------------------------------------
  model->SetBoundKD( project );


  // -------------------------------------------------------------------------------------

  int conv = true;

  for( int it=0; it<project->actualCycit; it++ )
  {
    conv = true;

    // print information on actual iteration ---------------------------------------------
    if( it > 0 )
    {
      project->PrintTheCycle( it+1 );
      REPORT::rpt.PrintTime( 1 );
    }

    // initialize Reynolds stresses and eddy viscosity -----------------------------------
    if( it > 0  &&  isFS(project->actualTurb, BCONSET::kVtIterat)
                && !isFS(project->actualTurb, BCONSET::kVtPrandtlKol) )
    {
      rg->Turbulence( project );
    }

    // set up equation numbers -----------------------------------------------------------
    if( model->Getinit() != modelInit )
    {
      initStructure = true;
      modelInit = model->Getinit();

      project->fix[0] = BCON::kFixD;

      project->elemKind = ELEM::kRegion;

      
      SetEqno( model, 1, 1, 0, project->fix, project->elemKind );

      if( B )  MEMORY::memo.Detach( B );
      if( X )  MEMORY::memo.Detach( X );

      B = (double*) MEMORY::memo.Array_eq( neq );
      X = (double*) MEMORY::memo.Array_eq( neq );
    }

    // solve equations with frontal solving algorithm ------------------------------------
    for( int i=0; i<neq; i++ ) X[i] = 0.0;

    div_cg = Solve( model, neq, B, X, project );


    // -----------------------------------------------------------------------------------
    // statistics

    REPORT::rpt.Message( 1, "\n\n%-25s%s\n\n %s\n\n",
                            " (EQS_D2D::Execute)","convergence parameters ...",
                            " variable   node           average          maximum" );


    // compute averaged changes and standard deviation of D ------------------------------
    double stdevD  = 0.0;
    double aveAbsD = 0.0;

    for( int i=0; i<np; i++ )
    {
      int eqno;
      double dD = 0.0;

      eqno = GetEqno( node[i], 0 );
      if( eqno >= 0 )  dD = X[eqno];

      aveAbsD += dD;
      stdevD  += dD*dD;
    }

    int nptot = 0;

    for( int i=0; i<np; i++ )
    {
      NODE* nd = rg->Getnode(i);
      if( !isFS(nd->flag, NODE::kInface_DN) )  nptot++;
    }

    //////////////////////////////////////////////////////////////////////////////////////
    // MPI: broadcast statistic
#   ifdef _MPI_
    aveAbsD = project->subdom.Mpi_sum( aveAbsD );
    stdevD  = project->subdom.Mpi_sum( stdevD );

    nptot   = project->subdom.Mpi_sum( nptot );
#   endif
    //////////////////////////////////////////////////////////////////////////////////////

    aveAbsD /= nptot;
    stdevD   = sqrt( stdevD / nptot );

    double norm   = stdevD;
    double fractD = 2.0 * sqrt( stdevD );


    // compute maximum changes of K and D limited to ~95% fractile -----------------------

    int    cntD    = 0;

    int    noPerD  = 0;
    double avePerD = 0.0;
    double maxPerD = 0.0;

    int    noAbsD  = 0;
    double maxAbsD = 0.0;

    for( int i=0; i<np; i++ )
    {
      int eqno;
      double dK = 0.0;
      double dD = 0.0;

      eqno = GetEqno( node[i], 0 );
      if( eqno >= 0 )
      {
        dD = X[eqno];
        if( fabs(dD) > project->maxDeltaKD  &&  fabs(dD) > fractD )
        {
          X[eqno] = dD/fabs(dD) * fractD;
        }
      }

      if( node[i]->v.D + dD > 0.0 )
      {
        if( fabs(dD) > fabs(maxAbsD) )
        {
          maxAbsD = dD;
          noAbsD = node[i]->Getname();
        }

        if( node[i]->v.D > 0.0 )
        {
          double per = dD / node[i]->v.D;

          avePerD += fabs(per);
          cntD++;

          if( fabs(per) > fabs(maxPerD) )
          {
            maxPerD = per;
            noPerD = node[i]->Getname();
          }
        }
      }
    }

    //////////////////////////////////////////////////////////////////////////////////////
    // MPI: broadcast statistic
#   ifdef _MPI_
    cntD    = project->subdom.Mpi_sum( cntD );
    maxAbsD = project->subdom.Mpi_max( maxAbsD );
    avePerD = project->subdom.Mpi_sum( avePerD );
    maxPerD = project->subdom.Mpi_max( maxPerD );
#   endif
    //////////////////////////////////////////////////////////////////////////////////////

    avePerD /= cntD;

    REPORT::rpt.Message( 1, "      %1c     %5d       %12.5le   %12.5le %s\n",
                            'D', noAbsD+1, aveAbsD, maxAbsD, "     (abs)" );

    REPORT::rpt.Message( 1, "      %1c     %5d       %12.5lf   %12.5lf %s\n\n",
                            ' ', noPerD+1, avePerD, maxPerD, "     ( % )" );

    if( fabs(maxAbsD) > project->convKD )  conv = false;


    // determine relaxation parameter for NEWTON-RAPHSON ---------------------------------

    double relax;

    double maxKD = fabs(maxAbsD);

    switch( relaxMethod )
    {
      default:
        REPORT::rpt.Warning( kParameterFault,
                             "relaxation method %d not supported", relaxMethod );

      case 0:
        relax = 1.0;

        REPORT::rpt.Message( 1, "\n%-25s%s %12.4le\n %s %12.4le\n\n",
                                " ", "relaxation:   norm  =", norm,
                                " ", "              relax =", relax );
        break;

      case 2:
        relax = project->maxDeltaKD / maxKD;

        if( relax > 1.0 )  relax = 1.0;

        REPORT::rpt.Message( 1, "\n%-25s%s %12.4le\n%-25s%s %12.4le\n\n",
                                " ", "relaxation:   norm  =", norm,
                                " ", "              relax =", relax );
        break;

      case 3:
      case 4:
        REPORT::rpt.Message( 1, "\n%-25s%s %12.4le\n",
                                " ", "relaxed time: dt_KD =", dt_KD );

        if( dt_KD < dt )  conv = false;

        relax = project->maxDeltaKD / maxKD;

        dt_KD *= relax;

        if( dt_KD > dt )          dt_KD = dt;
        if( dt_KD < relaxDt_KD )  dt_KD = relaxDt_KD;

        if( steadyFlow ) relaxThdt_KD = 1.0 / dt_KD;
        else             relaxThdt_KD = 1.0 / dt_KD / th;

        if( relax > 1.0 )  relax = 1.0;

        REPORT::rpt.Message( 1, "\n%-25s%s %12.4le\n%-25s%s %12.4le\n\n",
                                " ", "relaxation:   norm  =", norm,
                                " ", "              relax =", relax );
        break;
    }


    // update ----------------------------------------------------------------------------

    for( int i=0; i<np; i++ )
    {
      int n = GetEqno( node[i], 0 );
      if( n >= 0 )  node[i]->v.D += relax * X[n];
    }


    // check for range of values ---------------------------------------------------------

    double minD = 0.0;
    double maxD = 0.0;

    int first = true;

    int jd = 0;

    for( int i=0; i<np; i++ )
    {
      if( first )
      {
        first = false;
        minD  =
        maxD  = node[i]->v.D;
      }
      else
      {
        if( node[i]->v.D < minD )  minD = node[i]->v.D;
        if( node[i]->v.D > maxD )  maxD = node[i]->v.D;
      }

      if( node[i]->v.D <= 0.0 )
      {
//        node[i]->v.D = project->minD;
        if( GetEqno(node[i],1) >= 0 )  jd++;
      }
    }


    // compute useful values for K and D where they are negative -------------------------
    if( jd )
    {
      REPORT::rpt.Message( 1, "%-25s%d %s\n\n", " ", jd, "nodes with D out of range" );
      Validate( np, node, project );
    }

    REPORT::rpt.Message( 1, "%-25sminimum of D: %le\n", " ", minD );
    REPORT::rpt.Message( 1, "%-25smaximum of D: %le\n", " ", maxD );


    // compute time derivatives ----------------------------------------------------------

    rg->ReportCuPe( dt, project->vk );


    if( !steadyFlow )
    {
      double iTheta;

      iTheta  = 1.0  -  1.0 / project->timeint.thetaTurb;

      for( int i=0; i<np; i++ )
      {
        if(     isFS(node[i]->flag, NODE::kDry)
            ||  isFS(node[i]->flag, NODE::kMarsh) )
        {
          node[i]->v.dDdt = 0.0;
        }

        else
        {
          double D, pD, pdDdt;
          VARS *v, *vo;

          v  = &(node[i]->v);
          vo = &(node[i]->vo);

          D     = v->D;
          pD    = vo->D;
          pdDdt = vo->dDdt;

          // compute derivatives at actual time step
          node[i]->v.dDdt = iTheta*pdDdt + thdt*(D - pD);
        }
      }
    }


    if( conv )  break;
  }


  // finally: compute eddy viscosity from revised turbulence parameters K,D --------------

  rg->Turbulence( project );

  // -------------------------------------------------------------------------------------


  MEMORY::memo.Detach( B );
  MEMORY::memo.Detach( X );


  if( !conv )   project->errLevel |= kErr_some_errors | kErr_no_conv_nr;
  if( div_cg )  project->errLevel |= div_cg | kErr_no_conv_cg;
}