#include "RcppArmadillo.h"
#include "root.h"
#include "biophysicsutils.h"
#include "biophysicsutils_c.h"
#include "tissuemoisture_c.h"
#include "hydraulics_c.h"
#include "incgamma_c.h"
#include <math.h>


using namespace Rcpp;
using namespace std;


NumericVector Psi2K(double psi, NumericVector psi_extract, double exp_extract = 3.0) {
  int n = psi_extract.size();
  NumericVector k(n);
  for(int i=0; i<n; i++) {
    k[i] = Psi2K_c(psi,psi_extract[i],exp_extract);
  }
  return k;
}

NumericVector K2Psi(NumericVector K, NumericVector psi_extract, double exp_extract = 3.0) {
  int n = psi_extract.size();
  NumericVector psi(n);
  for(int i=0; i<n; i++) {
    psi[i] = K2Psi_c(K[i], psi_extract[i], exp_extract);
  }
  return psi;
}



//' @rdname hydraulics_conductancefunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_averagePsi")]]
double averagePsi(NumericVector psi, NumericVector v, double exp_extract, double psi_extract) {
  int nlayers = psi.size();
  NumericVector K(nlayers);
  for(int l=0;l<nlayers;l++) K[l]= exp(-0.6931472*pow(std::abs(psi[l]/psi_extract),exp_extract));
  double psires =  psi_extract*pow(log(sum(K*v))/(-0.6931472),1.0/exp_extract);
  psires = std::max(psires, -40.0); //Limits plant water potential to -40 MPa
  return(psires);
}
double averagePsiPool(NumericMatrix Psi, NumericMatrix RHOPcohV, double exp_extract, double psi_extract) {
  int nlayers = Psi.ncol();
  int numCohorts = Psi.nrow();
  NumericMatrix K(numCohorts, nlayers);
  for(int j =0;j<numCohorts;j++) for(int l=0;l<nlayers;l++) K(j,l)= exp(-0.6931472*pow(std::abs(Psi(j,l)/psi_extract),exp_extract));
  double psires =  psi_extract*pow(log(sum(K*RHOPcohV))/(-0.6931472),1.0/exp_extract);
  psires = std::max(psires, -40.0); //Limits plant water potential to -40 MPa
  return(psires);
}



//' @rdname hydraulics_conductancefunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_psi2Weibull")]]
NumericVector psi2Weibull(double psi50, double psi88 = NA_REAL, double psi12 = NA_REAL) {
  if(NumericVector::is_na(psi88) && NumericVector::is_na(psi12)) stop("Either 'psi88' or 'psi12' has to be non-missing");
  double a, psiRatio;
  if(!NumericVector::is_na(psi88)) {
    psiRatio = psi50/psi88;
    a = 0.3269156; // log(0.5)/log(0.12);
  } else {
    psiRatio = psi50/psi12;
    a = 5.422271; // log(0.5)/log(0.88);
  }
  double c = log(a)/log(psiRatio);
  double d = psi50/pow(0.6931472,1.0/c);
  NumericVector par = NumericVector::create(c,d);
  par.attr("names") = CharacterVector::create("c","d");
  return(par);
}


double ludcmp(NumericMatrix a, int n, IntegerVector indx) {
    const double TINY=1.0e-20;
    int i,imax=0,j,k;
    double big,dum,sum,temp;
    NumericVector vv(n);
    double d = 1.0;
    for(i=0;i<n;i++) {
      big = 0.0;
      for(j=0;j<n;j++) if((temp=std::abs(a(i,j)))>big) big=temp;
      if(big==0.0) throw std::range_error("Singular matrix in routine ludcmp");
      vv[i] = 1.0/big; //Save the scaling
    }
    //Loop over columns of Crout's method
    for(j=0;j<n;j++){
      for(i=0;i<j;i++) {
        sum=a(i,j);
        for(k=0;k<i;k++) sum-=a(i,k)*a(k,j);
        a(i,j)=sum;
      }
      big=0.0;
      for(i=j;i<n;i++) {
        sum=a(i,j);
        for(k=0;k<j;k++) sum-=a(i,k)*a(k,j);
        a(i,j)=sum;
        if((dum=vv[i]*std::abs(sum))>=big) {
          big=dum;
          imax=i;
        }
      }
      if(j!=imax) {
        for(k=0;k<n;k++){
          dum=a(imax,k);
          a(imax,k) = a(j,k);
          a(j,k) = dum;
        }
        d=-d;
        vv[imax] = vv[j];
      }
      indx[j] = imax;
      if(a(j,j)==0.0) a(j,j) = TINY;
      if(j!=n) {
        dum=1.0/(a(j,j));
        for(i=j+1;i<n;i++) a(i,j)*=dum;
      }
    }
    return(d);
}
void lubksb(NumericMatrix a, int n, IntegerVector indx, NumericVector b) {
  int i,ii=-1,ip,j;
  double sum;
  for(i = 0;i<n;i++){
    ip=indx[i];
    sum=b[ip];
    b[ip]=b[i];
    if(ii>=0) for(j=ii;j<=i-1;j++) sum-=a(i,j)*b[j];
    else if(sum) ii=i;
    b[i] = sum;
  }
  for(i=(n-1);i>=0;i--) {
    sum=b[i];
    for(j=i+1;j<n;j++) sum-=a(i,j)*b[j];
    b[i] = sum/a(i,i);
  }
}

