sandbox/nlemoine/groundwater/Meuvaines/aquifer_ML.h

    #include <math.h>
    #define S_HUGE 100.
    
    /* Fonction de mise à jour des cellules concernées par les conditions aux limites
       (Dirichlet et Neumann)
    */
    
    int update_BC_cells()
    {
      scalar zbmin = automatic(zb.dmin);
    
      foreach()
      {
        coord P = (coord){x,y}; 
          
        isDirichlet[] = isInPolygon(P, & PolyDirichlet ) ? 0. : 1. ;
      
        double pscal = (P.x-SegNeumannW.x)*n_int.x + (P.y-SegNeumannW.y)*n_int.y;
        isNeumann[] = (x<=XlimW) | (x>=XlimE) | (pscal<=0) ? 1. : 0.;    
      }
      
      return(0);
    }
    
    /* Fonction de mise à jour des caractéristiques hydrodynamiques locales T & S en fonction de la charge locale
       Inclut la prise en compte des conditions de Neumann via des faces à T.x[] ou T.y[] = 0.
    */
     
    int update_TS_simple(scalar zbottom, scalar ztop, scalar hgw, face vector T, scalar S)
    { 
      foreach()
      {
        S[] = (zb[]>ZMER) ? zbottom.omega_d : 1.;
        if(isDirichlet[]>0.) S[] = S_HUGE;      
      }
        
      foreach_face()
      { 
        bool noflux = (isNeumann[] > 0.) | (isNeumann[-1,0] > 0.) ;
        T.x[] = noflux ? 0. : 20.*zbottom.Ksat ;
      } 
    
      boundary({T,S});
      return(0);
    }
    
    /* Fonction de mise à jour des caractéristiques locales T et S en fonction de la configuration de l'aquifère
    (libre ou captif). Attention : nécessite que les informations de géométrie verticale aient été mises à jour
    (fonction update_geom_stack())
    */
    
    int update_TS(scalar zbottom, scalar ztop, scalar hgw, face vector T, scalar S, scalar isConfined, scalar hasThickness, scalar seep)
    { 
      scalar zbmin = automatic(zb.dmin);
    
      /* Mise à jour de S (scalar, centré) */  
      
      foreach()
      {
        S[] = isConfined[]>0. ? fmax(ztop[]-zbottom[],0.)*zbottom.Ss : zbottom.omega_d ;
          
        // si la cellule a généré du suintement au pas de temps précédent, on prévoit identiquement S = 1.
        if( (isConfined[]<1.) && (seep[]>0.) ) S[] = 1.;  
    
        // si la cellule est à charge imposée, on met très grande valeur de S pour mimer dh/dt ~ 0
        if( isDirichlet[]>0. ) S[] = S_HUGE;  
      }
      
      /* Mise à jour de T (face vector, centré sur les faces) */  
      
      foreach_face()
      { 
        // Si l'une des cellules de part et d'autre de la face ( accès par [] c'est-à-dire [0,0] ou bien [-1,0] )
        // est à l'extérieur d'une frontière à flux nul, alors on met T = 0.
        // Idem si l'aquifère est d'épaisseur nulle au centre de l'une ET l'autre des cellules.
          
        bool noflux = (isNeumann[] > 0.) || (isNeumann[-1,0] > 0.) || ( (hasThickness[] < 1.) && (hasThickness[-1,0] < 1.) );
    //    bool noflux = (isNeumann[] > 0.) || (isNeumann[-1,0] > 0.) || (hasThickness[] < 1.) || (hasThickness[-1,0] < 1.) ;
          
        if(noflux)
            T.x[] = 0.;
        else
        {
          double zt0 = hgw[]>ztop[] ? ztop[] : hgw[];
          double B0 = fmax(zt0-zbottom[],0.); 
          double zt1 = hgw[-1,0]>ztop[-1,0] ? ztop[-1,0] : hgw[-1,0];
          double B1 = fmax(zt1-zbottom[-1,0],0.); 
    
          T.x[] = 0.5*(B0+B1)*zbottom.Ksat + zbottom.Tf;
        }
      } 
    
      boundary({T,S});
      return(0);
    }
    
    /* Même fonction, avec en plus les coefficients de drainance verticale
       entre couches de la pile sédimentaire correctement mise à jour
    */
    
    int update_TS_drainance(scalar zbottom, scalar ztop, scalar hgw, face vector T, scalar S)
    {
        return(0);
    }
         
    /* Fonction pour appliquer les conditions de charge imposées (Dirichlet)
    */
    
    int apply_head_conditions()
    {
      scalar zbmin = automatic(zb.dmin);
    
      foreach()
      {
        double drainage_level = zbmin[]<=zb[] ? fmax(zbmin[],ZMER) : fmax(zb[],ZMER);
    
        if(isDirichlet[]>0.)
        {
          h1[] = fmax(zbottom1[],drainage_level);      
          h2[] = fmax(zbottom2[],drainage_level);  
        }
    
        if( (isConfined1[]<1.) && (zb[]<ZMER) && (hasThickness1[]>0.)) h1[] = ZMER;
        if( (isConfined2[]<1.) && (zb[]<ZMER) && (hasThickness2[]>0.)) h2[] = ZMER;
      }
      boundary(all);
    
      return(0);
    }
    
    int update_seepage()
    {
      scalar zbmin = automatic(zb.dmin);
    
      foreach()
      {
        double drainage_level = zbmin[]<=zb[] ? fmax(zbmin[],ZMER) : fmax(zb[],ZMER);
          
        double DL1 = fmax(drainage_level,zbottom1[]);      
        seep1[] = (h1[]>DL1) && (isConfined1[] < 1.) ? S1[]*(h1[]-DL1)/dt : 0.;
        h1[] = fmin(h1[],DL1);
    
        double DL2 = fmax(drainage_level,zbottom2[]);      
        seep2[] = (h2[]>DL2) && (isConfined2[] < 1.) ? S2[]*(h2[]-DL2)/dt : 0.;
        h2[] = fmin(h2[],DL2);      
      }
      boundary(all);
    
      return(0);
    }
    
    /* Fonction de mise à jour de la géométrie verticale de la pile sédimentaire,
       nécessaire à chaque pas de temps si on utilise l'adaptivité de la grille 2D.
       Remet en cohérence les cotes des toits, des murs et de la topo,
       et met à jour les scalaires booléens isConfined[] et hasThickness[] pour chaque couche
    */
    
    int update_geom_stack()
    {
    //  Y'a un loup sur les valeurs de h là où la formation est d'épaisseur nulle, sortir un flt non-masqué => OK dans apply_head_conditions
    
      foreach()
      {
        // BAJOCIEN + MALIERE
        zbottom1[] = maliere_mur[];
        ztop1[] = fmin(marnesPB_mur[],zb[]);
        hasThickness1[] = ztop1[] > zbottom1[] ? 1. : 0.; 
        if((h1[]<zbottom1[]) || (hasThickness1[]<1.)) h1[] = zbottom1[];
        // à adapter si couverture quaternaire : 
        // ztop1[] = quaternaire_mur[] < zb[] ? fmin(marnesPB_mur[],quaternaire_mur[]) : fmin(marnesPB_mur[],zb[]);
        isConfined1[] = ( ztop1[] < zb[] ) && ( h1[] > ztop1[] ) ? 1. : 0.;
          
        // BATHONIEN CALCAIRE
        zbottom2[] = bathonien_mur[];
        ztop2[] = zb[];//quaternaire_mur[] < zb[] ? quaternaire_mur[] : zb[];
        hasThickness2[] = ztop2[] > zbottom2[] ? 1. : 0.;
        if((h2[]<zbottom2[]) || (hasThickness2[]<1.)) h2[] = zbottom2[];
        isConfined2[] = 0.;//( ztop2[] < zb[] ) && ( h2[] > ztop2[] ) ? 1. : 0.;
    /*   
        QUATERNAIRE
        zbottom3[] = quaternaire_mur[];
        ztop3[] = zb[];
        isConfined3[] = 0.;
        hasThickness3[] = ztop3[] > zbottom3[] ? 1. : 0.;
    */
        if( (isDirichlet[]>0.) | (isNeumann[]>0.) | (hasThickness1[]<1.))
           recharge_layer[] = 0.;
        else
        {
           if((zb[]-bathonien_mur[])>0.) // si bathonien présent, il reçoit la recharge
              recharge_layer[] = 2.;
           else
           {
              if((zb[]-marnesPB_mur[])<3.) // bathonien absent et moins de 3 m de marnes => le bajocien reçoit la recharge
                recharge_layer[] = 1.;
              else
                recharge_layer[] = 0.;
           }
        }
      } // end foreach()
              
      return(0);
    }
    
    struct TimeSeries {
      int ndates;
      int nfields;
      double * time;
      double ** DATA;
    };
    
    /* Fonction de lecture des forçages P & ETP
    */
    
    int load_timedata(struct TimeSeries * _TS, char * datafile, int nskip, const char * separators, double datenum0)
    {
      char buffer[200]; 
      int ndates,ntok;
      double tt,v;
      FILE * fp;
        
      // Initialisation des pointeurs
      _TS->time = NULL;
      _TS->DATA = NULL;  
    
      if(!(fp = fopen (datafile, "r")))
      {
        printf("Failed to open data file!\n");
        return -1;
      }
      
      ndates = 0;
       
      // On zappe les lignes d'entête  
      for(int k=0;k<nskip;k++)
          fgets(buffer, 200, fp);
          
      // Lecture des n-uplets [tt,(data1,data2,...)]
      while( fgets(buffer, 200, fp)!= NULL ) // parse until end-of-file
      {
        // Get first token (tt)  
        char * strToken = strtok ( buffer, separators);
        sscanf(strToken,"%lf",&tt);
          
        if(tt>=datenum0) // pas besoin de charger les données antérieures au début de la simulation !
        {
          ndates++;
          _TS->time = (double *) realloc( _TS->time, ndates*sizeof(double));
          _TS->time[ndates-1] = tt-datenum0;
          _TS->DATA = (double **) realloc( _TS->DATA, ndates*sizeof(double *));
          _TS->DATA[ndates-1] = NULL;
            
          // On continue de décomposer la ligne courante (buffer)
          ntok = 0;
          strToken = strtok ( NULL, separators );
          while ( strToken != NULL )
          {
            sscanf(strToken,"%lf",&v);
            ntok++;
            _TS->DATA[ndates-1] = (double *) realloc( _TS->DATA[ndates-1], ntok*sizeof(double));   
            _TS->DATA[ndates-1][ntok-1] = v;
            // Get next token
            strToken = strtok ( NULL, separators );
          }
    
        } // fin test antériorité début simulation
      } // fin de lecture du fichier     
                   
      _TS->ndates = ndates;
      _TS->nfields = ntok;
      fclose(fp);
      printf("%d records with %d fields loaded from %s\n",ndates,ntok,datafile);
    
      return(0);   
    }
    
    
    int interpolate_timedata(struct TimeSeries * _TS, double tt, double ** valdate)
    {       
      if(tt <= _TS->time[0])
      {
        for(int k=0;k<_TS->nfields;k++)
          (*valdate)[k] = _TS->DATA[0][k];
        return(0);
      }
        
      if(tt >= _TS->time[(_TS->ndates)-1])
      {
        for(int k=0;k<_TS->nfields;k++)
          (*valdate)[k] = _TS->DATA[(_TS->ndates)-1][k];
        return(0);
      }
        
      int pos = 0;
      while(tt > (_TS->time[pos]) )
       pos++;
    
      double tt0 = _TS->time[pos-1];
      double tt1 = _TS->time[pos];
      double v0,v1;
      
      for(int k=0;k<_TS->nfields;k++)
      {
        v0 = _TS->DATA[pos-1][k];
        v1 = _TS->DATA[pos][k];
        (*valdate)[k] = v0 + (tt-tt0)*(v1-v0)/(tt1-tt0) ;
      }
      return(0);
    }
    
    /* Fonction de calcul de l'humidité du sol */
    
    int SMA_bucket(double * SMA, double maxStorage, double P_dt, double PE_dt,double * Excess)
    {
      (*Excess) = 0.; 
      (*SMA) += P_dt - PE_dt;
        
      if((*SMA)<0.) (*SMA) = 0.; 
      
      if((*SMA)>maxStorage)
      {
        (*Excess) = (*SMA)-maxStorage;
        (*SMA) = maxStorage; 
      } 
      return(0);   
    }
    
    int SMA_GR(double * SMA, double maxStorage, double P_dt, double PE_dt, double dt_days, bool withPerc, double * Excess)
    {
      (*Excess) = 0.; 
      double Pn,En,th_pn, th_en, Ps, Es, Sr, Sr4,Perc,CpercJ;
      
      Sr = (*SMA)/maxStorage; 
          
      if( P_dt > PE_dt )
      {
         Pn = P_dt-PE_dt;
         th_pn = tanh(Pn/maxStorage);
         Ps = maxStorage*(1.-Sr*Sr)*th_pn/(1.+Sr*th_pn); 
         En = 0.;
         Es = 0.; 
      }
      else
      {
         En = PE_dt - P_dt;
         th_en = tanh(En/maxStorage);
         Es = maxStorage*Sr*(2.-Sr)*th_en/(1.+(1.-Sr)*th_en); 
         Pn = 0.;
         Ps = 0.;           
      }    
    
      (*SMA) += (Ps-Es);
      
      if(withPerc)
      {    
        CpercJ = (9./4.)*maxStorage;
        Sr4 = (*SMA)/CpercJ;
        Sr4 *= Sr4;
        Sr4 *= Sr4; 
        Perc = (*SMA) * (1.-pow(1.+dt_days*Sr4,-0.25));
      }
      else
        Perc = 0.;
     
      (*SMA) -= Perc; 
      (*Excess) = Perc + Pn-Ps;
        
      return(0);   
    }
    
    int read_piezo_sites ( Piezo ** _PIEZO, char * datafile, int * _npiezo)
    {
      char buffer[200];
      char BSSbuf[20];
      double xsite,ysite;
      int layer;
      int npiezo;
      FILE * fp;
      
      *_PIEZO = NULL;
        
      if(!(fp = fopen (datafile, "r")))
      {
        printf("Failed to open file containing borehole sites!\n");
        return -1;
      }
      
      npiezo = 0;
    
      // Read (BSS,x,y) triplets
      while( fgets(buffer, 200, fp)!= NULL ) // parse until end-of-file
      {
        sscanf(buffer,"%s %lf %lf %d",BSSbuf, &xsite,&ysite,&layer);
        *_PIEZO = (Piezo *) realloc(*_PIEZO,(npiezo+1)*sizeof(Piezo));
        strncpy( (*_PIEZO)[npiezo].BSS_ID , BSSbuf, 12);
        (*_PIEZO)[npiezo].Site.x = xsite;
        (*_PIEZO)[npiezo].Site.y = ysite;
        (*_PIEZO)[npiezo].layer = layer;   
        npiezo++;
      } 
      
      fclose(fp);
      *_npiezo = npiezo;  
      printf("%d sites read from %s\n",npiezo,datafile);
      return(0);  
    }
    
    int read_param ( char * paramfile, double ** _PARAM, int * _nparam)
    {
      char buffer[200];
      char name[40];
      int npar;
      double v;
      FILE * fp;
      
      *_PARAM = NULL;
        
      if(!(fp = fopen (paramfile, "r")))
      {
        printf("Failed to open parameter file!\n");
        return -1;
      }
      
      npar = 0;
    
      // Read (name,value) pairs
      while( fgets(buffer, 200, fp)!= NULL ) // parse until end-of-file
      {
        sscanf(buffer,"%s %lf",name, &v);
        *_PARAM = (double *) realloc(*_PARAM,(npar+1)*sizeof(double));
        (*_PARAM)[npar] = v;   
        npar++;
      } 
      
      fclose(fp);
      *_nparam = npar;  
      printf("%d parameters read from %s\n",npar,paramfile);
      return(0);  
    }