| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- package spoptim
- import (
- "fmt"
- "math"
- "sort"
- "strconv"
- "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)
- }
- // format wind plants for consistency with other types of plants
- wind, _ := payload.Fuels.(map[string]interface{})["wind(%)"].(json.Number).Float64()
- for _, plant := range payload.Powerplants {
- plant_typed := plant.(map[string]interface{})
- if plant_typed["type"] == "windturbine" {
- pmax, _ := plant_typed["pmax"].(json.Number).Float64()
- plant_typed["pmax"] = json.Number(strconv.FormatFloat((pmax * wind / 100), 'f', 20, 64))
- }
- }
- payload.Fuels.(map[string]interface{})["wind(%)"] = json.Number(0)
- // order plants by merit
- sort.SliceStable(payload.Powerplants, func(i, j int) bool {
- planti, _ := payload.Powerplants[i].(map[string]interface{})
- plantj, _ := payload.Powerplants[j].(map[string]interface{})
- fuels, _ := payload.Fuels.(map[string]interface{})
- fueli, _ := fuels[powerplantFuel[planti["type"].(string)]].(json.Number).Float64()
- fuelj, _ := fuels[powerplantFuel[plantj["type"].(string)]].(json.Number).Float64()
- effi, _ := planti["efficiency"].(json.Number).Float64()
- effj, _ := plantj["efficiency"].(json.Number).Float64()
- return fueli / effi < fuelj / effj
- })
- // 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 {
- plant_typed := plant.(map[string]interface{})
- remainingLoad := *payload.Load - load
- if remainingLoad == 0 {
- break
- }
- pmin, _ := plant_typed["pmin"].(json.Number).Float64()
- pmax, _ := plant_typed["pmax"].(json.Number).Float64()
- eff, _ := plant_typed["efficiency"].(json.Number).Float64()
- fuel, _ := payload.Fuels.(map[string]interface{})[powerplantFuel[plant_typed["type"].(string)]].(json.Number).Float64()
- if pmin < remainingLoad {
- usedP := math.Min(remainingLoad, pmax)
- load += usedP
- cost = append(cost, usedP / eff * fuel)
- res = append(res, map[string]interface{}{
- "name": plant_typed["name"].(string),
- "p": usedP,
- })
- } else {
- altRes := make([]interface{}, 0)
- altCost := make([]float64, 0)
- copy(altRes, res)
- copy(altCost, cost)
- loadToRemove := 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_typed["name"].(string),
- "p": pmax,
- })
- altCost = append(altCost, pmax / eff * 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
- }
|