//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_E2psiBelowground")]]
List E2psiBelowground(double E, List hydraulicNetwork, 
                  NumericVector psiIni = NumericVector::create(0)) {
  
  List numericParams = hydraulicNetwork["numericParams"];
  int ntrial = numericParams["ntrial"];
  double psiTol = numericParams["psiTol"];
  double ETol = numericParams["ETol"];
  // Rcpp::Rcout<< "parameters: " << ntrial << " " << psiTol << " " << ETol << "\n";
  
  NumericVector psiSoil = hydraulicNetwork["psisoil"]; 
  NumericVector krhizomax = hydraulicNetwork["krhizomax"]; 
  NumericVector nsoil = hydraulicNetwork["nsoil"]; 
  NumericVector alphasoil = hydraulicNetwork["alphasoil"]; 
  NumericVector krootmax = hydraulicNetwork["krootmax"];
  double rootc = hydraulicNetwork["rootc"];
  double rootd  = hydraulicNetwork["rootd"]; 
  
  int nlayers = psiSoil.length();
  //Initialize
  NumericVector x(nlayers+1);
  if(psiIni.size()==(nlayers+1)){
    for(int l=0;l<(nlayers+1);l++) {
      x[l] = psiIni[l];
    }
  } else{
    double minPsi = -0.00001;
    for(int l=0;l<nlayers;l++) {
      x[l] =psiSoil[l];
      minPsi = std::min(minPsi, psiSoil[l]);
    }
    x[nlayers] = minPsi;

  }
  // for(int l=0;l<nlayers +1;l++) {
  //   Rcpp::Rcout << x[l]<<" ";
  // }
  // Rcpp::Rcout<<"\n";
  //Flow across root xylem and rhizosphere elements
  NumericVector Eroot(nlayers), Erhizo(nlayers);
  
  //Newton-Raphson algorithm
  NumericVector p(nlayers+1), fvec(nlayers+1);
  IntegerVector indx(nlayers+1);
  NumericMatrix fjac(nlayers+1,nlayers+1);
  double Esum = 0.0;
  for(int k=0;k<ntrial;k++) {
    // Rcout<<"trial "<<k<<"\n";
    //Calculate steady-state flow functions
    Esum = 0.0;
    bool stop = false;
    for(int l=0;l<nlayers;l++) {
      Eroot[l] = EXylem_c(x[nlayers], x[l], krootmax[l], rootc, rootd, true, 0.0);
      Erhizo[l] = EVanGenuchten_c(x[l], psiSoil[l], krhizomax[l], nsoil[l], alphasoil[l]);
      fvec[l] = Erhizo[l] - Eroot[l];
      // Rcout<<" Erhizo"<<l<<": "<< Erhizo[l]<<" psiSoil"<<l <<": "<< psiSoil[l]<< " krhizo max"<<l<<": "<< krhizomax[l]<< " nsoil "<< nsoil[l] << " alphasoil "<< alphasoil[l] << " kroot max"<<l<<": "<< krootmax[l]<<" Eroot"<<l<<": "<<Eroot[l]<<" fvec: "<<fvec[l]<<"\n";
      // Rcout<<"der psi_l "<<d_psi_l<<"der Eroot psi_l "<<d_Eroot_psi_l<<"  der psi_root "<<d_psi_root<<"\n";
      Esum +=Eroot[l];
    }
    fvec[nlayers] = Esum-E;
    // Rcout<<"fvec_nlayers: "<<fvec[nlayers]<<"\n";
    //Fill Jacobian
    for(int l1=0;l1<nlayers;l1++) { //funcio
      for(int l2=0;l2<nlayers;l2++) { //derivada
        if(l1==l2) {
          fjac(l1,l2) = -vanGenuchtenConductance_c(x[l2],krhizomax[l2], nsoil[l2], alphasoil[l2])-xylemConductance_c(x[l2], krootmax[l2], rootc, rootd);  
        }
        else fjac(l1,l2) = 0.0;
      }
    }
    fjac(nlayers,nlayers) = 0.0;
    for(int l=0;l<nlayers;l++) { 
      fjac(l,nlayers) = xylemConductance_c(x[nlayers], krootmax[l], rootc, rootd); //funcio l derivada psi_rootcrown
      fjac(nlayers,l) = xylemConductance_c(x[l], krootmax[l], rootc, rootd);//funcio nlayers derivada psi_l
      // funcio nlayers derivada psi_rootcrown
      fjac(nlayers,nlayers) +=-xylemConductance_c(x[nlayers], krootmax[l], rootc, rootd);
    }
    // for(int l1=0;l1<=nlayers;l1++) { //funcio
    //   for(int l2=0;l2<=nlayers;l2++) { //derivada
    //     Rcout<<fjac(l1,l2)<<" ";
    //   }
    //   Rcout<<"\n";
    // }
    //Check function convergence
    double errf = 0.0;
    for(int fi=0;fi<=nlayers;fi++) errf += std::abs(fvec[fi]);
    // Rcpp::Rcout<< "ERRF: "<< errf<<"\n";
    if(errf<=ETol) break;
    //Right-hand side of linear equations
    for(int fi=0;fi<=nlayers;fi++) p[fi] = -fvec[fi];
    //Solve linear equations using LU decomposition
    ludcmp(fjac,nlayers+1,indx);
    lubksb(fjac,nlayers+1,indx,p);
    //Check root convergence
    double errx = 0.0;
    for(int fi=0;fi<=nlayers;fi++) {
      errx +=std::abs(p[fi]);
      x[fi]+=p[fi];
      x[fi] = std::min(0.0, x[fi]);
      if(x[fi]<-40.0) {
        x[fi] = NA_REAL;
        stop = true;
      }
      // Rcout<<"("<<x[fi]<<") ";
    }
    // Rcout<<"\n";
    if(errx<=psiTol) break;
    else if(k==(ntrial-1)) { //Last trial and no convergence
      for(int fi=0;fi<=nlayers;fi++) x[fi] = NA_REAL;
      // Rcout<<"LC";
      stop = true;
    }
    if(stop) break;
  }
  
  //Initialize and copy output
  NumericVector psiRhizo(nlayers);
  for(int l=0;l<nlayers;l++) {
    psiRhizo[l] = x[l];
  }
  double psiRootCrown = x[nlayers];
  //Calculate final flows
  Esum = 0.0;
  for(int l=0;l<(nlayers-1);l++) {
    Erhizo[l] = EVanGenuchten_c(x[l], psiSoil[l], krhizomax[l], nsoil[l], alphasoil[l]);
    Esum += Erhizo[l];
  }
  Erhizo[nlayers-1] = E - Esum; //Define as difference to match input
  return(List::create(Named("E") = E, Named("ERhizo")=Erhizo, Named("psiRhizo") = psiRhizo, Named("psiRootCrown") = psiRootCrown, Named("x") = x));
} 


//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_E2psiAboveground")]]
List E2psiAboveground(double E, double psiRootCrown, 
                      List hydraulicNetwork) {
  double kstemmax = hydraulicNetwork["kstemmax"];
  double stemc = hydraulicNetwork["stemc"];
  double stemd = hydraulicNetwork["stemd"];
  double kleafmax = hydraulicNetwork["kleafmax"];
  double leafc = hydraulicNetwork["leafc"];
  double leafd = hydraulicNetwork["leafd"];
  double PLCstem = hydraulicNetwork["PLCstem"];
  double PLCleaf = hydraulicNetwork["PLCleaf"];
  bool stemCavitationEffects = hydraulicNetwork["stemCavitationEffects"];
  bool leafCavitationEffects = hydraulicNetwork["leafCavitationEffects"];
  // Rcpp::Rcout<< " Stem PLC " << PLCstem << " Leaf PLC " << PLCleaf<< "\n";
  // Rcpp::Rcout<< " kstemmax " << kstemmax << " kleafmax " << kleafmax<< "\n";
  // Rcpp::Rcout<< " stem c " << stemc << " stem d " << stemd<< "\n";
  // Rcpp::Rcout<< " leaf c " << leafc << " leaf d " << leafd<< "\n";
  
  double psiPLCStem =  0.0;
  if(stemCavitationEffects) psiPLCStem = apoplasticWaterPotential_c(std::max(0.0001, 1.0 - PLCstem), stemc, stemd);
  double psiStem = E2psiXylem_c(E, psiRootCrown, kstemmax, stemc, stemd, psiPLCStem); //Apliquem la fatiga per cavitacio a la caiguda de potencial a la tija 
  double psiPLCLeaf= 0.0;
  if(leafCavitationEffects) psiPLCLeaf = apoplasticWaterPotential_c(std::max(0.0001,1.0-PLCleaf), leafc, leafd);
  double psiLeaf = E2psiXylem_c(E, psiStem, kleafmax, leafc, leafd, psiPLCLeaf); 
  return(List::create( Named("E")=E, Named("psiStem") =psiStem,Named("psiLeaf") =psiLeaf));
}

List E2psiAbovegroundSymp(double E, double psiRootCrown, 
                          List hydraulicNetwork) {
  double kstemmax = hydraulicNetwork["kstemmax"];
  double stemc = hydraulicNetwork["stemc"];
  double stemd = hydraulicNetwork["stemd"];
  double kleafapomax = hydraulicNetwork["kleafapomax"];
  double kleafsymp = hydraulicNetwork["kleafsymp"];
  double leafc = hydraulicNetwork["leafc"];
  double leafd = hydraulicNetwork["leafd"];
  double PLCstem = hydraulicNetwork["PLCstem"];
  double PLCleaf = hydraulicNetwork["PLCleaf"];
  
  double psiPLCStem =  apoplasticWaterPotential_c(std::max(0.0001, 1.0 - PLCstem), stemc, stemd);
  double psiStem = E2psiXylem_c(E, psiRootCrown, kstemmax, stemc, stemd, psiPLCStem); //Apliquem la fatiga per cavitacio a la caiguda de potencial a la tija 
  double psiPLCLeaf=  apoplasticWaterPotential_c(std::max(0.0001,1.0-PLCleaf), leafc, leafd);
  double psiLeafApo = E2psiXylem_c(E, psiStem, kleafapomax, leafc, leafd, psiPLCLeaf); 
  double psiLeafSymp = psiLeafApo - E/kleafsymp;
  return(List::create( Named("E")=E, Named("psiStem") =psiStem,
                       Named("psiLeaf") =psiLeafSymp));
}



//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_E2psiNetwork")]]
List E2psiNetwork(double E, List hydraulicNetwork,
                  NumericVector psiIni = NumericVector::create(0)) {
  
  List E2psiRS = E2psiBelowground(E, hydraulicNetwork, 
                                  psiIni);
  
  //Copy output
  double psiRootCrown  = E2psiRS["psiRootCrown"];
  NumericVector psiRhizo = E2psiRS["psiRhizo"];  
  NumericVector ERhizo = E2psiRS["ERhizo"];  


  double psiStem =  NA_REAL;
  double psiLeaf = NA_REAL;
  if(!NumericVector::is_na(psiRootCrown)) {
    List E2psiAG = E2psiAboveground(E, psiRootCrown, hydraulicNetwork);
    psiLeaf = E2psiAG["psiLeaf"];
    psiStem = E2psiAG["psiStem"];
  } 
  return(List::create(Named("E") = E, Named("ERhizo")=ERhizo, Named("psiRhizo") = psiRhizo, 
                      Named("psiRootCrown") = psiRootCrown, Named("psiStem") = psiStem, Named("psiLeaf") = psiLeaf, 
                      Named("x") = E2psiRS["x"]));
} 


//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_supplyFunctionOneXylem")]]
List supplyFunctionOneXylem(NumericVector psiSoil, NumericVector v,
                            double kstemmax, double stemc, double stemd, double psiCav = 0.0,
                            int maxNsteps=200, double dE=0.01) {
  int nlayers = psiSoil.size();
  NumericVector supplyE(maxNsteps);
  NumericVector supplydEdp(maxNsteps);
  NumericMatrix supplyERhizo(maxNsteps,nlayers);
  NumericVector supplyPsi(maxNsteps);
  
  supplyE[0] = 0;
  for(int l=0;l<nlayers;l++) supplyERhizo[l] = 0.0;
  supplyPsi[0] = averagePsi(psiSoil, v, stemc, stemd);
  NumericVector Psilayers(nlayers);
  //Calculate initial slope
  for(int l=0;l<nlayers;l++) {
    Psilayers[l] = E2psiXylem_c(dE, psiSoil[l], 
                              kstemmax, stemc,stemd, psiCav);
    // Rcout<<Psilayers[l]<<" ";
  }
  // Rcout<<"\n";
  double psiI = averagePsi(Psilayers, v, stemc, stemd);
  double maxdEdp = dE/std::abs(psiI-supplyPsi[0]);
  // Rcout<<maxdEdp<<"\n";
  
  int nsteps = 1;
  dE = maxdEdp*0.1;
  for(int i=1;i<maxNsteps;i++) {
    supplyE[i] = supplyE[i-1]+dE;
    // Rcout<<supplyE[i]<<" ";
    for(int l=0;l<nlayers;l++) {
      Psilayers[l] = E2psiXylem_c(supplyE[i], psiSoil[l],
                                kstemmax, stemc,stemd, psiCav);
      // Rcout<<Psilayers[l]<<" ";
    }
    // Rcout<<"\n";
    supplyPsi[i] = averagePsi(Psilayers, v, stemc, stemd);
    for(int l=0;l<nlayers;l++) {
      supplyERhizo(i,l) = supplyE[i]*v[l];
    }
    
    if(!NumericVector::is_na(supplyPsi[i])) {
      if(i==1) {
        supplydEdp[0] = (supplyE[1]-supplyE[0])/std::abs(supplyPsi[1]-supplyPsi[0]);
      } else {
        double d1 = (supplyE[i-1]-supplyE[i-2])/std::abs(supplyPsi[i-1]-supplyPsi[i-2]);
        double d2 = (supplyE[i]-supplyE[i-1])/std::abs(supplyPsi[i]-supplyPsi[i-1]);
        supplydEdp[i-1] = (d1+d2)/2.0;
      }
      dE = supplydEdp[i-1]*0.1;
      nsteps++;
      if(supplydEdp[i-1]<0.01*maxdEdp) break;
    } else {
      break;
    }
  }
  //Calculate last dEdP
  if(nsteps>1) supplydEdp[nsteps-1] = (supplyE[nsteps-1]-supplyE[nsteps-2])/std::abs(supplyPsi[nsteps-1]-supplyPsi[nsteps-2]);
  //Copy values tp nsteps
  NumericVector supplyEDef(nsteps);
  NumericVector supplydEdpDef(nsteps);
  NumericMatrix supplyERhizoDef(nsteps,nlayers);
  NumericVector supplyPsiPlant(nsteps);
  for(int i=0;i<nsteps;i++) {
    supplyEDef[i] = supplyE[i];
    supplydEdpDef[i] = supplydEdp[i];
    supplyPsiPlant[i] = supplyPsi[i];
    for(int l=0;l<nlayers;l++) {
      supplyERhizoDef(i,l) = supplyERhizo(i,l);
    }
  }
  return(List::create(Named("E") = supplyEDef,
                      Named("ERhizo") = supplyERhizoDef,
                      Named("PsiPlant")=supplyPsiPlant,
                      Named("dEdP")=supplydEdpDef));
  
}

