Advertisement
karstenw

Ressource allocation example

Oct 14th, 2016
334
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
R 20.76 KB | None | 0 0
  1. library(lpSolveAPI)
  2. library(doParallel)
  3. library(foreach)
  4. library(compiler)
  5. #library(datamart)
  6.  
  7. #' internal function used by ds_user_parallel
  8. #' assign capacities to workload for one region and determines transferable workload
  9. ds_user <- function(
  10.   workload, capacity, min_workertype, min_worktype, region, month_map, NbMonth, categ_map, worktype_map, NbWT, Nbtot, NbCat, dimsToIndex,
  11.   no_work, max_offshore
  12. ) {      
  13.   if(length(region)!=1) stop("region must be character of length 1.")
  14.  
  15.   ### set up lp
  16.   # the first Nbtot are the decision variables we are interested in (effort per month/work_type/categ/workplace/work_source). These do not
  17.   # go into the objective (zero coefficients). They are, however, the main result.
  18.   # the next NbCat variables are the "unmet workload" that is the work for which there is no capacity. Ideally, these would all be zero.
  19.   # the next NbCat variables are "max effort over the months for a given category". This is used to evenly distribute the effort.
  20.   # the next NbWT variables are "min capacity required" or "max effort over the months for a given worker type.
  21.   lprec <- make.lp(0, ncol=Nbtot+NbCat+NbCat+NbWT+NbCat)
  22.   set.objfn(lprec, obj=c(rep(0, Nbtot), rep(1000, NbCat), rep(1, NbCat), c(100, 80, 60), rep(500, NbCat)))
  23.  
  24.   ### constraints 1: no work in certain months for some types of work
  25.   for (categ in names(no_work))
  26.     for (m in intersect(names(month_map), no_work[[categ]])) {
  27.       idx <- dimsToIndex(month=m, categ=categ)
  28.       add.constraint(lprec, xt=rep(1, length(idx)), type="=", rhs=0, indices=idx)
  29.     }
  30.  
  31.   ### constraints 2: workload must be met
  32.   for (categ in names(categ_map)) {
  33.     add.constraint(
  34.       lprec, xt=rep(1, NbMonth*NbWT+2), "=", rhs=workload[region, categ],
  35.       indices=c(
  36.         dimsToIndex(categ=categ),
  37.         Nbtot+categ_map[categ], # unmet untransferable effort
  38.         Nbtot+NbCat+NbCat+NbWT+categ_map[categ] # unmet transferable effort
  39.       )
  40.     )
  41.   }
  42.  
  43.   ### constraints 3: effort <= capacity
  44.   for (m in names(month_map)) {
  45.     idx <- which(capacity[,"mon"]==m)
  46.     add.constraint(
  47.       lprec, xt=rep(1, NbCat), type="<=", rhs=capacity[idx, "dcutil"],
  48.       indices=dimsToIndex(month=m, worker_type="data_collector")
  49.     )
  50.     add.constraint(
  51.       lprec, xt=rep(1, NbCat), type="<=", rhs=capacity[idx, "regutil"],
  52.       indices=dimsToIndex(month=m, worker_type="regular")
  53.     )
  54.     add.constraint(
  55.       lprec, xt=rep(1, NbCat), type="<=", rhs=capacity[idx, "nonregutil"],
  56.       indices=dimsToIndex(month=m, worker_type="nonregular")
  57.     )
  58.   }
  59.  
  60.   ### constraints 4: minimum worker type utilization
  61.   ## for each month, region and category, we have two constraints:
  62.   ## 1) effort_regular >= perc * (effort_dc + effort_nonreg + effort_regular)
  63.   ## 2) effort_dc <= perc * (effort_dc + effort_nonreg + effort_regular)
  64.   ## where perc are input constants given in the min_workertype data.frame
  65.   ## By simple algebra, this normalizes to
  66.   ## 1') -perc * effort_dc - perc * effort_nonreg + (1-perc) * effort_regular >= 0
  67.   ## 2') (1-perc) * effort_dc - perc * effort_nonreg - perc * effort_regular <= 0
  68.   ## the second constraint only applies if there is actually data_collector capacity in that region
  69.   for (m in names(month_map))
  70.     for (categ in names(categ_map)) {
  71.       cap_idx <- which(capacity[,"mon"]==m)
  72.       if(capacity[cap_idx, "regutil"]>0){
  73.         idx1 <- dimsToIndex(month=m, categ=categ, worker_type=c("data_collector", "nonregular"))
  74.         idx2 <- dimsToIndex(month=m, categ=categ, worker_type="regular")
  75.         perc <- min_workertype[categ, "reg_min"]
  76.         add.constraint(lprec, xt=c(rep(-perc, 2), (1-perc)), type=">=", rhs=0, indices=c(idx1, idx2))
  77.       }
  78.       if(capacity[cap_idx, "dcutil"]>0) {
  79.         idx1 <- dimsToIndex(month=m, categ=categ, worker_type=c("regular", "nonregular"))
  80.         idx2 <- dimsToIndex(month=m, categ=categ, worker_type="data_collector")
  81.         perc <- min_workertype[categ, "dc_min"]
  82.         add.constraint(lprec, xt=c(rep(-perc, 2), (1-perc)), type="<=", rhs=0, indices=c(idx1, idx2))
  83.       }
  84.     }
  85.  
  86.   ### constraints 5: maximum work that can be done offshore
  87.   ## only a certain percentage can be done offshore
  88.   for (m in names(month_map))
  89.     for (categ in names(categ_map))
  90.       add.constraint(lprec, xt=1/max_offshore[categ], "<=", rhs=workload[region, categ], indices=Nbtot+NbCat+NbCat+NbWT+categ_map[categ])
  91.      
  92.   ### constraints 6: minimum work for some worktypes
  93.   for (categ in names(min_worktype)) {
  94.     rhs <- workload[region, categ] * min_worktype[categ]
  95.     for (m in names(month_map)) add.constraint(lprec, xt=rep(1, NbWT), ">=", rhs=rhs, indices=dimsToIndex(month=m, categ=categ))
  96.   }
  97.  
  98.   ### helper variable: maximum effort over all months for each category
  99.   for (m in names(month_map))
  100.     for (categ in names(categ_map))
  101.       add.constraint(
  102.         lprec, xt=c(rep(1, NbWT),-1), "<=", rhs=0,
  103.         indices=c(dimsToIndex(month=m, categ=categ), Nbtot+NbCat+categ_map[categ])
  104.       )
  105.  
  106.   ### helper variable: maximum effort over all months for each worker type
  107.   for (m in names(month_map))
  108.     for (wt in names(worktype_map))
  109.       add.constraint(
  110.         lprec, xt=c(rep(1, NbCat), -1), "<=", rhs=0,
  111.         indices=c(dimsToIndex(month=m, worker_type=wt), Nbtot+NbCat+NbCat+worktype_map[wt])
  112.       )
  113.  
  114.   # browser()
  115.   ### solve
  116.   status <- solve(lprec)
  117.  
  118.   ### no solution? halt
  119.   if(status!=0) stop(paste("no solution found, error code ", status, "; region ", region))
  120.  
  121.   # unmet, non-transferable.
  122.   unmet <- matrix(get.variables(lprec)[seq(Nbtot+1, length.out=NbCat)], nrow=1, dimnames=list(region, names(categ_map)))
  123.  
  124.   # resulting effort
  125.   effort <- get.variables(lprec)[1:Nbtot]
  126.   ans <- expand.grid(
  127.     Month=names(month_map), Category=names(categ_map),
  128.     Region=region, Worker_Type=names(worktype_map),
  129.     stringsAsFactors=FALSE
  130.   )
  131.   lookup_one <- function(x) effort[dimsToIndex(month=x[["Month"]], categ=x[["Category"]], worker_type=x[["Worker_Type"]])]
  132.   ans[,"Effort"] <- apply(ans, 1, lookup_one)
  133.  
  134.   # transferable
  135.   transferable <- get.variables(lprec)[seq(Nbtot+NbCat+NbCat+NbWT+1, length.out=NbCat)]
  136.   idx <- which(transferable>.Machine$double.eps^0.5)
  137.   if(length(idx)>0) {
  138.     transferable_df <- data.frame(
  139.       region=region,
  140.       categ=names(categ_map)[idx],
  141.       transferable=transferable[idx],
  142.       stringsAsFactors=FALSE
  143.     )
  144.   } else transferable_df <- data.frame()
  145.  
  146.   # if( nrow(transferable_df)>0) browser()
  147.   return(list(effort=ans, unmet=unmet, transferable=transferable_df))
  148. }
  149.  
  150. #' internal function used by ds_user_parallel
  151. #' distributes transferable workload to regions with free capacities
  152. ds_user_transfer <- function(effort, capacity, transferable, min_workertype, month_map, NbMonth, categ_map, worktype_map, NbWT, NbCat, no_work) {
  153.   # calculate the capacity remaining after onshore work
  154.   eff <- aggregate(Effort ~ Month + Region + Worker_Type, data=effort, sum)
  155.   eff <- reshape(eff, timevar="Worker_Type", idvar=c("Month","Region"), direction="wide")
  156.   eff <- eff[, c("Month", "Region", "Effort.data_collector", "Effort.nonregular", "Effort.regular")]
  157.   colnames(eff) <- c("mon", "region", "dceff", "nonregeff", "regeff")
  158.   remaining_capa <- merge(eff, capacity)
  159.   remaining_capa[, "regremain"] <- pmax(0, remaining_capa[, "regutil"] - remaining_capa[, "regeff"])
  160.   remaining_capa[, "nonregremain"] <- pmax(0, remaining_capa[, "nonregutil"] - remaining_capa[, "nonregeff"])
  161.   remaining_capa[, "dcremain"] <- pmax(0, remaining_capa[, "dcutil"] - remaining_capa[, "dceff"])
  162.   remaining_capa <- remaining_capa[,c("mon", "region", "regremain", "nonregremain", "dcremain")]
  163.  
  164.   # from where to where can work be shifted?
  165.   from_regions <- unique(transferable[,"region"])
  166.   NbShifts <- nrow(transferable) # region / categ combinations
  167.   idx <- !(remaining_capa[,"region"] %in% from_regions) &
  168.     (remaining_capa[,"regremain"]+remaining_capa[,"nonregremain"]+remaining_capa[,"dcremain"])>0
  169.   to_regions <- unique(remaining_capa[idx, "region"])
  170.   NbRegTo <- length(to_regions)
  171.  
  172.   # is there at least one region where we can transfer work to?
  173.   if(length(to_regions)==0) {
  174.     unmet_df <- reshape(transferable, direction="wide", timevar="categ", idvar="region")
  175.     rownames(unmet_df) <- unmet_df[,"region"]
  176.     unmet_df[,"region"] <- NULL
  177.     unmet_df <- as.matrix(unmet_df)
  178.     unmet_df[is.na(unmet_df)] <- 0
  179.     colnames(unmet_df) <- substring(colnames(unmet_df), 14)
  180.     return(list(transfers=data.frame(), still_unmet=unmet_df))
  181.   }
  182.  
  183.   # set up lp
  184.   # first NbShifts*NbRegTo*NbWT*NbMonth entries: effort for the transfers
  185.   # next NbRegTo*NbWT entries: capacity needed after the shifts (this is to minimize)
  186.   # next NbShifts entries: unmet untransferable work (hopefully zero, this is penalized)
  187.   lprec <- make.lp(0, ncol=NbShifts*NbRegTo*NbWT*NbMonth+NbRegTo*NbWT+NbShifts)
  188.   set.objfn(lprec, obj=c(rep(0, NbShifts*NbRegTo*NbWT*NbMonth), rep(c(100, 80, 60), NbRegTo), rep(1000, NbShifts)))
  189.   cat("#Vars: ", NbShifts*NbRegTo*NbWT*NbMonth+NbRegTo*NbWT+NbShifts, "\n")
  190.  
  191.   ### constraints 1: no work in certain months for some types of work
  192.   for (categ in names(no_work))
  193.     for (m in intersect(names(month_map), no_work[[categ]])) {
  194.       idx_shifts <- which(transferable[,"categ"]==categ)
  195.       for (i in idx_shifts)
  196.         for (reg in seq.int(NbRegTo))
  197.           for (wt in names(worktype_map)) {
  198.             add.constraint(
  199.               lprec,
  200.               xt=1, type="=", rhs=0,
  201.               indices=(i-1)*NbRegTo*NbWT*NbMonth+(reg-1)*NbMonth + (worktype_map[wt]-1)*NbMonth + month_map[m]
  202.             )
  203.           }
  204.     }
  205.  
  206.   ### constraints 2: workload must be met
  207.   for (i in seq.int(nrow(transferable))) {
  208.     add.constraint(
  209.       lprec, xt=rep(1, NbRegTo*NbWT*NbMonth+1), "=", rhs=transferable[i, "transferable"] ,
  210.       indices=c(seq((i-1)*NbRegTo*NbWT*NbMonth+1, i*NbRegTo*NbWT*NbMonth), NbShifts*NbRegTo*NbWT*NbMonth+NbRegTo*NbWT+i)
  211.     )
  212.   }
  213.  
  214.   ### constraints 3: transfered effort <= remaining capacity
  215.   for (m in names(month_map))
  216.     for (reg in seq.int(NbRegTo)) {
  217.       idx <- which(remaining_capa[,"mon"]==m & remaining_capa[,"region"]==to_regions[reg])
  218.       add.constraint(
  219.         lprec, xt=rep(1, NbShifts), type="<=", rhs=capacity[idx, "dcutil"],
  220.         indices=(seq.int(NbShifts)-1)*NbRegTo*NbWT*NbMonth+(reg-1)*NbWT*NbMonth+(worktype_map["data_collector"]-1)*NbMonth+month_map[m]
  221.       )
  222.       add.constraint(
  223.         lprec, xt=rep(1, NbShifts), type="<=", rhs=capacity[idx, "regutil"],
  224.         indices=(seq.int(NbShifts)-1)*NbRegTo*NbWT*NbMonth+(reg-1)*NbWT*NbMonth+(worktype_map["regular"]-1)*NbMonth+month_map[m]
  225.       )
  226.       add.constraint(
  227.         lprec, xt=rep(1, NbShifts), type="<=", rhs=capacity[idx, "nonregutil"],
  228.         indices=(seq.int(NbShifts)-1)*NbRegTo*NbWT*NbMonth+(reg-1)*NbWT*NbMonth+(worktype_map["nonregular"]-1)*NbMonth+month_map[m]
  229.       )
  230.     }
  231.  
  232.   ### constraints 4: minimum worker type utilization
  233.   ## for each month, region and category, we have two constraints:
  234.   ## 1) effort_regular_from_offshore + effort_regular_onsite >= perc * (effort_total_from_offshore + effort_total_onsite)
  235.   ## 2) effort_dc_from_offshore + effort_dc_onsite <= perc * (effort_total_offshore + effort_total_onsite)
  236.   ## where perc are input constants depending on the category given in the min_workertype data.frame
  237.   ## By simple algebra, this normalizes to
  238.   ## 1') (1-perc) effort_regular_offshore >= perc effort_total_onsite - effort_regular_onsite
  239.   ## 2') (1-perc) effort_dc_offshore <= perc effort_total_onsite - effort_dc_onsite
  240.   ## the second constraint only applies if there is actually data_collector capacity in that region
  241.   for (reg in seq.int(NbRegTo))
  242.     for (m in names(month_map))
  243.       for (categ in names(categ_map)) {
  244.         # lookup onsite values
  245.         idx <- which(effort[,"Month"]==m & effort[, "Category"]==categ & effort[, "Region"]==to_regions[reg])
  246.         onsite <- setNames(effort[idx, "Effort"], effort[idx, "Worker_Type"])
  247.         perc <- min_workertype[categ, "reg_min"]
  248.         add.constraint(
  249.           lprec, xt=rep((1-perc), NbShifts), type=">=",
  250.           rhs=perc*sum(onsite)-onsite["regular"],
  251.           indices=(seq.int(NbShifts)-1)*NbRegTo*NbWT*NbMonth+(reg-1)*NbWT*NbMonth+(worktype_map["regular"]-1)*NbMonth+month_map[m]
  252.         )
  253.         perc <- min_workertype[categ, "dc_min"]
  254.         add.constraint(
  255.           lprec, xt=rep((1-perc), NbShifts), type="<=",
  256.           rhs=perc*sum(onsite)-onsite["data_collector"],
  257.           indices=(seq.int(NbShifts)-1)*NbRegTo*NbWT*NbMonth+(reg-1)*NbWT*NbMonth+(worktype_map["data_collector"]-1)*NbMonth+month_map[m]
  258.         )
  259.       }
  260.  
  261.   ### helper variable: maximum effort over all months for each worker type
  262.   for (reg in seq.int(NbRegTo))
  263.     for (m in names(month_map))
  264.       for (wt in names(worktype_map)) {
  265.         # lookup onsite values
  266.         idx <- which(effort[,"Month"]==m & effort[, "Region"]==to_regions[reg] & effort[, "Worker_Type"]==wt)
  267.         onsite <- sum(effort[idx, "Effort"]) # sum over categories
  268.         add.constraint(
  269.           lprec, xt=c(rep(1, NbShifts), -1), "<=", rhs=-onsite,
  270.           indices=c((seq.int(NbShifts)-1)*NbRegTo*NbWT*NbMonth+(reg-1)*NbWT*NbMonth+(worktype_map[wt]-1)*NbMonth+month_map[m],
  271.                     NbShifts*NbRegTo*NbWT*NbMonth+(reg-1)*NbWT+worktype_map[wt])
  272.         )
  273.       }
  274.  
  275.   ### solve
  276.   status <- solve(lprec)
  277.  
  278.   ### no solution? halt
  279.   if(status!=0) stop(paste("no solution found, error code ", status))
  280.  
  281.   ### gather results
  282.   one_transfer <- function(i) {
  283.     vars <- get.variables(lprec)[seq((i-1)*NbRegTo*NbWT*NbMonth+1, i*NbRegTo*NbWT*NbMonth)]
  284.     idx1 <- which(vars > .Machine$double.eps^0.5)
  285.     if(length(idx1)>0) {
  286.       reg <- idx1 %/% (NbWT*NbMonth) + 1
  287.       idx <- idx1 - (reg - 1) * (NbWT*NbMonth)
  288.       wt <- idx %/% NbMonth + 1
  289.       m <- idx - (wt - 1) * NbMonth + 1
  290.       # browser()
  291.      
  292.       ret <- data.frame(
  293.         region_from=transferable[i, "region"],
  294.         categ=transferable[i,"categ"],
  295.         region_to=to_regions[reg],
  296.         worker_type=names(worktype_map)[wt],
  297.         mon=names(month_map)[m],
  298.         effort=vars[idx1],
  299.         stringsAsFactors=FALSE
  300.       )
  301.     } else ret <- data.frame()
  302.     return(ret)
  303.   }
  304.  
  305.   transf <- lapply(seq.int(nrow(transferable)), FUN=one_transfer)
  306.   transf <- do.call(rbind, transf)
  307.  
  308.   ### still unmet workload?
  309.   unmet <- get.variables(lprec)[seq(NbShifts*NbRegTo*NbWT*NbMonth+NbRegTo*NbWT, length.out=NbShifts)]
  310.   if(any(unmet > .Machine$double.eps^0.5)) {
  311.     unmet_df <- transferable
  312.     unmet_df[, "transferable"] <- NULL
  313.     unmet_df[, "unmet"] <- unmet
  314.     unmet_df <- reshape(transferable, direction="wide", timevar="categ", idvar="region")
  315.     rownames(unmet_df) <- unmet_df[,"region"]
  316.     unmet_df[,"region"] <- NULL
  317.     unmet_df <- as.matrix(unmet_df)
  318.     unmet_df[is.na(unmet_df)] <- 0
  319.     colnames(unmet_df) <- substring(colnames(unmet_df), 14)
  320.   } else unmet_df <- data.frame()
  321.  
  322.   return(list(transfers=transf, still_unmet=unmet_df))
  323. }
  324.  
  325. #' Solves a resource allocation problem
  326. #'
  327. #' @param workload workload data.frame (rows=regions, columns=worktype)
  328. #' @param capacity work capacity per month data.frame (columns: region, month, regutil, nonregutil, dcutil)
  329. #' @param min_workertype minimum requirements data.frame (columns=region, month, worktype, regmin, dcmin)
  330. #' @param min_worktype optional minimum requirement for work types.
  331. #' @param start_month start month: from here to Dec the workload needs to be processed (three letter month abbreviation). Default "Jan"
  332. #' @param no_work named list of worktype with the months (three letter month abbreviation) where no work on that worktype is possible
  333. #' @param max_offshore named vector with the percentages of work that can be done offshore
  334. #'
  335. #' @return a list with three elements named "effort" with the distributed workload
  336. #'        (data.frame, columns: region, month, worktype, worker type, effort),  
  337. #'        "unmet", a (hopefully all zero) matrix (columns: category, rows: regions) where capacity problems arise, and
  338. #'        (only if transfers==TRUE) "transfers" a data.frame with distributed offshore work
  339. #'        (data.frame, columns: region_from, categ, region_to, worker_type, mon, effort)
  340. ds_user_parallel <- function(
  341.   workload,
  342.   capacity,
  343.   min_workertype,
  344.   min_worktype=c(Subdivisions=1/12, HomeValue=0.05),
  345.   start_month="Jan",
  346.   no_work=list(
  347.     "Reval_Days"=c("Jan", "Feb", "Mar", "Apr", "May", "Jun"),
  348.     "Asset"=c("Sep", "Oct", "Nov", "Dec", "Jan"),
  349.     "Reval_Objections"=c("Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
  350.   ),
  351.   max_offshore=c(
  352.     Building_Consents=0.5, Subdivisions=0.9, S12_Sales=0.5, RM_Objections=0.1, Reval_Days=0.3, Reval_Objections=0.1,
  353.     Asset=0.1, HomeValue=0.2, Rural=0.2, Urgent_New_Imp=0.2
  354.   ),
  355.   transfers=TRUE
  356. ){
  357.   ### helper functions
  358.   month_map <- setNames(
  359.     rev(seq.int(12)),
  360.     c("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
  361.   )
  362.   if(!start_month %in% names(month_map)) stop("invalid start_month, must be a three letter month abbreviation.")
  363.   NbMonth <- month_map[start_month]
  364.   month_map <- tail(month_map, NbMonth)
  365.   categ_map <- unique(rownames(min_workertype))
  366.   NbCat <- length(categ_map)
  367.   categ_map <- setNames(seq.int(NbCat), categ_map)
  368.  
  369.   ### sanity check 1: consistent use of work category names
  370.   if(length(intersect(colnames(workload), names(categ_map)))<max(c(length(colnames(workload)), length(categ_map))))
  371.     stop("work categories in workload and min_req argument do not match.")
  372.   if(length(setdiff(names(no_work), names(categ_map))) > 0)
  373.     stop("work categories in no_work and min_req argument do not match.")
  374.  
  375.   worktype_map <- c(regular=1, nonregular=2, data_collector=3)
  376.   NbWT <- length(worktype_map)
  377.   Nbtot <- (NbMonth * NbCat * NbWT)
  378.  
  379.   ### sanity check 2: need more capacity than workload
  380.   capacity <- capacity[capacity[,"mon"] %in% names(month_map),]
  381.   #if(sum(workload)>sum(capacity[, c("regutil", "nonregutil", "dcutil")])) stop("workload exceeds capacity")
  382.  
  383.   ### map subscripts of the decision vars to index and vice versa
  384.   dimsToIndex <- function(month=names(month_map), categ=names(categ_map), worker_type=names(worktype_map)) {
  385.     args <- expand.grid(
  386.       month=month_map[month]-1,
  387.       categ=categ_map[categ]-1,
  388.       worker_type=worktype_map[worker_type],
  389.       stringsAsFactors=FALSE
  390.     )
  391.     w <- c(NbCat*NbWT, NbWT, 1)
  392.     ret <- apply(args, 1, function(x) {ans <- x %*% w; dim(ans) <- NULL; return(ans)})
  393.     return(ret)
  394.   }
  395.   dimsToIndex <- cmpfun(dimsToIndex)
  396.  
  397.   subset_wl <- function(reg) matrix(workload[reg,], nrow=1, dimnames=list(reg, colnames(workload)))
  398.   splitted_cap <- split(capacity, capacity[,"region"])
  399.   my_combine <- function(x, y)
  400.     list(
  401.       effort=rbind(x[["effort"]], y[["effort"]]),
  402.       unmet=rbind(x[["unmet"]], y[["unmet"]]),
  403.       transferable=rbind(x[["transferable"]], y[["transferable"]])
  404.     )
  405.   arg <- list(
  406.     month_map=month_map, NbMonth=NbMonth, categ_map=categ_map, worktype_map=worktype_map, min_workertype=min_workertype, min_worktype=min_worktype,
  407.     NbWT=NbWT, Nbtot=Nbtot, NbCat=NbCat, dimsToIndex=dimsToIndex, no_work=no_work, max_offshore=max_offshore
  408.   )
  409.   ret <- foreach(reg=names(splitted_cap), .combine=my_combine) %do% {
  410.     arg[["workload"]] <- subset_wl(reg)
  411.     arg[["capacity"]] <- splitted_cap[[reg]]
  412.     arg[["region"]] <- reg
  413.     do.call(ds_user, arg)
  414.   }
  415.  
  416.   if(nrow(ret[["transferable"]])>0)
  417.     if(transfers) {
  418.       transf_ret <- ds_user_transfer(
  419.         effort=ret[["effort"]], capacity=capacity, transferable=ret[["transferable"]], min_workertype=min_workertype,
  420.         month_map=month_map, NbMonth=NbMonth, categ_map=categ_map, worktype_map, NbWT=NbWT, NbCat, no_work=no_work
  421.       )
  422.       still_unmet <- transf_ret[["still_unmet"]]
  423.       if(nrow(still_unmet)>0)
  424.         ret[["unmet"]][rownames(still_unmet), colnames(still_unmet)] <-
  425.         ret[["unmet"]][rownames(still_unmet), colnames(still_unmet)] + still_unmet
  426.       ret[["transferable"]] <- NULL
  427.       ret[["transfers"]] <- transf_ret[["transfers"]]
  428.     } else {
  429.       # no transfers wished -- add to unmet
  430.       unmet_df <- reshape(ret[["transferable"]], direction="wide", timevar="categ", idvar="region")
  431.       rownames(unmet_df) <- unmet_df[,"region"]
  432.       unmet_df[,"region"] <- NULL
  433.       unmet_df <- as.matrix(unmet_df)
  434.       unmet_df[is.na(unmet_df)] <- 0
  435.       colnames(unmet_df) <- substring(colnames(unmet_df), 14)
  436.       ret[["unmet"]][rownames(unmet_df), colnames(unmet_df)] <- ret[["unmet"]][rownames(unmet_df), colnames(unmet_df)] + unmet_df
  437.       ret[["transferable"]] <- NULL
  438.     }
  439.  
  440.   if(any(ret[["unmet"]]>.Machine$double.eps^0.5)) warning("there is unmet ", if(transfers) "untransferable", "workload")
  441.  
  442.   # browser()
  443.   return(ret)
  444. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement