diff --git a/DESCRIPTION b/DESCRIPTION
index c7daa70b755713420ba59666d37703d46594305e..8dfab7e1c23c5de48c76b42226866c4d20ae8a29 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
 Package: airGR
 Type: Package
 Title: Suite of GR Hydrological Models for Precipitation-Runoff Modelling
-Version: 1.3.1.4
+Version: 1.3.2.0
 Date: 2019-05-22
 Authors@R: c(
   person("Laurent", "Coron", role = c("aut", "trl"), comment = c(ORCID = "0000-0002-1503-6204")),
diff --git a/NEWS.rmd b/NEWS.rmd
index da3f2b091044209dfd563daaef054417fa7e9dd8..805f06f416e43c80345f0e14dad81623fef4f2de 100644
--- a/NEWS.rmd
+++ b/NEWS.rmd
@@ -14,7 +14,7 @@ output:
 
 
 
-### 1.3.1.4 Release Notes (2019-05-22)
+### 1.3.2.0 Release Notes (2019-05-22)
 
 
 #### New features
@@ -23,6 +23,8 @@ output:
 
 - Added <code>RunModel_CemaNeigeGR4H()</code> function to run the hourly model GR4H with the CemaNeige module.
 
+- Added <code>PE_Oudin()</code> function to compute Oudin's potential evapotranspiration for hourly or daily tim steps.
+
 
 #### Major user-visible changes
 
diff --git a/R/PE_Oudin.R b/R/PE_Oudin.R
new file mode 100644
index 0000000000000000000000000000000000000000..e02bfa4751ac7d99acefb85d9f3eed1732a977b5
--- /dev/null
+++ b/R/PE_Oudin.R
@@ -0,0 +1,154 @@
+PE_Oudin <- function(JD, Temp,
+                     LatRad, Lat, LatUnit = c("rad", "deg"),
+                     TimeStepIn = "daily", TimeStepOut = "daily") {
+  
+  
+  ## ---------- check arguments
+  
+  if (!missing(LatRad)) {
+    warning("Deprecated 'LatRad' argument. Please, use 'Lat' instead.")
+    if (missing(Lat)) {
+      Lat <- LatRad
+    }
+  }
+  if (!(inherits(JD, "numeric") | inherits(JD, "integer"))) {
+    stop("'JD' must be of class 'numeric'")
+  }
+  if (!(inherits(Temp, "numeric") | inherits(Temp, "integer"))) {
+    stop("'Temp' must be of class 'numeric'")
+  }
+  if (length(JD) != length(Temp)) {
+    stop("'Temp' and 'LatUnit' must have the same length")
+  }
+  if (!any(LatUnit %in% c("rad", "deg"))) {
+    stop("'LatUnit' must be one of \"rad\" or \"deg\"")
+  }
+  if (!inherits(Lat, "numeric") | length(Lat) != 1) {
+    stop("'Lat' must be a 'numeric' of length one")
+  }
+  if (LatUnit[1L] == "rad" & ((Lat >= pi/2) | (Lat <= -pi/2))) {
+    stop("'Lat' must be comprised between -pi/2 and +pi/2 degrees")
+  }
+  if (LatUnit[1L] == "deg" & ((Lat >= 90) | (Lat <= -90))) {
+    stop("'Lat' must be  comprised between -90 and +90 degrees")
+  }
+  if (LatUnit[1L] == "rad") {
+    FI <- Lat
+  }
+  if (LatUnit[1L] == "deg") {
+    FI <- Lat / (180 / pi)
+  }
+  if (any(JD < 0) | any(JD > 366)) {
+    stop("'JD' must only contain integers from 1 to 366")
+  }
+  if (!inherits(TimeStepIn, "character") | length(TimeStepIn) != 1) {
+    stop("'TimeStepIn' must be a 'character' of length one")
+  }
+  if (!inherits(TimeStepOut, "character") | length(TimeStepOut) != 1) {
+    stop("'TimeStepIn' must be a 'character' of length one")
+  }
+  if (!(TimeStepIn %in% c("daily", "hourly"))) {
+    stop("'TimeStepIn' must be one of \"daily\" or \"hourly\"")
+  }
+  if (!(TimeStepOut %in% c("daily", "hourly"))) {
+    stop("'TimeStepOut' must be one of \"daily\" or \"hourly\"")
+  }
+  
+  
+  ## ---------- hourly inputs aggregation
+  
+  if (TimeStepIn == "hourly") {
+    rleJD <- rle(JD)
+    JD <- rleJD$values
+    if (any(rleJD$lengths != 24)) {
+      stop("each days must have 24 identical Julian day values (one for each hour)")
+    }
+    idJD <- rep(seq_along(JD), each = rleJD$lengths[1L])
+    Temp <- as.vector(tapply(X = Temp, INDEX = idJD, FUN = mean))
+  }
+  
+  
+  ## ---------- Oudin's formula
+  
+  PE_Oudin_D <- rep(NA, length(Temp))
+  COSFI <- cos(FI)
+  AFI <- abs(FI / 42) 
+  
+  for (k in seq_along(Temp)) {
+    
+    TETA <- 0.4093 * sin(JD[k] / 58.1 - 1.405)
+    COSTETA <- cos(TETA)
+    COSGZ <- max(0.001, cos(FI - TETA))
+    GZ <- acos(COSGZ)
+    COSOM <- 1 - COSGZ / COSFI / COSTETA
+    
+    if (COSOM < -1) {
+      COSOM <- -1
+    }
+    if (COSOM > 1) {
+      COSOM <-  1
+    }
+    
+    COSOM2 <- COSOM * COSOM
+    
+    if (COSOM2 >= 1) {
+      SINOM <- 0
+    } else {
+      SINOM <- sqrt(1 - COSOM2)
+    }
+    
+    OM <- acos(COSOM)
+    COSPZ <- COSGZ + COSFI * COSTETA * (SINOM/OM - 1)
+    
+    if (COSPZ < 0.001) {
+      COSPZ <- 0.001
+    }
+    
+    ETA <- 1 + cos(JD[k] / 58.1) / 30
+    GE <- 446 * OM * COSPZ * ETA
+    
+    if (is.na(Temp[k])) {
+      PE_Oudin_D[k] <- NA
+    } else {
+      if (Temp[k] >= -5.0) {
+        PE_Oudin_D[k] <- GE * (Temp[k] + 5) / 100 / 28.5
+      } else {
+        PE_Oudin_D[k] <- 0
+      }
+    }
+    
+  }
+  
+  
+  ## ---------- disaggregate PE from daily to hourly
+  
+  if (TimeStepOut == "hourly") {
+    sinus_D2H <- c(0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
+                   0.035, 0.062, 0.079, 0.097, 0.110, 0.117,
+                   0.117, 0.110, 0.097, 0.079, 0.062, 0.035,
+                   0.000, 0.000, 0.000, 0.000, 0.000)
+    PE_Oudin_H <- rep(PE_Oudin_D, each = 24) * rep(sinus_D2H, times = length(PE_Oudin_D))
+  }
+  
+  
+  ## ---------- outputs warnings
+  
+  if (any(is.na(Temp))) {
+    if (any(is.na(PE_Oudin_D))) {
+      warning("'Temp' time series, and therefore the returned 'PE' time series, contain missing value(s)")
+    } else {
+      warning("'Temp' time series contains missing value(s)")
+    }
+  }
+  if (!any(is.na(Temp)) & any(is.na(PE_Oudin_D))) {
+    warning("returned 'PE' time series contains missing value(s)")
+  }
+  
+  if (TimeStepOut == "daily") {
+    PE_Oudin <- PE_Oudin_D
+  } else {
+    PE_Oudin <- PE_Oudin_H
+  }
+  return(PE_Oudin)
+  
+}