//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_supplyFunctionTwoElements")]]
List supplyFunctionTwoElements(double Emax, double psiSoil, double krhizomax, double kxylemmax, double n, double alpha, double c, double d, 
                               double psiCav = 0.0, 
                               double dE = 0.1, double psiMax = -10.0) {
  dE = std::min(dE,Emax/5.0);
  int maxNsteps = round(Emax/dE)+1;
  NumericVector supplyE(maxNsteps);
  NumericVector supplydEdp(maxNsteps);
  NumericVector supplyFittedE(maxNsteps);
  NumericVector supplyPsiRoot(maxNsteps);
  NumericVector supplyPsiPlant(maxNsteps);
  double Eg1 = 0.0;
  double Eg2 = 0.0;
  double psiStep1 = -0.1;
  double psiStep2 = -0.1;
  double psiRoot = psiSoil;
  double psiPlant = psiSoil;
  double vgPrev = vanGenuchtenConductance_c(psiRoot, krhizomax, n, alpha);
  double vg = 0.0;
  double wPrev = xylemConductance_c(std::min(psiCav,psiPlant), kxylemmax, c, d); //conductance can decrease if psiCav < psiPlant
  double w = 0.0;
  double incr = 0.0;
  supplyPsiRoot[0] = psiSoil;
  supplyPsiPlant[0] = psiSoil;
  supplyE[0] = 0.0;
  double psiPrec = -0.000001;
  for(int i=1;i<maxNsteps;i++) {
    supplyE[i] = supplyE[i-1]+dE;
    psiStep1 = -0.01;
    psiRoot = supplyPsiRoot[i-1];
    vgPrev = vanGenuchtenConductance_c(psiRoot, krhizomax, n, alpha);
    while((psiStep1<psiPrec) && (psiRoot>psiMax))  {
      vg = vanGenuchtenConductance_c(psiRoot+psiStep1, krhizomax, n, alpha);
      incr = ((vg+vgPrev)/2.0)*std::abs(psiStep1);
      if((Eg1+incr)>supplyE[i]) {
        psiStep1 = psiStep1*0.5;
      } else {
        psiRoot = psiRoot + psiStep1;
        Eg1 = Eg1+incr;
        vgPrev = vg;
      }
    }
    supplyPsiRoot[i] = psiRoot;
    if(supplyPsiRoot[i]<psiMax) supplyPsiRoot[i] = psiMax;
    
    psiStep2 = -0.01;
    Eg2 = 0.0;
    psiPlant = psiRoot;
    wPrev = xylemConductance_c(std::min(psiCav,psiPlant), kxylemmax, c, d);
    while((psiStep2<psiPrec) && (psiPlant>psiMax))  {
      w = xylemConductance_c(std::min(psiCav,psiPlant+psiStep2), kxylemmax, c, d);
      incr = ((w+wPrev)/2.0)*std::abs(psiStep2);
      if((Eg2+incr)>supplyE[i]) {
        psiStep2 = psiStep2*0.5;
      } else {
        psiPlant = psiPlant + psiStep2;
        Eg2 = Eg2+incr;
        wPrev = w;
      }
    }
    supplyPsiPlant[i] = psiPlant;
    if(supplyPsiPlant[i]<psiMax) supplyPsiPlant[i] = psiMax;
    supplyFittedE[i] = std::max(supplyFittedE[i-1], Eg2); //Ensure non-decreasing function
    // supplyFittedE[i] = Eg2;
    if(i==1) {
      supplydEdp[0] = (supplyFittedE[1]-supplyFittedE[0])/(supplyPsiPlant[0]-supplyPsiPlant[1]); 
      if((supplyPsiPlant[0]-supplyPsiPlant[1])==0.0) supplydEdp[0] = 0.0;
    }
    else if(i>1) {
      supplydEdp[i-1] = 0.5*(supplyFittedE[i-1]-supplyFittedE[i-2])/(supplyPsiPlant[i-2]-supplyPsiPlant[i-1])+0.5*(supplyFittedE[i]-supplyFittedE[i-1])/(supplyPsiPlant[i-1]-supplyPsiPlant[i]); 
      if((supplyPsiPlant[i-2]-supplyPsiPlant[i-1])==0.0) supplydEdp[i-1] = 0.0;
      else if((supplyPsiPlant[i-1]-supplyPsiPlant[i])==0.0) supplydEdp[i-1] = 0.0;
    }
    if(i==(maxNsteps-1)) {
      supplydEdp[i] = (supplyFittedE[i]-supplyFittedE[i-1])/(supplyPsiPlant[i-1]-supplyPsiPlant[i]); 
      if((supplyPsiPlant[i-1]-supplyPsiPlant[i])==0.0) supplydEdp[i] = 0.0;
    }
  }
  return(List::create(Named("E") = supplyE,
                      Named("FittedE") = supplyFittedE,
                      Named("psiRootCrown")=supplyPsiRoot, 
                      Named("psiPlant")=supplyPsiPlant,
                      Named("dEdP")=supplydEdp));
}

//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_supplyFunctionThreeElements")]]
List supplyFunctionThreeElements(double Emax, double psiSoil, double krhizomax, double kxylemmax, double kleafmax, 
                                 double n, double alpha, 
                                 double stemc, double stemd, 
                                 double leafc, double leafd,
                                 double psiCav = 0.0, 
                                 double dE = 0.1, double psiMax = -10.0) {
  dE = std::min(dE,Emax/5.0);
  int maxNsteps = round(Emax/dE)+1;
  NumericVector supplyE(maxNsteps);
  NumericVector supplydEdp(maxNsteps);
  NumericVector supplyFittedE(maxNsteps);
  NumericVector supplyPsiRoot(maxNsteps);
  NumericVector supplyPsiStem(maxNsteps);
  NumericVector supplyPsiLeaf(maxNsteps);
  double Eg1 = 0.0;
  double Eg2 = 0.0;
  double Eg3 = 0.0;
  double psiStep1 = -0.1;
  double psiStep2 = -0.1;
  double psiStep3 = -0.1;
  double psiRoot = psiSoil;
  double psiStem = psiSoil;
  double psiLeaf = psiSoil;
  double vgPrev = vanGenuchtenConductance_c(psiRoot, krhizomax, n, alpha);
  double vg = 0.0;
  //conductance can decrease if psiCav < psiStem/psiLeaf
  double wPrevStem = xylemConductance_c(std::min(psiCav,psiStem), kxylemmax, stemc, stemd); 
  double wPrevLeaf = xylemConductance_c(std::min(psiCav,psiLeaf), kleafmax, leafc, leafd); 
  double w = 0.0;
  double incr = 0.0;
  supplyPsiRoot[0] = psiSoil;
  supplyPsiStem[0] = psiSoil;
  supplyPsiLeaf[0] = psiSoil;
  supplyE[0] = 0.0;
  double psiPrec = -0.000001;
  for(int i=1;i<maxNsteps;i++) {
    supplyE[i] = supplyE[i-1]+dE;
    psiStep1 = -0.01;
    
    // Root
    psiRoot = supplyPsiRoot[i-1];
    vgPrev = vanGenuchtenConductance_c(psiRoot, krhizomax, n, alpha);
    while((psiStep1<psiPrec) && (psiRoot>psiMax))  {
      vg = vanGenuchtenConductance_c(psiRoot+psiStep1, krhizomax, n, alpha);
      incr = ((vg+vgPrev)/2.0)*std::abs(psiStep1);
      if((Eg1+incr)>supplyE[i]) {
        psiStep1 = psiStep1*0.5;
      } else {
        psiRoot = psiRoot + psiStep1;
        Eg1 = Eg1+incr;
        vgPrev = vg;
      }
    }
    supplyPsiRoot[i] = psiRoot;
    if(supplyPsiRoot[i]<psiMax) supplyPsiRoot[i] = psiMax;
    
    //Stem
    psiStep2 = -0.01;
    Eg2 = 0.0;
    psiStem = psiRoot;
    wPrevStem = xylemConductance_c(std::min(psiCav,psiStem), kxylemmax, stemc, stemd);
    while((psiStep2<psiPrec) && (psiStem>psiMax))  {
      w = xylemConductance_c(std::min(psiCav,psiStem+psiStep2), kxylemmax, stemc, stemd);
      incr = ((w+wPrevStem)/2.0)*std::abs(psiStep2);
      if((Eg2+incr)>supplyE[i]) {
        psiStep2 = psiStep2*0.5;
      } else {
        psiStem = psiStem + psiStep2;
        Eg2 = Eg2+incr;
        wPrevStem = w;
      }
    }
    supplyPsiStem[i] = psiStem;
    if(supplyPsiStem[i]<psiMax) supplyPsiStem[i] = psiMax;
    
    //Leaf
    psiStep3 = -0.01;
    Eg3 = 0.0;
    psiLeaf = psiStem;
    wPrevLeaf = xylemConductance_c(psiLeaf, kleafmax,  leafc, leafd);
    while((psiStep3<psiPrec) && (psiLeaf>psiMax))  {
      w = xylemConductance_c(psiLeaf+psiStep3, kleafmax,  leafc, leafd);
      incr = ((w+wPrevLeaf)/2.0)*std::abs(psiStep3);
      if((Eg3+incr)>supplyE[i]) {
        psiStep3 = psiStep3*0.5;
      } else {
        psiLeaf = psiLeaf + psiStep3;
        Eg3 = Eg3+incr;
        wPrevLeaf = w;
      }
    }
    supplyPsiLeaf[i] = psiLeaf;
    if(supplyPsiLeaf[i]<psiMax) supplyPsiLeaf[i] = psiMax;
    
    
    //Ensure non-decreasing function
    supplyFittedE[i] = std::max(supplyFittedE[i-1], Eg3); 
    
    
    // supplyFittedE[i] = Eg2;
    if(i==1) {
      supplydEdp[0] = (supplyFittedE[1]-supplyFittedE[0])/(supplyPsiLeaf[0]-supplyPsiLeaf[1]); 
      if((supplyPsiLeaf[0]-supplyPsiLeaf[1])==0.0) supplydEdp[0] = 0.0;
    }
    else if(i>1) {
      supplydEdp[i-1] = 0.5*(supplyFittedE[i-1]-supplyFittedE[i-2])/(supplyPsiLeaf[i-2]-supplyPsiLeaf[i-1])+0.5*(supplyFittedE[i]-supplyFittedE[i-1])/(supplyPsiLeaf[i-1]-supplyPsiLeaf[i]); 
      if((supplyPsiLeaf[i-2]-supplyPsiLeaf[i-1])==0.0) supplydEdp[i-1] = 0.0;
      else if((supplyPsiLeaf[i-1]-supplyPsiLeaf[i])==0.0) supplydEdp[i-1] = 0.0;
    }
    if(i==(maxNsteps-1)) {
      supplydEdp[i] = (supplyFittedE[i]-supplyFittedE[i-1])/(supplyPsiLeaf[i-1]-supplyPsiLeaf[i]); 
      if((supplyPsiLeaf[i-1]-supplyPsiLeaf[i])==0.0) supplydEdp[i] = 0.0;
    }
  }
  return(List::create(Named("E") = supplyE,
                      Named("FittedE") = supplyFittedE,
                      Named("psiRootCrown")=supplyPsiRoot, 
                      Named("psiStem")=supplyPsiStem,
                      Named("psiLeaf")=supplyPsiLeaf,
                      Named("dEdP")=supplydEdp));
}

//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_supplyFunctionBelowground")]]
List supplyFunctionBelowground(List hydraulicNetwork, 
                           double minFlow = 0.0, double pCrit = 0.001) {
  
  List numericParams = hydraulicNetwork["numericParams"];
  int maxNsteps  = numericParams["maxNsteps"];
  double ETol = numericParams["ETol"];
  
  NumericVector psiSoil = hydraulicNetwork["psisoil"]; 
  int nlayers = psiSoil.size();
  NumericVector supplyE(maxNsteps);
  NumericVector supplydEdp(maxNsteps);
  NumericMatrix supplyERhizo(maxNsteps,nlayers);
  NumericMatrix supplyPsiRhizo(maxNsteps,nlayers);
  NumericVector supplyPsiRoot(maxNsteps);
  List sol = E2psiBelowground(minFlow, hydraulicNetwork,
                              NumericVector::create(0));
  NumericVector solERhizo =sol["ERhizo"];
  NumericVector solPsiRhizo = sol["psiRhizo"];
  supplyERhizo(0,_) = solERhizo;
  supplyPsiRhizo(0,_) = solPsiRhizo;
  supplyE[0] = sol["E"];
  supplyPsiRoot[0] = sol["psiRootCrown"];

  //Calculate initial slope
  List solI = E2psiBelowground(minFlow+ETol*2.0, hydraulicNetwork,
                               sol["x"]);
  double psiRootI = solI["psiRootCrown"];
  double maxdEdp = (ETol*2.0)/std::abs(psiRootI - supplyPsiRoot[0]);

  int nsteps = 1;
  double dE = std::min(0.05,maxdEdp*0.05);
  for(int i=1;i<maxNsteps;i++) {
    // if(i==3) stop("kk");
    supplyE[i] = supplyE[i-1]+dE;
    sol = E2psiBelowground(supplyE[i], hydraulicNetwork,
                           sol["x"]);
    solERhizo =sol["ERhizo"];
    solPsiRhizo = sol["psiRhizo"];
    supplyERhizo(i,_) =  solERhizo;
    supplyPsiRhizo(i,_) = solPsiRhizo;
    supplyPsiRoot[i] = sol["psiRootCrown"];

    if(!NumericVector::is_na(supplyPsiRoot[i])) {
      if(i==1) {
        supplydEdp[0] = (supplyE[1]-supplyE[0])/std::abs(supplyPsiRoot[1] - supplyPsiRoot[0]);
      } else {
        double d1 = (supplyE[i-1]-supplyE[i-2])/std::abs(supplyPsiRoot[i-1] - supplyPsiRoot[i-2]);
        double d2 = (supplyE[i]-supplyE[i-1])/std::abs(supplyPsiRoot[i] - supplyPsiRoot[i-1]);
        supplydEdp[i-1] = (d1+d2)/2.0;
      }
      if(supplyE[i]>0.1) dE = std::min(0.1,supplydEdp[i-1]*0.05);
      nsteps++;
      if((supplydEdp[i-1]<(pCrit*maxdEdp)) && (i>5)) break;
    } else {
      break;
    }
  }
  //Calculate last dEdP
  if(nsteps>1) supplydEdp[nsteps-1] = (supplyE[nsteps-1]-supplyE[nsteps-2])/std::abs(supplyPsiRoot[nsteps-1]-supplyPsiRoot[nsteps-2]);
  //Copy values tp nsteps
  NumericVector supplyEDef(nsteps);
  NumericVector supplydEdpDef(nsteps);
  NumericMatrix supplyERhizoDef(nsteps,nlayers);
  NumericMatrix supplyPsiRhizoDef(nsteps,nlayers);
  NumericVector supplyPsiRootDef(nsteps);
  for(int i=0;i<nsteps;i++) {
    supplyEDef[i] = supplyE[i];
    supplydEdpDef[i] = supplydEdp[i];
    supplyPsiRootDef[i] = supplyPsiRoot[i];
    supplyERhizoDef(i,_) = supplyERhizo(i,_);
    supplyPsiRhizoDef(i,_) = supplyPsiRhizo(i,_);
  }
  return(List::create(Named("E") = supplyEDef,
                      Named("ERhizo") = supplyERhizoDef,
                      Named("psiRhizo")=supplyPsiRhizoDef,
                      Named("psiRootCrown")=supplyPsiRootDef,
                      Named("dEdP")=supplydEdpDef));
  
}

//' @rdname hydraulics_supplyfunctions
// [[Rcpp::export("hydraulics_supplyFunctionAboveground")]]
List supplyFunctionAboveground(NumericVector Erootcrown, NumericVector psiRootCrown, 
                               List hydraulicNetwork) {

  int maxNsteps = Erootcrown.size();
  NumericVector supplyE(maxNsteps);
  NumericVector supplydEdp(maxNsteps);
  NumericVector supplyPsiLeaf(maxNsteps);
  NumericVector supplyPsiStem(maxNsteps);

  
  
  int Nsteps = 0;
  for(int i=0;i<maxNsteps;i++) {
    List sol = E2psiAboveground(Erootcrown[i], psiRootCrown[i],                        
                                hydraulicNetwork);
    supplyPsiStem[i] = sol["psiStem"];
    supplyPsiLeaf[i] = sol["psiLeaf"];
    if(NumericVector::is_na(supplyPsiLeaf[i])) {
      break; 
    } else {
      Nsteps = Nsteps + 1;
    }
    supplyE[i] = sol["E"];
    
    if(i==1) {
      supplydEdp[0] = (supplyE[1]-supplyE[0])/std::abs(supplyPsiLeaf[1]-supplyPsiLeaf[0]);
    } else {
      double d1 = (supplyE[i-1]-supplyE[i-2])/std::abs(supplyPsiLeaf[i-1]-supplyPsiLeaf[i-2]);
      double d2 = (supplyE[i]-supplyE[i-1])/std::abs(supplyPsiLeaf[i]-supplyPsiLeaf[i-1]);
      supplydEdp[i-1] = (d1+d2)/2.0;
    }
    
  }
  //Calculate last dEdP
  if(Nsteps>1) supplydEdp[Nsteps-1] = (supplyE[Nsteps-1]-supplyE[Nsteps-2])/std::abs(supplyPsiLeaf[Nsteps-1]-supplyPsiLeaf[Nsteps-2]);
  
  // Rcout<<Nsteps;
  //Copy values tp nsteps
  NumericVector supplyKtermDef(Nsteps);
  NumericVector supplyEDef(Nsteps);
  NumericVector supplydEdpDef(Nsteps);
  NumericVector supplyPsiLeafDef(Nsteps);
  NumericMatrix supplyPsiStemDef(Nsteps);

  for(int i=0;i<Nsteps;i++) {
    supplyEDef[i] = supplyE[i];
    supplydEdpDef[i] = supplydEdp[i];
    supplyPsiLeafDef[i] = supplyPsiLeaf[i];
    supplyPsiStemDef[i] = supplyPsiStem[i]; 
  }
  
  return(List::create(Named("E") = supplyEDef,
                      Named("psiStem")=supplyPsiStemDef,
                      Named("psiLeaf")=supplyPsiLeafDef,
                      Named("dEdP")=supplydEdpDef));
  
}


