Shiny-sequence-to-kcpairs

[ref doc] what is done when converting a crop sequence file (& refyear+kc_pairs per crop files) into an integrated kc pairs VLE parameter for ASCROM model

Some context on ASCROM plot model and associated MFX_X Shiny applications

In the ASCROM model the part responsible for the kcb dynamic (eg. basal crop coefficient) is managed by an instance of the vle.discrete-time.generic/Interpolate atomic model.

vle.discrete-time.generic/Interpolate atomic model is a generic interpolation function based on time and value pairs given as parameters at the start of the simulation.

        <port name="kcb_params">
          <map>
            <key name="t0">
              <integer>1</integer>
            </key>
            <key name="pairs">
              <set>
                <map>
                  <key name="kcb">
                    <double>0</double>
                  </key>
                  <key name="date">
                    <integer>0</integer>
                  </key>
                </map>
                <map>
                  <key name="kcb">
                    <double>0.3</double>
                  </key>
                  <key name="date">
                    <integer>1</integer>
                  </key>
                </map>
              </set>
            </key>
          </map>
        </port>

XML representation of an example of the vle.discrete-time.generic/Interpolate parameters (see atomic model reference documentation in VLE package for more details)

As a consequence, for configuring the models using ASCROM, one need to provide a set of date/value pairs corresponding to all the crops to be simulated on a given plot during the simulation.

For practical reasons, in the Shiny applications MFX_X available in this rASCROM R package, we added several intermediate csv data files that allow the user to choose a crop sequence from a set of predefined ones (see data files reference documentation vignette in rASCROM R package). Then the Shiny applications use the information in those files along with a reference year and a set of per crop kc pairs files to generate an integrated sequence of date/value pairs corresponding to all the crops to be simulated.

Content of a sample crop sequence file
sequence padding startdate enddate
F_onion 0 15/4/+0 20/7/+0
F_beetroot 0 1/8/+0 31/10/+0
F_tomato 0 15/11/+0 31/03/+1
F_onion 0 15/4/+1 20/7/+1
F_beetroot 0 1/8/+1 31/10/+1
F_tomato 0 15/11/+1 31/03/+2
Content of a sample crop kc pairs file
date kc
0 0
1 0.15
113 0.15
196 1.05
310 1.05
363 0.9
364 0

Detailled example of the algorithm computation steps

First step is to use a user defined reference year (in the following example we use: 2015, Nota: 2016 is a leap year) to fully define the sequence dates

sequence padding startdate enddate
F_onion 0 15/4/2015 20/7/2015
F_beetroot 0 1/8/2015 31/10/2015
F_tomato 0 15/11/2015 31/03/2016
F_onion 0 15/4/2016 20/7/2016
F_beetroot 0 1/8/2016 31/10/2016
F_tomato 0 15/11/2016 31/03/2017

Then the sequence column is used to get all crops involved. Each crop identifier correspond to a reference kc pairs file that is loaded for latter use.

F_onion crop kc pairs file content
date kc
0 0
1 0.15
18 0.15
62 0.95
80 0.95
89 0.65
90 0
F_beetroot crop kc pairs file content
date kc
0 0
1 0.15
18 0.15
39 0.95
57 0.95
64 0.85
65 0
F_tomato crop kc pairs file content
date kc
0 0
1 0.15
20 0.15
46 1.2
73 1.2
89 0.7
90 0

Two separate checking steps are then done to ensure :

  1. Non overlapping time windows for each crops in the sequence (and also that the first crop of the sequence occurs after the simulation starting date)
  2. That the time window allocated for each crop in the sequence file is big enough to fit the full duration of a crop as defined by its reference kc pair file. (eg. number of days between sequence’s startdate and enddate is not smaller than the last value of date for the corresponding crop) (Nota: depending on whether the reference year used is a leap year or not this can affect the number of days between two calendar dates)

Then for each element of the sequence we first compute a padding duration corresponding to the number of days since the previous crop (or the start of simulation for the first crop of the sequence, in the following example we use: 2015-1-1).

We then sequentially integrate these paddings with the pairs duration of the previous crop and the difference between the current crop dates and pairs duration.

sequence padding startdate enddate dates_duration pairs_duration integrated_padding
F_onion 104 15/4/2015 20/7/2015 96 90 104
F_beetroot 12 1/8/2015 31/10/2015 91 65 212
F_tomato 15 15/11/2015 31/03/2016 137 90 318
F_onion 15 15/4/2016 20/7/2016 96 90 470
F_beetroot 12 1/8/2016 31/10/2016 91 65 578
F_tomato 15 15/11/2016 31/03/2017 136 90 684

Finally these integrated padding are used to shift all the dates when concatenating the individual crop reference kc pairs.

