| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- package spoptim
- import (
- "math"
- "sort"
- "encoding/json"
- "gem-spaas-coding-challenge/models"
- )
- var powerplantFuel = map[string]string{"gasfired": "gas(euro/MWh)", "turbojet": "kerosine(euro/MWh)", "windturbine": "wind(%)"}
- func sliceSum(slice []float64) float64 {
- tot := 0.0
- for _, el := range slice {
- tot += el
- }
- return tot
- }
- func ProductionPlanner(payload *models.Payload) []interface{} {
- // handle empty payload
- if payload == nil {
- return make([]interface{}, 0)
- }
- // cast the fuels to map and validate
- fuels, _ := payload.Fuels.(map[string]interface{})
- for k := range powerplantFuel {
- if _, ok := fuels[powerplantFuel[k]]; !ok {
- res := make([]interface{}, 0)
- res = append(res, powerplantFuel[k] + " missing in Fuels")
- return res
- }
- }
- // format wind plants for consistency with other types of plants
- wind, _ := fuels["wind(%)"].(json.Number).Float64()
- for _, plant := range payload.Powerplants {
- if *plant.Type == "windturbine" {
- *plant.Pmax = math.Round(*plant.Pmax * wind / 10) / 10
- }
- }
- fuels["wind(%)"] = json.Number(0)
- // order plants by merit
- sort.SliceStable(payload.Powerplants, func(i, j int) bool {
- fueli, _ := fuels[powerplantFuel[*payload.Powerplants[i].Type]].(json.Number).Float64()
- fuelj, _ := fuels[powerplantFuel[*payload.Powerplants[j].Type]].(json.Number).Float64()
- return fueli / *payload.Powerplants[i].Efficiency < fuelj / *payload.Powerplants[j].Efficiency
- })
- // Naively assign production by merit-order (with one check
- // to replace the last plant if too big pmin)
- load := 0.0
- cost := make([]float64, 0)
- res := make([]interface{}, 0)
- costToCompare := make([]interface{}, 0)
- resToCompare := make([][]interface{}, 0)
- for _, plant := range payload.Powerplants {
- remainingLoad := *payload.Load - load
- if remainingLoad == 0 {
- break
- }
- fuel, _ := fuels[powerplantFuel[*plant.Type]].(json.Number).Float64()
- if *plant.Pmin < remainingLoad {
- if *plant.Pmax != 0 {
- usedP := math.Min(remainingLoad, *plant.Pmax)
- load += usedP
- cost = append(cost, usedP / *plant.Efficiency * fuel)
- res = append(res, map[string]interface{}{
- "name": plant.Name,
- "p": usedP,
- })
- }
- } else {
- altRes := make([]interface{}, len(res))
- for i, re := range res {
- altRes[i] = map[string]interface{}{
- "name": re.(map[string]interface{})["name"],
- "p": re.(map[string]interface{})["p"],
- }
- }
- altCost := make([]float64, len(cost))
- copy(altCost, cost)
- loadToRemove := *plant.Pmin - remainingLoad
- for loadToRemove > 0 {
- if altRes[len(altRes)-1].(map[string]interface{})["p"].(float64) > loadToRemove {
- altCost[len(altCost)-1] = altCost[len(altCost)-1] * (1 - loadToRemove / altRes[len(altRes)-1].(map[string]interface{})["p"].(float64))
- altRes[len(altRes)-1].(map[string]interface{})["p"] = altRes[len(altRes)-1].(map[string]interface{})["p"].(float64) - loadToRemove
- loadToRemove = 0
- } else {
- loadToRemove = loadToRemove - altRes[len(altRes)-1].(map[string]interface{})["p"].(float64)
- altRes = altRes[:len(altRes)-1]
- altCost = altCost[:len(altRes)-1]
- }
- }
- altRes = append(altRes, map[string]interface{}{
- "name": *plant.Name,
- "p": *plant.Pmin,
- })
- altCost = append(altCost, *plant.Pmin / *plant.Efficiency * fuel)
- resToCompare = append(resToCompare, altRes)
- costToCompare = append(costToCompare, altCost)
- }
- }
- bestCost := sliceSum(cost)
- for i, altCost := range costToCompare {
- if sliceSum(altCost.([]float64)) < bestCost {
- bestCost = sliceSum(altCost.([]float64))
- res = resToCompare[i]
- }
- }
- return res
- }
|