//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_supplyFunctionNetwork")]]
List supplyFunctionNetwork(List hydraulicNetwork, 
                           double minFlow = 0.0, double pCrit = 0.001) {

  List numericParams = hydraulicNetwork["numericParams"];
  int maxNsteps  = numericParams["maxNsteps"];
  double ETol = numericParams["ETol"];
  
  NumericVector psiSoil = hydraulicNetwork["psisoil"]; 
  
  int nlayers = psiSoil.size();
  NumericVector supplyE(maxNsteps);
  NumericVector supplydEdp(maxNsteps);
  NumericMatrix supplyERhizo(maxNsteps,nlayers);
  NumericMatrix supplyPsiRhizo(maxNsteps,nlayers);
  NumericVector supplyPsiRoot(maxNsteps);
  NumericVector supplyPsiStem(maxNsteps);
  NumericVector supplyPsiLeaf(maxNsteps);

  List sol = E2psiNetwork(minFlow, hydraulicNetwork,
                          NumericVector::create(0));
  NumericVector solERhizo = sol["ERhizo"];
  NumericVector solPsiRhizo = sol["psiRhizo"];
  supplyERhizo(0,_) = solERhizo;
  supplyPsiRhizo(0,_) = solPsiRhizo;
  supplyPsiStem[0] = sol["psiStem"];
  supplyPsiLeaf[0] = sol["psiLeaf"];
  supplyPsiRoot[0] = sol["psiRootCrown"];
  supplyE[0] = minFlow;
  
  //Calculate initial slope
  List solI = E2psiNetwork(minFlow+ETol*2.0, hydraulicNetwork,
                           sol["x"]);
  double psiLeafI = solI["psiLeaf"];
  double maxdEdp = (ETol*2.0)/std::abs(psiLeafI - supplyPsiLeaf[0]);
  // Rcout<< " maxdEdp " << maxdEdp <<"\n";

  int nsteps = 1;
  double dE = std::min(0.0005,maxdEdp*0.05);
  for(int i=1;i<maxNsteps;i++) {
    // Rcout<< " step: "<< i;
    supplyE[i] = supplyE[i-1]+dE;
    sol = E2psiNetwork(supplyE[i], hydraulicNetwork,
                       sol["x"]);
    solERhizo = sol["ERhizo"];
    solPsiRhizo = sol["psiRhizo"];
    supplyERhizo(i,_) = solERhizo;
    supplyPsiRhizo(i,_) = solPsiRhizo;
    supplyPsiStem[i] = sol["psiStem"];
    supplyPsiLeaf[i] = sol["psiLeaf"];
    supplyPsiRoot[i] = sol["psiRootCrown"];

    if(!NumericVector::is_na(supplyPsiLeaf[i])) {
      // Rcout<< " psiRC: "<<supplyPsiRoot[i] <<" psileaf: "<< supplyPsiLeaf[i] <<"\n";
      if(i==1) {
        supplydEdp[0] = (supplyE[1]-supplyE[0])/std::abs(supplyPsiLeaf[1] - supplyPsiLeaf[0]);
      } else {
        double d1 = (supplyE[i-1]-supplyE[i-2])/std::abs(supplyPsiLeaf[i-1] - supplyPsiLeaf[i-2]);
        double d2 = (supplyE[i]-supplyE[i-1])/std::abs(supplyPsiLeaf[i] - supplyPsiLeaf[i-1]);
        supplydEdp[i-1] = (d1+d2)/2.0;
      }
      if(supplyE[i]>0.1) dE = std::min(0.05,supplydEdp[i-1]*0.05);
      else if(supplyE[i]>0.05) dE = std::min(0.01,supplydEdp[i-1]*0.05);
      else if(supplyE[i]>0.01) dE = std::min(0.005,supplydEdp[i-1]*0.05);
      nsteps++;
      if(supplydEdp[i-1]<(pCrit*maxdEdp)) break;
    } else {
      break;
    }
  }
  //Calculate last dEdP
  if(nsteps>1) supplydEdp[nsteps-1] = (supplyE[nsteps-1]-supplyE[nsteps-2])/std::abs(supplyPsiLeaf[nsteps-1] - supplyPsiLeaf[nsteps-2]);
  //Copy values tp nsteps
  NumericVector supplyEDef(nsteps);
  NumericVector supplydEdpDef(nsteps);
  NumericMatrix supplyERhizoDef(nsteps,nlayers);
  NumericMatrix supplyPsiRhizoDef(nsteps,nlayers);
  NumericMatrix supplyPsiStemDef(nsteps);
  NumericVector supplyPsiLeafDef(nsteps);
  NumericVector supplyPsiRootDef(nsteps);
  for(int i=0;i<nsteps;i++) {
    if(NumericVector::is_na(supplyE[i])) stop("NA E in supplyFunctionNetwork");
    supplyEDef[i] = supplyE[i];
    supplydEdpDef[i] = supplydEdp[i];
    supplyPsiRootDef[i] = supplyPsiRoot[i];
    supplyERhizoDef(i,_) = supplyERhizo(i,_);
    supplyPsiRhizoDef(i,_) = supplyPsiRhizo(i,_);
    supplyPsiStemDef[i] = supplyPsiStem[i];
    supplyPsiLeafDef[i] = supplyPsiLeaf[i];
  }

  return(List::create(Named("E") = supplyEDef,
                      Named("ERhizo") = supplyERhizoDef,
                      Named("psiRhizo")=supplyPsiRhizoDef,
                      Named("psiRootCrown")=supplyPsiRootDef,
                      Named("psiStem")=supplyPsiStemDef,
                      Named("psiLeaf")=supplyPsiLeafDef,
                      Named("dEdP")=supplydEdpDef));
  
}