date kcb calendar_dates
104 0 15/04/2015
105 0.15
122 0.15
166 0.95
184 0.95
193 0.65
194 0
212 0 01/08/2015
213 0.15
230 0.15
251 0.95
269 0.95
276 0.85
277 0
318 0 15/11/2015
319 0.15
338 0.15
364 1.2
391 1.2
407 0.7
408 0
470 0 15/04/2016
471 0.15
488 0.15
532 0.95
550 0.95
559 0.65
560 0
578 0 01/08/2016
579 0.15
596 0.15
617 0.95
635 0.95
642 0.85
643 0
684 0 15/11/2016
685 0.15
704 0.15
730 1.2
757 1.2
773 0.7
774 0
.generateKcPairs
#> function (RvleObj, condPortName, T0, crop_seq, kc_list, debut_sim, 
#>     clim_corr = FALSE, clim_filepath = "", h = 0, dbgLog = FALSE) 
#> {
#>     error_kc <- FALSE
#>     kc_params <- .getSamplePair(RvleObj, condPortName, T0)
#>     sample_kc_pair <- kc_params$pairs[[1]]
#>     kc_pair_index <- 1
#>     padding <- 0
#>     dureeDates <- dureePaires <- vector(mode = "numeric", length = nrow(crop_seq))
#>     for (idseq_index in 1:nrow(crop_seq)) {
#>         if (idseq_index == 1) {
#>             crop_seq$padding[idseq_index] <- as.integer(chron(crop_seq$startdate[idseq_index], 
#>                 format = c(dates = "d/m/Y")) - chron(debut_sim, 
#>                 format = c(dates = "Y-m-d")))
#>         }
#>         else {
#>             crop_seq$padding[idseq_index] <- as.integer(chron(crop_seq$startdate[idseq_index], 
#>                 format = c(dates = "d/m/Y")) - chron(crop_seq$enddate[idseq_index - 
#>                 1], format = c(dates = "d/m/Y")))
#>         }
#>         {
#>             if (crop_seq$padding[idseq_index] < 1) {
#>                 if (idseq_index == 1) {
#>                   cat(file = stderr(), "Attention date de debut de la sequence avant le debut de simulation\n")
#>                   cat(file = stderr(), as.character(chron(debut_sim, 
#>                     format = c(dates = "Y-m-d"), out.format = c(dates = "d/m/Y"))), 
#>                     " < ", as.character(chron(crop_seq$startdate[idseq_index], 
#>                       format = c(dates = "d/m/Y"))), "\n")
#>                 }
#>                 else {
#>                   cat(file = stderr(), "Attention date de debut avant date de fin du precedent\n")
#>                   cat(file = stderr(), as.character(chron(crop_seq$startdate[idseq_index], 
#>                     format = c(dates = "d/m/Y"))), " < ", as.character(chron(crop_seq$enddate[idseq_index - 
#>                     1], format = c(dates = "d/m/Y"))), "\n", 
#>                     idseq_index, " vs ", idseq_index - 1, " (element de la sequence)\n")
#>                 }
#>                 error_kc <- TRUE
#>             }
#>             dureeDates[idseq_index] <- as.integer(chron(crop_seq$enddate[idseq_index], 
#>                 format = c(dates = "d/m/Y")) - chron(crop_seq$startdate[idseq_index], 
#>                 format = c(dates = "d/m/Y")))
#>             dureePaires[idseq_index] <- max(kc_list[[crop_seq$sequence[idseq_index]]]$date)
#>             if (dureeDates[idseq_index] < dureePaires[idseq_index]) {
#>                 cat(file = stderr(), "Attention durees plus courte entre les dates du fichier sequence et la duree de cycle du fichier de kc\n")
#>                 cat(file = stderr(), idseq_index, "eme element de la sequence, ID:", 
#>                   crop_seq$sequence[idseq_index], "\n")
#>                 cat(file = stderr(), dureeDates[idseq_index], 
#>                   "(dates sequence) != ", dureePaires[idseq_index], 
#>                   "(duree paires)\n")
#>                 error_kc <- TRUE
#>             }
#>         }
#>         extra_seq_pairs_delay <- ifelse(idseq_index == 1, 0, 
#>             dureeDates[idseq_index - 1] - dureePaires[idseq_index - 
#>                 1])
#>         padding <- padding + crop_seq$padding[idseq_index] + 
#>             extra_seq_pairs_delay
#>         for (idpair_index in 1:nrow(kc_list[[crop_seq$sequence[idseq_index]]])) {
#>             kc_params$pairs[[kc_pair_index]] <- sample_kc_pair
#>             kc_params$pairs[[kc_pair_index]]$kcb <- kc_list[[crop_seq$sequence[idseq_index]]][idpair_index, 
#>                 ]$kc
#>             kc_params$pairs[[kc_pair_index]]$date <- as.integer(kc_list[[crop_seq$sequence[idseq_index]]][idpair_index, 
#>                 ]$date + padding)
#>             kc_pair_index <- kc_pair_index + 1
#>         }
#>         padding <- padding + max(kc_list[[crop_seq$sequence[idseq_index]]]$date)
#>     }
#>     if (clim_corr & file.exists(clim_filepath)) {
#>         if (dbgLog) 
#>             cat(file = stderr(), "Calcul de correction climatique sur les valeurs de kc\n")
#>         weatherdata <- read_delim(clim_filepath, delim = " ", 
#>             col_types = cols_only(annee = col_integer(), mois = col_integer(), 
#>                 jour = col_integer(), RHmin = col_double(), u2 = col_double()))
#>         kc_params <- .kc_climate_correction(kc_params, weatherdata, 
#>             debut_sim, h, dbgLog)
#>     }
#>     else {
#>         cat(file = stderr(), "Pas de correction climatique sur les valeurs de kc\n")
#>     }
#>     return(list(KC_PARAMS = kc_params, KC_SEQ = crop_seq, ERROR_KC = error_kc))
#> }
#> <bytecode: 0x560da5e1b2d0>