瀏覽代碼

Add gitignore and implement (naive) production plan function.

Aurelien 5 年之前
父節點
當前提交
76b1e96fee
共有 2 個文件被更改,包括 106 次插入3 次删除
  1. 1 0
      .gitignore
  2. 105 3
      spoptim/production_planner.go

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.project

+ 105 - 3
spoptim/production_planner.go

@@ -1,12 +1,114 @@
 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{} {
-    return []interface{}{map[string]interface{}{"name": "windpark1", "p": 75},
-        map[string]interface{}{"name": "windpark2", "p": 25},
-        map[string]interface{}{"name": "gasfiredbig1", "p": 200}}
+	// 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
 }