//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_regulatedPsiXylem")]]
NumericVector regulatedPsiXylem(double E, double psiUpstream, double kxylemmax, double c, double d, double psiStep = -0.01) {
  //If Ein > Ecrit then set Ein to Ecrit
  double psiUnregulated = E2psiXylem_c(E, psiUpstream, kxylemmax, c, d, 0.0);
  double Ec = ECrit_c(psiUpstream, kxylemmax,c,d);
  double Ein = E;
  if(Ein > Ec) {
    Ein = Ec;
    psiUnregulated = psiCrit_c(c,d, 0.001);
  }
  double deltaPsiUnregulated = psiUnregulated - psiUpstream;
  double kp = xylemConductance_c(psiUpstream, kxylemmax, c, d);
  double deltaPsiRegulated = deltaPsiUnregulated*(xylemConductance_c(psiUnregulated, kxylemmax, c, d)/kp);
  //replace by maximum if found for lower psi values
  // Rcout <<"Initial "<<psiUnregulated << " "<< deltaPsiRegulated <<"\n";
  for(double psi = psiUpstream; psi > psiUnregulated; psi +=psiStep) {
    double deltaPsi = (psi-psiUpstream)*(xylemConductance_c(psi, kxylemmax, c,d)/kp);
    // Rcout <<psi << " "<< deltaPsi<< " "<< deltaPsiRegulated <<"\n";
    if(NumericVector::is_na(deltaPsiRegulated)) deltaPsiRegulated = deltaPsi;
    else if(deltaPsi < deltaPsiRegulated) deltaPsiRegulated = deltaPsi;
  }
  double psiRegulated = psiUpstream + deltaPsiRegulated;
  double Efin = EXylem_c(psiRegulated, psiUpstream, kxylemmax, c, d);
  double relativeConductance1 = Efin/Ein;
  double relativeConductance2 = Efin/E;
  return(NumericVector::create(psiUnregulated, psiRegulated, Ein, Efin, relativeConductance1, relativeConductance2));
}

//' @rdname hydraulics_supplyfunctions
//' @keywords internal
// [[Rcpp::export("hydraulics_regulatedPsiTwoElements")]]
NumericVector regulatedPsiTwoElements(double Emax, double psiSoil, double krhizomax, double kxylemmax, double n, double alpha, double c, double d, double dE = 0.1, double psiMax = -10.0) {
  List s = supplyFunctionTwoElements(Emax, psiSoil, krhizomax, kxylemmax, n, alpha, c, d, 0.0, dE,psiMax);
  NumericVector supplyPsi = s["PsiPlant"];
  NumericVector Efitted = s["FittedE"];
  NumericVector dEdP = s["dEdP"];
  int maxNsteps = Efitted.size();
  double deltaPsiRegulated = 0.0;
  double deltaPsiRegulatedi=0.0;
  double dEdP0 = dEdP[0];
  for(int i=1;i<maxNsteps;i++) {
    if(supplyPsi[i]>psiMax) {
      deltaPsiRegulatedi = (supplyPsi[i] - psiSoil)*std::min(1.0, dEdP[i]/dEdP0);
      // Rcout<<supplydEdp <<" "<<deltaPsiRegulatedi<<"\n";
      if(deltaPsiRegulatedi < deltaPsiRegulated) {
        deltaPsiRegulated = deltaPsiRegulatedi;
      }
    }
  }
  //Regulated potential
  double psiRegulated = psiSoil + deltaPsiRegulated;
  //Find transpiration corresponding to regulated potential
  double ERegulated = 0.0, dEdPRegulated = 0.0;
  for(int i=1;i<maxNsteps;i++) {
    if((supplyPsi[i-1] >= psiRegulated) && (supplyPsi[i]<psiRegulated)) {
      ERegulated = Efitted[i]*std::abs((supplyPsi[i-1]-psiRegulated)/(supplyPsi[i-1]-supplyPsi[i])) + Efitted[i-1]*std::abs((supplyPsi[i]-psiRegulated)/(supplyPsi[i-1]-supplyPsi[i]));
      ERegulated = std::min(ERegulated, Emax);
      psiRegulated = supplyPsi[i]*std::abs((supplyPsi[i-1]-psiRegulated)/(supplyPsi[i-1]-supplyPsi[i])) + supplyPsi[i-1]*std::abs((supplyPsi[i]-psiRegulated)/(supplyPsi[i-1]-supplyPsi[i]));
      dEdPRegulated = dEdP[i]*std::abs((supplyPsi[i-1]-psiRegulated)/(supplyPsi[i-1]-supplyPsi[i])) + dEdP[i-1]*std::abs((supplyPsi[i]-psiRegulated)/(supplyPsi[i-1]-supplyPsi[i]));
      if((supplyPsi[i-1]-supplyPsi[i])==0.0) dEdPRegulated = 0.0;
      // Rcout<<dEdP[i]<< " "<<dEdP[i-1]<< " "<<dEdPRegulated<<"\n";
      break;
    }
  }
  return(NumericVector::create(supplyPsi[maxNsteps-1], psiRegulated, Efitted[maxNsteps-1], ERegulated, dEdPRegulated));
}



//' Scaling from conductivity to conductance
//' 
//' Functions used to scale from tissue conductivity to conductance of different elements of the continuum.
//' 
//' @param psiSoil Soil water potential (in MPa). A scalar or a vector depending on the function.
//' @param psiRhizo Water potential (in MPa) in the rhizosphere (root surface).
//' @param psiStem Water potential (in MPa) in the stem.
//' @param psiLeaf Water potential (in MPa) in the leaf.
//' @param PLCstem Percent loss of conductance (in %) in the stem.
//' @param L Vector with the length of coarse roots (mm) for each soil layer.
//' @param V Vector with the proportion \[0-1\] of fine roots within each soil layer.
//' @param krhizomax Maximum rhizosphere hydraulic conductance (defined as flow per leaf surface unit and per pressure drop).
//' @param kleafmax Maximum leaf hydraulic conductance (defined as flow per leaf surface unit and per pressure drop).
//' @param kstemmax Maximum stem xylem hydraulic conductance (defined as flow per leaf surface unit and per pressure drop).
//' @param krootmax Maximum root xylem hydraulic conductance (defined as flow per leaf surface unit and per pressure drop).
//' @param psiStep Water potential precision (in MPa).
//' @param rootc,rootd Parameters of the Weibull function for roots (root xylem vulnerability curve).
//' @param stemc,stemd Parameters of the Weibull function for stems (stem xylem vulnerability curve).
//' @param leafc,leafd Parameters of the Weibull function for leaves (leaf vulnerability curve).
//' @param root_P50,root_slope Parameters of the Sigmoid function for roots (root xylem vulnerability curve).
//' @param stem_P50,stem_slope Parameters of the Sigmoid function for stems (stem xylem vulnerability curve).
//' @param leaf_P50,leaf_slope Parameters of the Sigmoid function for leaves (leaf vulnerability curve).
//' @param n,alpha Parameters of the Van Genuchten function (rhizosphere vulnerability curve).
//' @param averageResistancePercent Average (across water potential values) resistance percent of the rhizosphere, with respect to total resistance (rhizosphere + root xylem + stem xylem).
//' @param initialValue Initial value of rhizosphere conductance.
//' @param xylemConductivity Xylem conductivity as flow per length of conduit and pressure drop (in kg·m-1·s-1·MPa-1).
//' @param Al2As Leaf area to sapwood area (in m2·m-2).
//' @param height Plant height (in cm).
//' @param refheight Reference plant height of measurement of xylem conductivity (in cm).
//' @param taper A boolean flag to indicate correction by taper of xylem conduits (Christoffersen et al. 2017).
//' 
//' @details Details of the hydraulic model are given in the medfate book
//' 
//' @return
//' Values returned for each function are:
//' \itemize{
//'   \item{\code{hydraulics_maximumSoilPlantConductance}: The maximum soil-plant conductance, in the same units as the input segment conductances.}
//'   \item{\code{hydraulics_averageRhizosphereResistancePercent}: The average percentage of resistance due to the rhizosphere, calculated across water potential values.}
//'   \item{\code{hydraulics_findRhizosphereMaximumConductance}: The maximum rhizosphere conductance value given an average rhizosphere resistance and the vulnerability curves of rhizosphere, root and stem elements.}
//'   \item{\code{hydraulics_taperFactorSavage}: Taper factor according to Savage et al. (2010).}
//' }
//' 
//' @references
//' Christoffersen, B. O., M. Gloor, S. Fauset, N. M. Fyllas, D. R. Galbraith, T. R. Baker, L. Rowland, R. A. Fisher, O. J. Binks, S. A. Sevanto, C. Xu, S. Jansen, B. Choat, M. Mencuccini, N. G. McDowell, and P. Meir. 2016. Linking hydraulic traits to tropical forest function in a size-structured and trait-driven model (TFS v.1-Hydro). Geoscientific Model Development Discussions 9: 4227–4255.
//' 
//' Savage, V. M., L. P. Bentley, B. J. Enquist, J. S. Sperry, D. D. Smith, P. B. Reich, and E. I. von Allmen. 2010. Hydraulic trade-offs and space filling enable better predictions of vascular structure and function in plants. Proceedings of the National Academy of Sciences of the United States of America 107:22722–7.
//' 
//' Olson, M.E., Anfodillo, T., Rosell, J.A., Petit, G., Crivellaro, A., Isnard, S., \enc{León-Gómez}{Leon-Gomez}, C., \enc{Alvarado-Cárdenas}{Alvarado-Cardenas}, L.O., and Castorena, M. 2014. Universal hydraulics of the flowering plants: Vessel diameter scales with stem length across angiosperm lineages, habits and climates. Ecology Letters 17: 988–997.
//' 
//' @author Miquel De \enc{Cáceres}{Caceres} Ainsa, CREAF
//' 
//' @seealso
//' \code{\link{hydraulics_psi2K}}, \code{\link{hydraulics_supplyFunctionPlot}}, \code{\link{spwb}}, \code{\link{soil}}
//' 
//' @name hydraulics_scalingconductance
//' @keywords internal
// [[Rcpp::export("hydraulics_maximumSoilPlantConductance")]]
double maximumSoilPlantConductance(NumericVector krhizomax, NumericVector krootmax, 
                                   double kstemmax, double kleafmax) {
  int nlayers = krhizomax.length();
  double krhizo = 0.0;
  double kroot = 0.0;
  for(int i=0;i<nlayers;i++) {
    krhizo = krhizo + krhizomax[i];
    kroot = kroot + krootmax[i];
  }
  double rrhizo = 1.0/krhizo;
  double rroot = 1.0/kroot;
  double rstem = 1.0/kstemmax;
  double rleaf = 1.0/kleafmax;
  return(1.0/(rrhizo+rroot+rstem+rleaf));
}

