production_planner.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package spoptim
  2. import (
  3. "fmt"
  4. "math"
  5. "sort"
  6. "strconv"
  7. "encoding/json"
  8. "gem-spaas-coding-challenge/models"
  9. )
  10. var powerplantFuel = map[string]string{"gasfired": "gas(euro/MWh)", "turbojet": "kerosine(euro/MWh)", "windturbine": "wind(%)"}
  11. func sliceSum(slice []float64) float64 {
  12. tot := 0.0
  13. for _, el := range slice {
  14. tot += el
  15. }
  16. return tot
  17. }
  18. func ProductionPlanner(payload *models.Payload) []interface{} {
  19. // handle empty payload
  20. if payload == nil {
  21. return make([]interface{}, 0)
  22. }
  23. // format wind plants for consistency with other types of plants
  24. wind, _ := payload.Fuels.(map[string]interface{})["wind(%)"].(json.Number).Float64()
  25. for _, plant := range payload.Powerplants {
  26. plant_typed := plant.(map[string]interface{})
  27. if plant_typed["type"] == "windturbine" {
  28. pmax, _ := plant_typed["pmax"].(json.Number).Float64()
  29. plant_typed["pmax"] = json.Number(strconv.FormatFloat((pmax * wind / 100), 'f', 20, 64))
  30. }
  31. }
  32. payload.Fuels.(map[string]interface{})["wind(%)"] = json.Number(0)
  33. // order plants by merit
  34. sort.SliceStable(payload.Powerplants, func(i, j int) bool {
  35. planti, _ := payload.Powerplants[i].(map[string]interface{})
  36. plantj, _ := payload.Powerplants[j].(map[string]interface{})
  37. fuels, _ := payload.Fuels.(map[string]interface{})
  38. fueli, _ := fuels[powerplantFuel[planti["type"].(string)]].(json.Number).Float64()
  39. fuelj, _ := fuels[powerplantFuel[plantj["type"].(string)]].(json.Number).Float64()
  40. effi, _ := planti["efficiency"].(json.Number).Float64()
  41. effj, _ := plantj["efficiency"].(json.Number).Float64()
  42. return fueli / effi < fuelj / effj
  43. })
  44. // Naively assign production by merit-order (with one check
  45. // to replace the last plant if too big pmin)
  46. load := 0.0
  47. cost := make([]float64, 0)
  48. res := make([]interface{}, 0)
  49. costToCompare := make([]interface{}, 0)
  50. resToCompare := make([][]interface{}, 0)
  51. for _, plant := range payload.Powerplants {
  52. plant_typed := plant.(map[string]interface{})
  53. remainingLoad := *payload.Load - load
  54. if remainingLoad == 0 {
  55. break
  56. }
  57. pmin, _ := plant_typed["pmin"].(json.Number).Float64()
  58. pmax, _ := plant_typed["pmax"].(json.Number).Float64()
  59. eff, _ := plant_typed["efficiency"].(json.Number).Float64()
  60. fuel, _ := payload.Fuels.(map[string]interface{})[powerplantFuel[plant_typed["type"].(string)]].(json.Number).Float64()
  61. if pmin < remainingLoad {
  62. usedP := math.Min(remainingLoad, pmax)
  63. load += usedP
  64. cost = append(cost, usedP / eff * fuel)
  65. res = append(res, map[string]interface{}{
  66. "name": plant_typed["name"].(string),
  67. "p": usedP,
  68. })
  69. } else {
  70. altRes := make([]interface{}, 0)
  71. altCost := make([]float64, 0)
  72. copy(altRes, res)
  73. copy(altCost, cost)
  74. loadToRemove := pmin - remainingLoad
  75. for loadToRemove > 0 {
  76. if altRes[len(altRes)-1].(map[string]interface{})["p"].(float64) > loadToRemove {
  77. altCost[len(altCost)-1] = altCost[len(altCost)-1] * (1 - loadToRemove / altRes[len(altRes)-1].(map[string]interface{})["p"].(float64))
  78. altRes[len(altRes)-1].(map[string]interface{})["p"] = altRes[len(altRes)-1].(map[string]interface{})["p"].(float64) - loadToRemove
  79. loadToRemove = 0
  80. } else {
  81. loadToRemove = loadToRemove - altRes[len(altRes)-1].(map[string]interface{})["p"].(float64)
  82. altRes = altRes[:len(altRes)-1]
  83. altCost = altCost[:len(altRes)-1]
  84. }
  85. }
  86. altRes = append(altRes, map[string]interface{}{
  87. "name": plant_typed["name"].(string),
  88. "p": pmax,
  89. })
  90. altCost = append(altCost, pmax / eff * fuel)
  91. resToCompare = append(resToCompare, altRes)
  92. costToCompare = append(costToCompare, altCost)
  93. }
  94. }
  95. bestCost := sliceSum(cost)
  96. for i, altCost := range costToCompare {
  97. if sliceSum(altCost.([]float64)) < bestCost {
  98. bestCost = sliceSum(altCost.([]float64))
  99. res = resToCompare[i]
  100. }
  101. }
  102. return res
  103. }