//' @rdname hydraulics_scalingconductance
//' @keywords internal
// [[Rcpp::export("hydraulics_soilPlantResistancesSigmoid")]]
List soilPlantResistancesSigmoid(NumericVector psiSoil, NumericVector psiRhizo, 
                                  double psiStem, double PLCstem,
                                  double psiLeaf, double PLCleaf,
                                  NumericVector krhizomax, NumericVector n, NumericVector alpha,
                                  NumericVector krootmax, double root_P50, double root_slope, 
                                  double kstemmax, double stem_P50, double stem_slope,
                                  double kleafmax, double leaf_P50, double leaf_slope) {
   int nlayers = psiSoil.length();
   NumericVector rrhizo(nlayers, 0.0);
   NumericVector rroot(nlayers, 0.0);
   for(int i=0;i<nlayers;i++) {
     rrhizo[i] = 1.0/vanGenuchtenConductance_c(psiSoil[i], krhizomax[i], n[i], alpha[i]);
     rroot[i] = 1.0/xylemConductanceSigmoid_c(psiRhizo[i], krootmax[i], root_P50, root_slope);
   }
   double rstem = 1.0/(kstemmax*std::min(1.0 - PLCstem, xylemConductanceSigmoid_c(psiStem, 1.0, stem_P50, stem_slope)));
   double rleaf = 1.0/(kleafmax*std::min(1.0 - PLCstem, xylemConductanceSigmoid_c(psiLeaf, kleafmax, leaf_P50, leaf_slope)));
   List resistances = List::create(_["rhizosphere"] = rrhizo, 
                                   _["root"] = rroot, 
                                   _["stem"] = rstem, 
                                   _["leaf"] = rleaf);
   return(resistances);
 }

//' @rdname hydraulics_scalingconductance
//' @keywords internal
// [[Rcpp::export("hydraulics_soilPlantResistancesWeibull")]]
List soilPlantResistancesWeibull(NumericVector psiSoil, NumericVector psiRhizo, 
                                 double psiStem, double PLCstem,
                                 double psiLeaf, double PLCleaf,
                                 NumericVector krhizomax, NumericVector n, NumericVector alpha,
                                 NumericVector krootmax, double rootc, double rootd, 
                                 double kstemmax, double stemc, double stemd,
                                 double kleafmax, double leafc, double leafd) {
  int nlayers = psiSoil.length();
  NumericVector rrhizo(nlayers, 0.0);
  NumericVector rroot(nlayers, 0.0);
  for(int i=0;i<nlayers;i++) {
    rrhizo[i] = 1.0/vanGenuchtenConductance_c(psiSoil[i], krhizomax[i], n[i], alpha[i]);
    rroot[i] = 1.0/xylemConductance_c(psiRhizo[i], krootmax[i], rootc, rootd);
  }
  double rstem = 1.0/(kstemmax*std::min(1.0-PLCstem, xylemConductance_c(psiStem, 1.0, stemc, stemd)));
  double rleaf = 1.0/(kleafmax*std::min(1.0-PLCleaf, xylemConductance_c(psiLeaf, 1.0, leafc, leafd)));
  List resistances = List::create(_["rhizosphere"] = rrhizo, 
                                  _["root"] = rroot, 
                                  _["stem"] = rstem, 
                                  _["leaf"] = rleaf);
  return(resistances);
}






/**
 * Proportions of root xylem conductance
 * 
 * Calculates the proportion of total xylem conductance that corresponds to each layer in a network of 
 * parallel xylem resistances.
 * 
 * Sperry, J. S., Y. Wang, B. T. Wolfe, D. S. Mackay, W. R. L. Anderegg, N. G. Mcdowell, and W. T. Pockman. 2016. 
 * Pragmatic hydraulic theory predicts stomatal responses to climatic water deficits. 
 * New Phytologist 212:577–589.
 * 
 */
//' @rdname hydraulics_scalingconductance
//' @keywords internal
// [[Rcpp::export("hydraulics_rootxylemConductanceProportions")]]
NumericVector rootxylemConductanceProportions(NumericVector L, NumericVector V) {
  return(Rcpp::wrap(rootxylemConductanceProportions_c( 
      as<std::vector<double>>(L),
      as<std::vector<double>>(V))));
}

/**
 * Calculate maximum leaf-specific root hydraulic conductance (in mmol·m-2·s-1·MPa-1)
 * 
 * xylemConductivity - Sapwood-specific conductivity of root xylem (in kg·m-1·s-1·MPa-1)
 * Al2As - Leaf area to sapwood area ratio (in m2·m-2)
 * v - proportion of fine roots in each soil layer
 * widths - soil layer depths (in mm)
 */
// double maximumRootHydraulicConductance(double xylemConductivity, double Al2As, NumericVector v, 
//                                        NumericVector widths, double depthWidthRatio = 1.0){
//   NumericVector rl = coarseRootLengths(v,widths, depthWidthRatio);
//   NumericVector w = xylemConductanceProportions(v,widths, depthWidthRatio);
//   int nlayers = v.length();
//   double kmax = 0.0;
//   for(int i=0;i<nlayers;i++) {
//     if(rl[i]>0.0) kmax = kmax + w[i]*(1000.0/0.018)*(xylemConductivity/((rl[i]/1000.0)*Al2As)); 
//   }
//   return(kmax); 
// }
