├── .gitignore ├── README.md ├── main.py └── optimizer ├── __init__.py ├── __pycache__ ├── costFunctionBuilder.cpython-37.pyc └── optimizer.cpython-37.pyc ├── optimizer.py ├── optimizerV2.py └── simulator ├── __init__.py ├── __pycache__ ├── __init__.cpython-37.pyc └── dollarCost.cpython-37.pyc ├── costs ├── __init__.py ├── __pycache__ │ └── __init__.cpython-37.pyc ├── carbon │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ └── carbonCost.cpython-37.pyc │ └── carbonCost.py └── dollars │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-37.pyc │ └── dollarCost.cpython-37.pyc │ ├── battery │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── auxilliaryCostFunctions.cpython-37.pyc │ │ └── batteryCost.cpython-37.pyc │ ├── auxilliaryCostFunctions.py │ └── batteryCost.py │ ├── dg │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── auxilliaryCostFunctions.cpython-37.pyc │ │ ├── dgCost.cpython-37.pyc │ │ └── dgCostAlt.cpython-37.pyc │ ├── auxilliaryCostFunctions.py │ ├── dgCost.py │ └── dgCostAlt.py │ ├── dollarCost.py │ ├── pv │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── auxilliaryCostFunctions.cpython-37.pyc │ │ ├── pvCost.cpython-37.pyc │ │ └── pvCostAlt.cpython-37.pyc │ ├── auxilliaryCostFunctions.py │ ├── pvCost.py │ └── pvCostAlt.py │ └── windmill │ └── windCost.py └── dispatching ├── __pycache__ ├── dispatchingLoop.cpython-37.pyc └── dispatchingStrategy.cpython-37.pyc ├── dispatchingLoop.py └── dispatchingStrategy.py /.gitignore: -------------------------------------------------------------------------------- 1 | tests -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Outline 2 | 3 | - I. Introduction 4 | - II. Quickstart 5 | - II.A. Packages 6 | - II.B. Code execution 7 | - III. Architecture 8 | - IV. Simulator 9 | - IV.A. Dispatching 10 | - IV.B. Costs 11 | - IV.B.1. Battery 12 | - IV.B.2. Diesel generator 13 | - IV.B.3. Photovoltaic pannels 14 | - IV.B.4. Wind turbines 15 | - IV.B.a) Dollar cost 16 | - IV.B.b) Carbon cost 17 | - V. Optimizer 18 | - V.A. Cost functions 19 | - V.B. Bounds and parameters 20 | - VI. Limits and extensions 21 | - VI.A. Efficiency 22 | - VI.B. Solver 23 | - VI.C. User interface 24 | - VII. Acknowledgements 25 | - VIII. License 26 | - IX. Contributors 27 | 28 | # I. Introduction 29 | 30 | In most places (in developped countries at least), people get their energy from the national grid. The energy comes from different power plants such as nuclear power plants or hydro power plants. But in many other isolated places, like islands (for instance the Ouessant Island in France), it is more relevant to implemant **a new smaller grid** to meet the power needs of the island's inhabitants. It is more cost-efficient to build a new grid than to connect the island to the national grid with submarine cables. 31 | 32 | The power needs of a small island are obviously not that of a whole region, so nuclear power plants can be discarded from the beginning, water dams as well. It usually boils down to **a few renewable energy production sites and a diesel generator** to ensure steady energy production (when the sun sets, and the wind stops to blow, mainly). 33 | 34 | However, before picking your hammer and your screw driver, you must precisely know the dimensions of your micro grid : _What is the power demand on the grid?_, _How much sun and wind ressources can we reckon with?_, _How many PVs and windmills should we build?_, _How big should our batteries be?_, etc. 35 | All these questions are subject to **optimization** : there are multile costs to optimize but mainly the **regular cost** 💸 and the **environmental cost** ⛽ captured inside the carbon dioxyde emissions. 36 | 37 | Unfailingly, this leads to a [multi-objective optimization problem](#https://en.wikipedia.org/wiki/Multi-objective_optimization) where variables are merely the sizes of each installation (how much power supplied by each of the energy production sites). 38 | 39 | Therefore, the aim of the project is to give an end-to-end program that optimizes the grid specifications for its needs. The inputs should be the _load_ (during a year, what is the power demand hour after hour), and a few _constraints_ (no more than 3 windmills, no more than one acre of PVs, you name it). The outputs of this program are mainly the values of the multiple cost-functions that have been optimized and most importantly, the **size of each power plant facility**. 40 | 41 | # II. Quickstart 42 | 43 | Start by git **cloning** the repository on your machine and then download the following packages if they are or not already in your environment. 44 | 45 | ## II.A. Packages 46 | 47 | - **numpy** 👉 the basic scientific library of Python with built-in math functions and easy array handling 48 | - **time** 👉 for the sole purpose of printing the computational times 49 | - **platypus** 👉 the multi-objective optimization library with [It's library](#https://platypus.readthedocs.io/en/latest/getting-started.html) 50 | - **matplotlib.pyplot** 👉 for graph plotting 51 | - **pandas** 👉 to manipulate dataframes, a Python object that comes in handy when we manipulate large datasets 52 | 53 | ## II.B. Code execution 54 | 55 | Too make sure everything works all right, open `main.py` in your favorite _compiler_ (Pyzo, Spyder, ...) and execute the file. You should see plots popping. They display the results of the optimization process. 56 | 57 | Then, if you want to go further and run the optimizer _with your own data_, you are more than encouraged to modify the parameters inside the `optimizerTest()` function. You should look for these variables in the code, and modify them accordingly : 58 | 59 | ```python 60 | fixedParameters = 61 | { 62 | "gridComponents": { 63 | . 64 | . 65 | . 66 | "strategy" : "LF" 67 | } 68 | 69 | constraints = 70 | { 71 | "diesel": { 72 | "upperBound": 2000, 73 | . 74 | . 75 | . "lowerBound": 0 76 | } 77 | ``` 78 | 79 | # III. Architecture 80 | 81 | Below is the architecture of our project. it is subdivided in multiple packages and modules that all lead one main function, the one that will help the end user in his task of properly designing the micro-grid. 82 | 83 | - 📑 README.md 84 | - 📑 .gitignore 85 | - 📑 main.py 86 | - 📁 optimizer 87 | - 📑 optimizer.py 88 | - 📑 optimizerV2.py 89 | - 📁 simulator 90 | - 📁 dispatching 91 | - 📑 dispatchingLoop.py 92 | - 📑 dispatchingStrategy.py 93 | - 📁 costs 94 | - 📁 dollars 95 | - 📑 dollarCost.py 96 | - 📁 battery 97 | - 📑 auxilliaryCostFunctions.py 98 | - 📑 batteryCost.py 99 | - 📁 pv 100 | - 📑 auxilliaryCostFunctions.py 101 | - 📑 pvCostAlt.py 102 | - 📑 pvCost.py 103 | - 📁 diesel 104 | - 📑 auxilliaryCostFunctions.py 105 | - 📑 dgCostAlt.py 106 | - 📑 dgCost.py 107 | - 📁 windmill 108 | - 📑 windCost.py 109 | - 📁 carbon 110 | - 📑 carbonCost.py 111 | 112 | # IV. Simulator 113 | 114 | The simulator is a simplified package that yields the various costs of the micro-grid, given the sizes of each component of the grid. For instance : 115 | 116 | > The lifespan of the project is 25 years. I know the load profile of the place over a whole year. How much would it cost (💸 and ⛽) if I had a 5 kWh battery, 2 wind turbines of 2kW each, 3kW worth of PVs and a diesel generator (dg) of 3kW ? 117 | 118 | `dollarCost.py` and `carbonCost.py` are the two Python files in which functions compute the global cost and the global carbon dioxyde emissions during the whole project. Those are the _cost functions that the optimizer will aim at minimizing_. 119 | 120 | ## IV.A. Dispatching 121 | 122 | Over a year, depending on the **sun irradiation**, or the **windspeed**, you might alternatively switch on and off the diesel generator, or even store energy in the battery to meet future demand. For instance, during summer, there is a much higher sun irradiation, more than needed to meet the power demand during the day. So thanks to the PVs and the battery, one part of the energy can be directly consumed, and the extra amount of energy can be stored in the battery. The battery will discharge at night, after the sun set. 123 | 124 | With regard to the dispatch, we can adopt _two different dispatching strategies_ : **cycle charging** and **load following**. 125 | 126 | | Load following | Cycle charging | 127 | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 128 | | When renewable energies don't suffice to provide enough energy to the grid, we might want to turn on the dg. If we follow a load following strategy, we first check if there's enough energy in the battery to complete the energy supply. If not, we turn on the dg at a pace such that it **precisely meets the load**. | The case-study is exactly the same as the previous one, but this time, instead of turning on the dg at a lower pace, we turn it on at rate such that it both meets the load and the power needed to fully charge the battery. | 129 | 130 | Whether we chose one strategy or the other, the dispatching algorithm will compute the amount of **energy stored in the battery** at each time step as well as the **functioning power of the dg** at each time step. It directly impacts the amount of _fuel consumed_ as well as the _lifetime of the battery_ for instance. 131 | 132 | ## IV.B. Costs 133 | 134 | #### IV.B.1. Battery 135 | 136 | The cost of the battery is calculated with the following formula : 137 | 138 | ```latex 139 | total cost = investment cost + (replacement cost - salvage cost) + operating cost 140 | ``` 141 | 142 | where : 143 | 144 | - The investment cost is basically the price of your batteries 145 | - The replacement cost is the price of the batteries multiplied by the number of times you should replace them over the whole project's lifespan 146 | where : 147 | - The salvage cost is the price at which you could expect to sell your batteries at the end of the project, considering their remaining lifetime 148 | - The operating cost is the price you pay for your workforce to operate and maintain the batteries 149 | 150 | The battery cost relies on the dispatch described above. Indeed, the dispatch heavily impacts the battery throughput (how much energy flows through the battery) and therefore its **lifetime** and consequently the number of replacements and therefore : **the cost**. 151 | 152 | #### IV.B.2. Diesel generator 153 | 154 | The cost of the Diesel generator is calculated with the following formula : 155 | 156 | ```latex 157 | total cost = capital cost + (replacement cost - salvage cost) + operation & maintenance cost + fuel cost 158 | ``` 159 | 160 | where : 161 | 162 | - The capital cost is the inital purchase price of the diesel generator 163 | - The replacement cost is the cost of replacing the generator at the end of its lifetime 164 | - The operational and maintenance cost is the annual cost of operating and maintaining the generator 165 | - The salvage cost is the price at which you could expect to sell your diesel generator at the end of the project, considering their remaining lifetime 166 | - The fuel cost is the price of fuel consumption according to the market price of diesel fuel. 167 | 168 | Just like for the battery, the diesel generator cost is **heavily impacted by the dispatch**. Indeed, depending on the strategy and the dispatching result, we can't reckon with the same number of **working hours** for the dg for instance. If the number of working hours is not the same, the lifetime is not the same and therefore the number of replacements is not the same. _The more replacements, the more expensive_. 169 | 170 | #### IV.B.3. Photo Voltaic Panels 171 | 172 | The cost of the PV is calculated with the following formula : 173 | 174 | ```latex 175 | total cost = capital cost + (replacement cost - salvage cost) + operation & maintenance cost 176 | ``` 177 | 178 | where : 179 | 180 | - The capital cost is the inital purchase price of the PV 181 | - The replacement cost is the cost of replacing the PV at the end of its lifetime 182 | - The operational and maintenance cost is the annual cost of operating and maintaining the PV 183 | - The salvage cost is the price at which you could expect to sell your PV at the end of the project, considering their remaining lifetime 184 | 185 | #### IV.B.4. Wind turbines 186 | 187 | The cost of the Wind Turbine is calculated with the following formula : 188 | 189 | ```latex 190 | total cost = capital cost + (replacement cost - salvage cost) + operation & maintenance cost 191 | ``` 192 | 193 | where : 194 | 195 | - The capital cost is the inital purchase price of the Wind Turbine 196 | - The replacement cost is the cost of replacing the Wind Turbine at the end of its lifetime 197 | - The operational and maintenance cost is the annual cost of operating and maintaining the Wind Turbine 198 | - The salvage cost is the price at which you could expect to sell your Wind Turbine at the end of the project, considering their remaining lifetime 199 | 200 | ### IV.B.a) Total cost 💸 201 | 202 | Ultimately, thanks to the 4 previous cost functions, we can compute the total cost of the project in dollars. This will be the first cost function that the optimizer will try to minimize. 203 | 204 | ### IV.B.b) CO2 emissions ♻️ 205 | 206 | The inputs into `costCarbon.py` module would be the same as the inputs into `dollarsCost.py` module. The output of the costCarbon.emissionCO2() function would be the average emission of CO2 (kgCO2e/h) across the entire project lifespan. 207 | 208 | The value of the output (kgCO2e/h) depends on: 209 | 210 | - The size of the generator 211 | - The storage capacity of the battery 212 | - The nominal power rating of the PV 213 | - The dispatch strategy being used 214 | 215 | The fixed parameters used in the function are: 216 | 217 | - CO2 emission per litre of diesel fuel consumed = 2.65 kgCO2e/L 218 | - Upstream factor for diesel generator = 1.2 (The upstream factor is taken into account for the entire lifecyle carbon footprint of the diesel generator) 219 | - CO2 emission per manufacturing capacity of battery = 0.72 kgCO₂e/kWh 220 | - CO2 emission footprint of PV = 6gCO2e/kWh 221 | 222 | The carbon cost function will be the second function that our optimizer will aim at minimizing. 223 | 224 | # V. Optimizer 225 | 226 | The optimizer is based on pre-existing python modules. Given a set of cost functions to minimize, the optimizer returns the sizes of each component in order to minimize the cost functions in the mean time. For instance : 227 | 228 | > I want to minimize the cost and the carbon dioxyde emissions. I have a limited number of disposable pVs and windmills, my dg cannot be bigger than x, but I have no limits regarding my battery. The optimizer will return the power specifications of each of the components so as to minimize the cost and the carbon dioxyde emissions. 229 | 230 | We used the **plapytus** module. It's user friendly and does not need a lot of parameters. It was the perfect "black box" for this project. 231 | 232 | The specific algorithm being used is the **NSGA-II** algorithm. It is a non-dominated sorted genetic algorithm. We tried this algorithm with different population sizes to compare the results. A graph is generated to show the set of feasible solutions resulting from the optimized variables (with both axis being the 2 objective functions to be minimized). From the graph, we are able to determine the pareto frontier, to show the possible trade-offs between the 2 objective functions. 233 | 234 | ## V.A. Cost functions 235 | 236 | The two cost functions to minimize were `dollarCost` and `carbonCost` implemented in the simulator. These functions were passed as arguments to the platypus optimizer. 237 | 238 | ## V.B. Bounds and parameters 239 | 240 | The parameters the optimizer iterated over were the **battery maximum storage capacity**, the **generator maximum output power** and the **solar pannels installed power**. Simply put: after each iteration, the optimizer changed these three values just a bit to see how it affected both the `dollarCost` and the `carbonCost`, in order to find the three values that could minimize both functions in the meantime. The optimimum is called a _Paretto Optimum_ (cf. Wikipedia on top of this document). 241 | 242 | However, as we don't have unlimited ressources, we had to add lower and upper bounds to each of three variables (we can't have negative values nor infinite values). 243 | 244 | Finally, here is how the problem definition looks : 245 | 246 | ```python 247 | problem = Problem(3, 2) 248 | problem.types[:] = [Real(constraints["battery"]["lowerBound"], constraints["battery"]["upperBound"]), Real(constraints["diesel"]["lowerBound"], constraints["diesel"]["upperBound"]), Real(constraints["photovoltaic"]["lowerBound"], constraints["photovoltaic"]["upperBound"])] 249 | problem.function = costFunction # the function returns [dollarCost, carbonCost] 250 | 251 | algorithm = NSGAII(problem) # NSGAII is the solver used to solve the optimization problem 252 | algorithm.run(1) 253 | ``` 254 | 255 | # VI. Limits and extensions 256 | 257 | ## VI.A. Efficiency 258 | 259 | Unfortunately, the whole optimization process is **computationally expensive**. Indeed, after each iteration of the plytapus optimizer, the program has to compute the result of the _dispatching strategy_ (power output of the dg and battery storage during the whole) and then to iterate over multiple and long lists to compute the various costs (`dollarCost` and `carbonCost`). 260 | 261 | With this configuration, for a 1-year simulation and a 25-year project, with 1 run of the optimizer, it takes **more than 1 minute** to compute and display the results. 262 | 263 | One solution is to use the ressources of Google to run the optimizer (see Google Collaboratory for more info). 264 | 265 | ## VI.B. One solver only 266 | 267 | We only used the **NSGAII** solver provided by plytapus whereas the library offers many more solvers. We ought to try different solvers to compare their efficience. 268 | 269 | ## VI.C. User Interface 270 | 271 | One possible extension for this project would be to code a friendly user interface with a web page served by a local server ran by Python (**Django** or **Flask** for instance). More precisely, there could be a web page coded with _html_, _css_ and _js_ where the user can enter and set the parameters for his simulation. The parameters could be sent to the local server running the Python code and would then dynamically render the results of the optimization in the web page. 272 | 273 | # VII. Acknowledgements 274 | 275 | Our work was frequently reviewed and guided by both our teachers **@CentraleSupélec**: _Nabil Sadou_ and _Pierre Haessig_. 276 | 277 | # VIII. License 278 | 279 | _This project is licensed under the terms of the CentraleSupélec license_. 280 | 281 | Reproduction and modifications are allowed as long as there is a **mention** of either of the contributors or of this repository. 282 | 283 | # IX. Contributors 284 | 285 | - **Yun Bin Choh** - _Student @ CentraleSupélec_ - [lukecyb8687](https://github.com/lukecyb8687) 286 | - **Paula Rayol** - _Student @ CentraleSupélec_ - [paularayol](https://github.com/paularayol) 287 | - **Bastien Velitchkine** - _Student @ CentraleSupélec_ - [Bassvelitchkine](https://github.com/Bassvelitchkine) 288 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 10 13:37:42 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | 8 | import os 9 | os.chdir("./optimizer") 10 | 11 | from optimizer import optimizer 12 | import time 13 | import numpy as np 14 | 15 | def optimizerTest(): 16 | """ 17 | A simple function to test the function `optimizer` 18 | """ 19 | debut = time.time() 20 | 21 | fixedParameters = { 22 | "gridComponents": { 23 | "battery" : { 24 | "initialStorage": 1., 25 | "maxInputPow": 6 * 157, 26 | "maxOutputPow": 6 * 500, 27 | "SOC_min": 0., 28 | "maxThroughput": 3000., 29 | "lifetime": 5 * 365 * 24, 30 | "capitalCost": 200, 31 | "replacementCost": 200, 32 | "operationalCost": 0.03 33 | }, 34 | "diesel" : { 35 | "fuelCost": 1.2, 36 | "fuelCostGrad": 1., 37 | "fuelCostIntercept": 0., 38 | "lifetime": 10 * 365 * 24, 39 | "capitalCost": 1000, 40 | "replacementCost": 1000, 41 | "operationalCost": 0.03, 42 | }, 43 | "photovoltaic" : { 44 | "lifetime": 25 * 365 * 24, 45 | "capitalCost": 500, 46 | "replacementCost": 500, 47 | "operationalCost": 0.03, 48 | ## PV VECTOR TO MODIFY 49 | "powerTimeVector": np.array([abs(np.sin((2 * np.pi * hour/ 48))) for hour in np.arange(0, 24 * 365)]) # We suppose that the irradiance of the pannels is a sinusoide 50 | } 51 | }, 52 | 53 | "timeStep" : 1, 54 | ## LOAD VECTOR TO MODIFY 55 | "loadVector" : np.array([abs(np.sin((2 * np.pi * hour/ 24 - (np.pi/2)))) for hour in np.arange(0, 24 * 365)]), # We model the load by a sinusoide with max demand at 6 am and 6pm 56 | "projectDuration" : 25 * 365 * 24, 57 | "discountRate": 0.0588, 58 | "strategy" : "LF" 59 | } 60 | 61 | constraints = { 62 | "diesel": 63 | { 64 | "upperBound": 2000, 65 | "lowerBound": 0, 66 | }, 67 | "battery": 68 | { 69 | "upperBound": 1000, 70 | "lowerBound": 0, 71 | }, 72 | "photovoltaic": 73 | { 74 | "upperBound": 100, 75 | "lowerBound": 0, 76 | }, 77 | } 78 | 79 | optimizer(fixedParameters, constraints) 80 | print("The total computation took {}s".format(time.time() - debut)) 81 | 82 | optimizerTest() -------------------------------------------------------------------------------- /optimizer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/__init__.py -------------------------------------------------------------------------------- /optimizer/__pycache__/costFunctionBuilder.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/__pycache__/costFunctionBuilder.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/__pycache__/optimizer.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/__pycache__/optimizer.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/optimizer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 7 12:58:54 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | import numpy as np 8 | from platypus import NSGAII, Problem, Real 9 | import time 10 | import matplotlib.pyplot as plt 11 | import pandas as pd 12 | 13 | from simulator.dispatching.dispatchingLoop import dispatchingLoop 14 | from simulator.costs.dollars.dollarCost import dollarCost 15 | from simulator.costs.carbon.carbonCost import carbonCost 16 | 17 | def displayResults(netLoad, pvPowerVector, battStorageVector, genPowerVector, optResults): 18 | """ 19 | INPUT: 20 | - netLoad: np.array, the net load of the grid (kW) 21 | - pvPowerVector: np.array, the power output of the pannels (kW) 22 | - battStorageVector: np.array, the energy storage of the battery at each time step 23 | - genPowerVector: np.array, the power output of the diesel generator (kW) 24 | - optResults: list of lists of pareto optimums (floats) as returned by platypus 25 | OUTPUT: 26 | - None 27 | The function displays the time graphs of each of the vectors during the simulation duration 28 | """ 29 | maxLength = 100 30 | fig = plt.figure(figsize = (18, 10)) 31 | 32 | plt.subplot(221) 33 | plt.plot(np.arange(min([len(netLoad), maxLength])), netLoad[:min([len(netLoad), maxLength])], color = (78/255, 78/255, 78/255), label = "The net load over time") 34 | plt.xlabel("Time steps") 35 | plt.ylabel("The net load (kW)") 36 | plt.legend() 37 | 38 | plt.subplot(222) 39 | plt.plot(np.arange(min([len(pvPowerVector), maxLength])), pvPowerVector[:min([len(pvPowerVector), maxLength])], color = (1, 145/255, 1), label = "The solar pannels output over time") 40 | plt.xlabel("Time steps") 41 | plt.ylabel("Power output of PVs (kW)") 42 | plt.legend() 43 | 44 | plt.subplot(223) 45 | plt.plot(np.arange(min([len(battStorageVector), maxLength])), battStorageVector[:min([len(battStorageVector), maxLength])], color = (64/255, 128/255, 128/255), label = "The energy storage of the battery over time") 46 | plt.xlabel("Time steps") 47 | plt.ylabel("Energy storage of the battery (kWh)") 48 | plt.legend() 49 | 50 | plt.subplot(224) 51 | plt.plot(np.arange(min([len(genPowerVector), maxLength])), genPowerVector[:min([len(genPowerVector), maxLength])], color = (255/255, 164/255, 72/255), label = "The dg power output over time") 52 | plt.xlabel("Time steps") 53 | plt.ylabel("DG power output (kW)") 54 | plt.legend() 55 | 56 | fig = plt.figure(figsize= (8, 8)) 57 | plt.scatter([s.objectives[0] for s in optResults], [s.objectives[1] for s in optResults], alpha = 0.8, color = "purple", label = "Optimization") 58 | plt.xlabel("dollarCost ($)") 59 | plt.ylabel("carbonCost (kg CO2e)") 60 | 61 | def optimizer(fixedParameters, constraints): 62 | """ 63 | - fixedParameters: {gridComponents : { 64 | "battery" : { 65 | "initialStorage": float between 0 and 1, the battery initial energy storage as a percentage of its maximum capacity, 66 | "maxInputPow": float, the maximum charging power of the battery in kW for a 1kWh battery. The real value will be obtained by multiplying by the maximum storage capacity, 67 | "maxOutputPow": float, the maximum discharging power of the battery in kW for a 1kWh battery. The real value will be obtained by multiplying by the maximum storage capacity, 68 | "SOC_min": float, the minimum amount of energy that can be stored in the battery as a percentage of the maximum storage capacity, 69 | "maxThroughput": float, the number by which we multiply the max storage to get the maximum amount of energy that can flow in and out of the battery during its lifetime (kWh), 70 | "lifetime": int, the nominal lifetime of the battery in hours. It's the time after which we must replace it if we did not exceed the maximum throughput, 71 | "capitalCost": float, the cost of a 1kWh battery in $, 72 | "replacementCost": float, the cost to replace 1kWh of batteries in $, 73 | "operationalCost": float, the cost PER HOUR, to operate and maintain the battery ($) 74 | }, 75 | "diesel" : { 76 | "fuelCost": float, the cost of 1l of fuel ($), 77 | "fuelCostGrad": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the slope of the model curve, 78 | "fuelCostIntercept": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the intercept of the model curve, 79 | "lifetime": int, the nominal lifetime of the dg in hours, 80 | "capitalCost": float, the cost of the dg in $, 81 | "replacementCost": float, the cost to replace the dg in $, 82 | "operationalCost": float, the cost PER HOUR, to operate and maintain the dg, 83 | }, 84 | "photovoltaic" : { 85 | "lifetime": int, the nominal lifetime of a pannel in hours, 86 | "capitalCost": float, the cost of a pannel in $, 87 | "replacementCost": float, the cost to replace a pannel in $, 88 | "operationalCost": float, the cost PER HOUR, to operate and maintain the pannel ($), 89 | "powerTimeVector": numpy array, the power output of the pannel at each time step (kW) 90 | } 91 | }, 92 | 93 | timeStep : float, the time step of the simulation that generated the load in hours, 94 | loadVector : numpy array of floats, the power demand on the grid (kW), 95 | projectDuration : int, the duration of the whole project in hours (e.g 25 * 365 * 24), 96 | discountRate: float, the discount ratio, 97 | strategy : "LF" or "CC", respectively for "Load Following" or "Cycle Charging" 98 | - constraints : { 99 | "diesel": 100 | { 101 | "upperBound": float, the upper Bound for the value of the generatorMaximumPower (kW), 102 | "lowerBound": float, the lower Bound for the value of the generatorMaximumPower (kW), 103 | }, 104 | "battery": 105 | { 106 | "upperBound": float, the upper Bound for the value of the battery maximum storage capacity (kWh), 107 | "lowerBound": float, the lower Bound for the value of the battery maximum storage capacity (kWh), 108 | }, 109 | "photovoltaic": 110 | { 111 | "upperBound": float, the upper Bound for the value of the pvMaximumPower (kW), 112 | "lowerBound": float, the upperlower Bound for the value of the pvMaximumPower (kW), 113 | }, 114 | } 115 | 116 | OUTPUT: 117 | { 118 | "parameters": 119 | { 120 | "battery": float, the battery storage capacity (kWh), 121 | "diesel": float, the generator maximum power (kW), 122 | "photovoltaic": float, the pv maximum power (kW) 123 | }, 124 | "costs": 125 | { 126 | "dollars": float, the cost of the project in dollars, 127 | "carbon": float, the carbon emissions generated by the project (kg) 128 | } 129 | 130 | } 131 | """ 132 | 133 | gridComponents = fixedParameters["gridComponents"] 134 | timeStep = fixedParameters["timeStep"] 135 | loadVector = fixedParameters["loadVector"] 136 | projectDuration = fixedParameters["projectDuration"] 137 | discountRate = fixedParameters["discountRate"] 138 | strategy = fixedParameters["strategy"] 139 | pvPowerTimeVector = gridComponents["photovoltaic"]["powerTimeVector"] 140 | normalizingFactor = 52 141 | maxInputPowMulti = gridComponents["battery"]["maxInputPow"] 142 | maxOutputPowMulti = gridComponents["battery"]["maxOutputPow"] 143 | SOC_min_multi = gridComponents["battery"]["SOC_min"] 144 | 145 | def costFunction(x): 146 | global dispatchingResult, netLoadVector, pvPowerVector 147 | gridComponents["battery"]["maxStorage"] = x[0] 148 | gridComponents["diesel"]["maxPower"] = x[1] 149 | gridComponents["photovoltaic"]["maxPower"] = x[2] 150 | 151 | batteryInitialStorage = gridComponents["battery"]["initialStorage"] * x[0] 152 | battMaxInputPow = maxInputPowMulti * x[0] 153 | battMaxOutputPow = maxOutputPowMulti * x[0] 154 | SOC_min = SOC_min_multi * x[0] 155 | specifications = [x[0], x[1], battMaxInputPow, battMaxOutputPow, SOC_min] 156 | 157 | pvPowerVector = ((pvPowerTimeVector)/normalizingFactor)*(x[2]) 158 | netLoadVector = loadVector - pvPowerVector 159 | dispatchingResult = dispatchingLoop(timeStep, netLoadVector, batteryInitialStorage, specifications, strategy) 160 | 161 | return [dollarCost(gridComponents, timeStep, loadVector, projectDuration, discountRate, strategy, dispatchingResult), 162 | carbonCost(gridComponents, timeStep, loadVector, projectDuration, discountRate, strategy, dispatchingResult)] 163 | 164 | problem = Problem(3, 2) 165 | problem.types[:] = [Real(constraints["battery"]["lowerBound"], constraints["battery"]["upperBound"]), Real(constraints["diesel"]["lowerBound"], constraints["diesel"]["upperBound"]), Real(constraints["photovoltaic"]["lowerBound"], constraints["photovoltaic"]["upperBound"])] 166 | problem.function = costFunction # lambda x: [sum(x), sum([val**2 for val in x])] #The function helped us see that the computational time of our cost functions was a problem 167 | 168 | algorithm = NSGAII(problem) 169 | algorithm.run(1) 170 | 171 | displayResults(netLoadVector, pvPowerVector, dispatchingResult[0], dispatchingResult[1], [solution for solution in algorithm.result if solution.feasible]) 172 | 173 | def optimizerTest(): 174 | """ 175 | A simple function to test the function `optimizer` 176 | """ 177 | debut = time.time() 178 | 179 | df = pd.read_csv("tests/testOuessant.csv", delimiter =",") 180 | # print(df.head(3)) 181 | load = df["AC Primary Load"] 182 | pvPower = df["Pv_Output"] 183 | 184 | fixedParameters = { 185 | "gridComponents": { 186 | "battery" : { 187 | "initialStorage": 1., 188 | "maxInputPow": 6 * 157, 189 | "maxOutputPow": 6 * 500, 190 | "SOC_min": 0., 191 | "maxThroughput": 3000., 192 | "lifetime": 5 * 365 * 24, 193 | "capitalCost": 200, 194 | "replacementCost": 200, 195 | "operationalCost": 0.03 196 | }, 197 | "diesel" : { 198 | "fuelCost": 1.2, 199 | "fuelCostGrad": 1., 200 | "fuelCostIntercept": 0., 201 | "lifetime": 10 * 365 * 24, 202 | "capitalCost": 1000, 203 | "replacementCost": 1000, 204 | "operationalCost": 0.03, 205 | }, 206 | "photovoltaic" : { 207 | "lifetime": 25 * 365 * 24, 208 | "capitalCost": 500, 209 | "replacementCost": 500, 210 | "operationalCost": 0.03, 211 | "powerTimeVector": pvPower #np.array([abs(np.sin((2 * np.pi * hour/ 48))) for hour in np.arange(0, 24 * 365)]) # We suppose that the irradiance of the pannels is a sinusoide 212 | } 213 | }, 214 | 215 | "timeStep" : 1, 216 | "loadVector" : load, #np.array([abs(np.sin((2 * np.pi * hour/ 24 - (np.pi/2)))) for hour in np.arange(0, 24 * 365)]), # We model the load by a sinusoide with max demand at 6 am and 6pm 217 | "projectDuration" : 25 * 365 * 24, 218 | "discountRate": 0.0588, 219 | "strategy" : "LF" 220 | } 221 | 222 | constraints = { 223 | "diesel": 224 | { 225 | "upperBound": 2000, 226 | "lowerBound": 0, 227 | }, 228 | "battery": 229 | { 230 | "upperBound": 1000, 231 | "lowerBound": 0, 232 | }, 233 | "photovoltaic": 234 | { 235 | "upperBound": 100, 236 | "lowerBound": 0, 237 | }, 238 | } 239 | 240 | optimizer(fixedParameters, constraints) 241 | print("The total computation took {}s".format(time.time() - debut)) -------------------------------------------------------------------------------- /optimizer/optimizerV2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 7 12:58:54 2020 4 | 5 | @author: bastien velitchkine 6 | 7 | Edited on Wed Apr 8 10:46:23 2020 8 | 9 | @author: yun bin choh 10 | """ 11 | import numpy as np 12 | 13 | from platypus import NSGAII, Problem, Real 14 | import matplotlib.pyplot as plt 15 | from simulator.costs.dollars.dollarCost import dollarCost 16 | from simulator.costs.carbon.carbonCost import carbonCost 17 | 18 | def optimizer(gridComponents,constraints,timeStep,loadVector ,projectDuration ,discountRate ,strategy ): 19 | """ 20 | - gridComponents : { 21 | "battery" : { 22 | "initialStorage": float, the battery initial energy storage (kWh), 23 | "maxInputPow": float, the maximum charging power of the battery (kW), 24 | "maxOutputPow": float, the maximum discharging power of the battery (kW), 25 | "SOC_min": float, the minimum amount of energy that can be stored in the battery (kWh), 26 | "maxThroughput": float, the maximum amount of energy that can flow in and out of the battery during its lifetime (kWh), 27 | "lifetime": int, the nominal lifetime of the battery in hours. It's the time after which we must replace it if we did not exceed the maximum throughput, 28 | "capitalCost": float, the cost of a 1kWh battery in $, 29 | "replacementCost": float, the cost to replace 1kWh of batteries in $, 30 | "operationalCost": float, the cost PER HOUR, to operate and maintain the battery ($) 31 | }, 32 | "diesel" : { 33 | "fuelCost": float, the cost of 1l of fuel ($), 34 | "fuelCostGrad": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the slope of the model curve, 35 | "fuelCostIntercept": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the intercept of the model curve, 36 | "lifetime": int, the nominal lifetime of the dg in hours, 37 | "capitalCost": float, the cost of the dg in $, 38 | "replacementCost": float, the cost to replace the dg in $, 39 | "operationalCost": float, the cost PER HOUR, to operate and maintain the dg, 40 | }, 41 | "photovoltaic" : { 42 | "lifetime": int, the nominal lifetime of a pannel in hours, 43 | "capitalCost": float, the cost of a pannel in $, 44 | "replacementCost": float, the cost to replace a pannel in $, 45 | "operationalCost": float, the cost PER HOUR, to operate and maintain the pannel ($), 46 | "powerTimeVector": numpy array, the power output of the pannel at each time step (kW) 47 | } 48 | }, 49 | 50 | - timeStep : float, the time step of the simulation that generated the load in hours, 51 | - loadVector : numpy array of floats, the power demand on the grid (kW), 52 | - projectDuration : int, the duration of the whole project in hours (e.g 25 * 365 * 24), 53 | - discountRate: float, the discount ratio, 54 | - strategy : "LF" or "CC", respectively for "Load Following" or "Cycle Charging" 55 | 56 | 57 | - constraints : { 58 | "diesel": 59 | { 60 | "upperBound": float, the upper Bound for the value of the generatorMaximumPower (kW), 61 | "lowerBound": float, the lower Bound for the value of the generatorMaximumPower (kW), 62 | }, 63 | "battery": 64 | { 65 | "upperBound": float, the upper Bound for the value of the battery maximum storage capacity (kWh), 66 | "lowerBound": float, the lower Bound for the value of the battery maximum storage capacity (kWh), 67 | }, 68 | "photovoltaic": 69 | { 70 | "upperBound": float, the upper Bound for the value of the pvMaximumPower (kW), 71 | "lowerBound": float, the upperlower Bound for the value of the pvMaximumPower (kW), 72 | }, 73 | } 74 | 75 | OUTPUT: 76 | { 77 | "parameters": 78 | { 79 | "battery": float, the battery storage capacity (kWh), 80 | "diesel": float, the generator maximum power (kW), 81 | "photovoltaic": float, the pv maximum power (kW) 82 | }, 83 | "costs": 84 | { 85 | "dollars": float, the cost of the project in dollars, 86 | "carbon": float, the carbon emissions generated by the project (kg) 87 | } 88 | 89 | } 90 | """ 91 | 92 | 93 | def optimizerBuilder(x): 94 | from simulator.costs.dollars.dollarCost import dollarCost 95 | from simulator.costs.carbon.carbonCost import carbonCost 96 | gridComponents["battery"]["maxStorage"] = x[0] 97 | gridComponents["diesel"]["maxPower"] = x[1] 98 | gridComponents["photovoltaic"]["maxPower"] = x[2] 99 | 100 | 101 | return [dollarCost(gridComponents, timeStep, loadVector, projectDuration, discountRate, strategy), 102 | carbonCost(gridComponents, timeStep, loadVector, projectDuration, discountRate, strategy)] 103 | 104 | # def optimizerBuilder(x): 105 | # def f1(y): 106 | # print("We computed f1") 107 | # return sum(y) 108 | # def f2(y): 109 | # print("We computed f2") 110 | # return sum([z ** 2 for z in y]) 111 | # 112 | # return [f1(x),f2(x)] 113 | 114 | problem = Problem(3, 2) 115 | problem.types[:] = [Real(constraints["battery"]["lowerBound"], constraints["battery"]["upperBound"]), Real(constraints["diesel"]["lowerBound"], constraints["diesel"]["upperBound"]), Real(constraints["photovoltaic"]["lowerBound"], constraints["photovoltaic"]["upperBound"])] 116 | 117 | problem.function = optimizerBuilder 118 | algorithm = NSGAII(problem) 119 | algorithm.run(1) 120 | feasible_solutions = [s for s in algorithm.result if s.feasible] 121 | 122 | # Storing the results 123 | f1 = [] 124 | f2 = [] 125 | for solution in algorithm.result: 126 | print(solution.objectives) 127 | f1.append(solution.objectives[0]) 128 | f2.append(solution.objectives[1]) 129 | 130 | # Display results in a scatter plot 131 | plt.scatter(f1, 132 | f2) 133 | plt.xlim([1e5, 3e7]) 134 | plt.ylim([0, 15]) 135 | plt.xlabel("$f_1(x)$: NPC function (euros)") 136 | plt.ylabel("$f_2(x)$: Carbon emission function (kgCO2e/h)") 137 | plt.show() 138 | 139 | return f1,f2,feasible_solutions 140 | 141 | ## To test (Input variables to be modified. This section can be ignored after successful test run): 142 | #gridComponents = { 143 | # "battery" : { 144 | # "initialStorage": 892, 145 | # "maxInputPow": 6 * 167 * 892, 146 | # "maxOutputPow": 6 * 500 * 892, 147 | # "SOC_min": 0, 148 | # "maxThroughput": 3000, 149 | # "lifetime": 15*8760, 150 | # "capitalCost": 550, 151 | # "replacementCost": 550, 152 | # "operationalCost": 10 153 | # }, 154 | # "diesel" : { 155 | # "fuelCost": 1, 156 | # "fuelCostGrad": 0.2359760012, 157 | # "fuelCostIntercept": 28.5, 158 | # "lifetime": 162060, 159 | # "capitalCost": 500, 160 | # "replacementCost": 500, 161 | # "operationalCost": 0.030, 162 | # }, 163 | # "photovoltaic" : { 164 | # "lifetime": 25*8760, 165 | # "capitalCost": 2500, 166 | # "replacementCost": 2500, 167 | # "operationalCost": 10, 168 | # "powerTimeVector": np.array([abs(np.sin((2 * np.pi * hour/ 48))) for hour in np.arange(0, 24 * 365)]) # We suppose that the irradiance of the pannels is a sinusoide 169 | # } 170 | # } 171 | # 172 | #timeStep = 1 173 | #loadVector = np.array([abs(np.sin((2 * np.pi * hour/ 24 - (np.pi/2)))) for hour in np.arange(24 * 365)]), # We model the load by a sinusoide with max demand at 6 am and 6pm 174 | #projectDuration = 25*8760 175 | #discountRate = 5/100 176 | #strategy = "LF" 177 | # 178 | #constraints = { 179 | # "diesel": 180 | # { 181 | # "upperBound": 2000, 182 | # "lowerBound": 0, 183 | # }, 184 | # "battery": 185 | # { 186 | # "upperBound": 1000, 187 | # "lowerBound": 0, 188 | # }, 189 | # "photovoltaic": 190 | # { 191 | # "upperBound": 10, 192 | # "lowerBound": 0, 193 | # }, 194 | # } 195 | ## %%time 196 | #results = optimizer(gridComponents,constraints,timeStep,loadVector,projectDuration ,discountRate ,strategy)[2] 197 | # 198 | 199 | def optimizerTest(): 200 | """ 201 | A simple function to test the function `optimizer` 202 | """ 203 | 204 | gridComponents = { 205 | "battery" : { 206 | "initialStorage": 1., 207 | "maxInputPow": 6 * 157, 208 | "maxOutputPow": 6 * 500, 209 | "SOC_min": 0., 210 | "maxThroughput": 3000., 211 | "lifetime": 5 * 365 * 24, 212 | "capitalCost": 200, 213 | "replacementCost": 200, 214 | "operationalCost": 0.03 215 | }, 216 | "diesel" : { 217 | "fuelCost": 1.2, 218 | "fuelCostGrad": 1., 219 | "fuelCostIntercept": 0., 220 | "lifetime": 10 * 365 * 24, 221 | "capitalCost": 1000, 222 | "replacementCost": 1000, 223 | "operationalCost": 0.03, 224 | }, 225 | "photovoltaic" : { 226 | "lifetime": 25 * 365 * 24, 227 | "capitalCost": 500, 228 | "replacementCost": 500, 229 | "operationalCost": 0.03, 230 | "powerTimeVector": np.array([abs(np.sin((2 * np.pi * hour/ 48))) for hour in np.arange(0, 24 * 365)]) # We suppose that the irradiance of the pannels is a sinusoide 231 | } 232 | } 233 | # gridComponents = { 234 | # "battery" : { 235 | # "initialStorage": 892, 236 | # "maxInputPow": 6 * 167 * 892, 237 | # "maxOutputPow": 6 * 500 * 892, 238 | # "SOC_min": 0, 239 | # "maxThroughput": 3000, 240 | # "lifetime": 15*8760, 241 | # "capitalCost": 550, 242 | # "replacementCost": 550, 243 | # "operationalCost": 10 244 | # }, 245 | # "diesel" : { 246 | # "fuelCost": 1, 247 | # "fuelCostGrad": 0.2359760012, 248 | # "fuelCostIntercept": 28.5, 249 | # "lifetime": 162060, 250 | # "capitalCost": 500, 251 | # "replacementCost": 500, 252 | # "operationalCost": 0.030, 253 | # }, 254 | # "photovoltaic" : { 255 | # "lifetime": 25*8760, 256 | # "capitalCost": 2500, 257 | # "replacementCost": 2500, 258 | # "operationalCost": 10, 259 | # "powerTimeVector": np.array([abs(np.sin((2 * np.pi * hour/ 48))) for hour in np.arange(0, 24 * 365)]) # We suppose that the irradiance of the pannels is a sinusoide 260 | # } 261 | # } 262 | 263 | timeStep = 1 264 | loadVector = np.array([abs(np.sin((2 * np.pi * hour/ 24 - (np.pi/2)))) for hour in np.arange(0, 24 * 365)]) # We model the load by a sinusoide with max demand at 6 am and 6pm 265 | projectDuration = 25 * 365 * 24 266 | discountRate = 0.0588 267 | strategy = "LF" 268 | 269 | constraints = { 270 | "diesel": 271 | { 272 | "upperBound": 100, 273 | "lowerBound": 0, 274 | }, 275 | "battery": 276 | { 277 | "upperBound": 100, 278 | "lowerBound": 0, 279 | }, 280 | "photovoltaic": 281 | { 282 | "upperBound": 100, 283 | "lowerBound": 0, 284 | } 285 | } 286 | 287 | results = optimizer(gridComponents,constraints,timeStep,loadVector,projectDuration ,discountRate ,strategy)[2] 288 | print(results) -------------------------------------------------------------------------------- /optimizer/simulator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/__init__.py -------------------------------------------------------------------------------- /optimizer/simulator/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/__pycache__/dollarCost.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/__pycache__/dollarCost.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/__init__.py -------------------------------------------------------------------------------- /optimizer/simulator/costs/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/carbon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/carbon/__init__.py -------------------------------------------------------------------------------- /optimizer/simulator/costs/carbon/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/carbon/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/carbon/__pycache__/carbonCost.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/carbon/__pycache__/carbonCost.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/carbon/carbonCost.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 7 12:30:42 2020 4 | 5 | @author: yun bin choh 6 | """ 7 | 8 | import numpy as np 9 | import time 10 | 11 | def carbonCost(gridComponents, timeStep, loadVector, projectDuration, discountRate, strategy, dispatchingResult): 12 | """ 13 | INPUTS : 14 | - gridComponents : 15 | { 16 | "battery" : { 17 | "maxStorage": float, the battery capacity storage (kWh), 18 | "initialStorage": float between 0 and 1, the battery initial energy storage as a percentage of the maximum storage capacity, 19 | "maxInputPow": float, the maximum charging power of the battery in kW for a 1kWh battery. The real value will be obtained by multiplying by the maximum storage capacity, 20 | "maxOutputPow": float, the maximum discharging power of the battery in kW for a 1kWh battery. The real value will be obtained by multiplying by the maximum storage capacity, 21 | "SOC_min": float, the minimum amount of energy that can be stored in the battery as a percentage of the maximum storage capacity, 22 | "maxThroughput": float, the maximum amount of energy that can flow in and out of the battery during its lifetime (kWh), 23 | "lifetime": int, the nominal lifetime of the battery in hours. It's the time after which we must replace it if we did not exceed the maximum throughput, 24 | "capitalCost": float, the cost of a 1kWh battery in $, 25 | "replacementCost": float, the cost to replace 1kWh of batteries in $, 26 | "operationalCost": float, the cost PER HOUR, to operate and maintain the battery ($) 27 | }, 28 | "diesel" : { 29 | "maxPower": float, the generator nominal power (kW), 30 | "fuelCost": float, the cost of 1l of fuel ($), 31 | "fuelCostGrad": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the slope of the model curve, 32 | "fuelCostIntercept": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the intercept of the model curve, 33 | "lifetime": int, the nominal lifetime of the dg in hours, 34 | "capitalCost": float, the cost of the dg in $, 35 | "replacementCost": float, the cost to replace the dg in $, 36 | "operationalCost": float, the cost PER HOUR, to operate and maintain the dg, 37 | }, 38 | "photovoltaic" : { 39 | "maxPower": float, the nominal power of a pannel (kWP), 40 | "lifetime": int, the nominal lifetime of a pannel in hours, 41 | "capitalCost": float, the cost of a pannel in $, 42 | "replacementCost": float, the cost to replace a pannel in $, 43 | "operationalCost": float, the cost PER HOUR, to operate and maintain the pannel ($), 44 | "powerTimeVector": numpy array, the power output of the pannel at each time step (kW) 45 | } 46 | } 47 | - timeStep : float, the time step of the simulation that generated the load in hours 48 | - loadVector : numpy array of floats, the power demand on the grid (kW) 49 | - projectDuration : int, the duration of the whole project in hours (e.g 25 * 365 * 24) 50 | - discountRate: float, the discount ratio 51 | - strategy : "LF" or "CC", respectively for "Load Following" or "Cycle Charging" 52 | - dispatchingResult : the output of the function dispatchingLoop, type help(dispatchingLoop) for more info 53 | OUTPUTS : 54 | - totalEmissionCO2hourly : float, the average emission of CO2 across the entire project lifespan by Diesel Generator + PV + Battery in (kgCO2eq/h) 55 | """ 56 | # debut = time.time() 57 | 58 | # # First, we need to compute the netLoadVetor from the loadVector and the power generated by renewables 59 | # normalizingFactor = 52 60 | # pvPowerVector = ((gridComponents["photovoltaic"]["powerTimeVector"])/normalizingFactor)*(gridComponents["photovoltaic"]["maxPower"]) 61 | # netLoadVector = loadVector - pvPowerVector 62 | 63 | # # Now we need to run the dispatching loop 64 | # battMaxInputPow = gridComponents["battery"]["maxInputPow"] * gridComponents["battery"]["maxStorage"] 65 | # battMaxOutputPow = gridComponents["battery"]["maxOutputPow"] * gridComponents["battery"]["maxStorage"] 66 | # SOC_min = gridComponents["battery"]["SOC_min"] * gridComponents["battery"]["maxStorage"] 67 | 68 | # specifications = [gridComponents["battery"]["maxStorage"], gridComponents["diesel"]["maxPower"], battMaxInputPow, battMaxOutputPow, SOC_min] 69 | # batteryInitialStorage = gridComponents["battery"]["initialStorage"] * gridComponents["battery"]["maxStorage"] 70 | # dispatchingResult = dispatchingLoop(timeStep, netLoadVector, batteryInitialStorage, specifications, strategy) 71 | 72 | # Now we extract the battery storage at each time step as well as the power of the dg at each time step 73 | generatorPowerVector = dispatchingResult[1] 74 | # batteryStorageVector, generatorPowerVector = dispatchingResult[0], dispatchingResult[1] 75 | # batteryPowerVector = [energy/timeStep for energy in batteryStorageVector] 76 | 77 | # Emission (kg) per litre of diesel fuel consumed 78 | co2PerLitre = 2.65 #kg/L 79 | generatorPowerVector = np.tile(generatorPowerVector, projectDuration//(len(generatorPowerVector) * timeStep)) 80 | 81 | # Now we calculate the hourly emission of CO2 in kg by the Diesel Generator 82 | N_years = projectDuration//8760 83 | dgMaxPower = gridComponents["diesel"]["maxPower"] 84 | fuelCostGrad = gridComponents["diesel"]["fuelCostGrad"] 85 | fuelCostIntercept = gridComponents["diesel"]["fuelCostIntercept"] 86 | 87 | for elem in range(1,N_years+1): 88 | totalFuelConsumption = 0 89 | for i in range(8760*(elem-1) , 8760*elem): 90 | if generatorPowerVector[i] > 0.25 * dgMaxPower: 91 | fCon = fuelCostGrad * generatorPowerVector[i] + fuelCostIntercept 92 | totalFuelConsumption += fCon 93 | emiCO2Gen = co2PerLitre*totalFuelConsumption #kgCO2e 94 | 95 | # Now we multiply the emission by an upstream factor to take into account life-cycle CO2 production 96 | upstreamFactor = 1.2 97 | emiCO2Gen = emiCO2Gen * upstreamFactor #kgCO2e 98 | 99 | # Taking into account manufacturing emission for battery 100 | co2PerCapacity = 0.72 # kgCO₂/kWh 101 | emiCO2Batt = co2PerCapacity*gridComponents["battery"]["maxStorage"] 102 | 103 | # Taking into account carbon footprint of PV (6gCO2e/kWh) 104 | co2PerkWhPV = 6e-3 105 | normalizingFactor = 52 106 | pvPowerVector = ((gridComponents["photovoltaic"]["powerTimeVector"])/normalizingFactor)*(gridComponents["photovoltaic"]["maxPower"]) 107 | emiCO2Pv = sum(pvPowerVector)*8760*co2PerkWhPV 108 | 109 | # Total CO2 emission for project lifespan (kgCO2e) 110 | totalEmissionCO2 = emiCO2Gen + emiCO2Batt + emiCO2Pv 111 | 112 | # Average per hour C02 emission for entire project lifespan (kgCO2e/h) 113 | totalEmissionCO2hourly = totalEmissionCO2/projectDuration 114 | # print("carbonCost took {}s to compute".format(time.time() - debut)) 115 | return totalEmissionCO2hourly -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/__init__.py -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/__pycache__/dollarCost.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/__pycache__/dollarCost.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/battery/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/battery/__init__.py -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/battery/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/battery/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/battery/__pycache__/auxilliaryCostFunctions.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/battery/__pycache__/auxilliaryCostFunctions.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/battery/__pycache__/batteryCost.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/battery/__pycache__/batteryCost.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/battery/auxilliaryCostFunctions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 3 14:51:52 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | 8 | import numpy as np 9 | 10 | def operatingCost(costPerHour, discountRate, lifespan): 11 | """The function takes the cost to operate the battery for one kW and for one hour, the discount 12 | rate and the lifespan of the whole project. It returns the total operating cost (discounted). We suppose that the battery is working 13 | at all times""" 14 | nbWorkHoursYear = 24 * 365 15 | # we suppose that the number of working hours is uniformously scattered on the whole lifespan of the project, on a year scale 16 | discountedCost = np.sum([ 17 | (costPerHour * nbWorkHoursYear) / ((1 + discountRate)**i) 18 | for i in range(lifespan//(365 * 24)) 19 | ]) 20 | return discountedCost 21 | 22 | def batteryThroughput(powerTimeVector, timeStep): 23 | """The function takes the vector of power coming in or out of the battery at each time step in kW as well as the timeStep of the 24 | simulation in hours""" 25 | throughput = 0 26 | for power in powerTimeVector: 27 | throughput += abs(power) * timeStep 28 | return throughput 29 | 30 | def batteryLifeTime(powerTimeVector, timeStep, maxThroughput, batteryMaxLife): 31 | """The function takes the vector of power running through the battery at each time step, the time step, 32 | the maximum amount of energy allowed to run through the battery as well as the expected life time of the battery. 33 | The function returns the actual time span after which we must replace the battery """ 34 | throughput = batteryThroughput(powerTimeVector, timeStep) 35 | simulationDuration = len(powerTimeVector) * timeStep 36 | throughputExpectancy = (maxThroughput / throughput) * simulationDuration 37 | replacementTime = min(throughputExpectancy, batteryMaxLife) 38 | return replacementTime 39 | 40 | def totalReplacementCost(replacementTime, lifespan, replacementCost, 41 | discountRate): 42 | """The function takes the time after which we must replace the battery, the lifespan of the project, 43 | the replacementCost of the machine in €/kW 44 | and the discount Rate. 45 | It returns the total and discounted replacement cost 46 | of the machine during the project (we might have to replace 47 | it several times)""" 48 | machineLifeTimeYear = replacementTime / (24 * 365) 49 | numberReplacements = int(lifespan / replacementTime) #If lifespan = 20y and machineLifeTimeYears = 6y then numberReplacements = 3 50 | temp = [ 51 | replacementCost / ((1 + discountRate) ** (i * machineLifeTimeYear)) 52 | for i in range(1, numberReplacements + 1) 53 | ] 54 | totalCost = np.sum(temp) 55 | return totalCost 56 | 57 | def remainingHours(replacementTime, lifespan): 58 | """The function takes the time after which we must replace the battery and the lifespan of the project, both in hours. 59 | Then it returns the number hours 60 | the machine could still have worked starting from the end of the project""" 61 | remainingHours = replacementTime - (lifespan % replacementTime) 62 | return remainingHours 63 | 64 | def salvageCost(hoursLeft, replacementCost, lifespan, replacementTime, discountRate): 65 | """The function takes the number of remaining hours the machine has, the replacement cost (€/kW), the lifespan of the project, the lifetime of the machine and the 66 | discount rate. It returns the discounted salvage cost""" 67 | salvageCost = (hoursLeft/replacementTime) * replacementCost / ((1 + discountRate)**(lifespan // (24 * 365))) 68 | return salvageCost -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/battery/batteryCost.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 3 14:53:02 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | 8 | from simulator.costs.dollars.battery.auxilliaryCostFunctions import batteryLifeTime, totalReplacementCost, operatingCost, remainingHours, salvageCost 9 | 10 | def batteryCost(powerTimeVector, discountRate, timeVariables, costVariables, 11 | batteryVariables): 12 | """ 13 | INPUT : 14 | - powerTimeVector : np.array of the power input/output of the battery at each time step of the simulation 15 | - discountRate : float 16 | - timeVariables : list of 17 | - timeStep : float, the time step of the simulation in hours 18 | - lifespan : int, the lifespan of the project in hours 19 | - costVariables : list of 20 | - replacementCost : float, the cost in €/kW to replace the battery 21 | - costPerHour : float, how much it costs to operate the battery per kW and per hour 22 | - investmentCost : float, investment cost of the battery 23 | - batteryVariables : list of 24 | - battMaxThroughput : int, the maximum throughput of the battery over its lifetime in kWh 25 | - batteryMaxLife : int, the lifespan of the battery in hours 26 | OUTPUT : 27 | - totalCost : float, the total discounted cost of the dg during the whole project 28 | 29 | """ 30 | # print("début") 31 | timeStep, lifespan = timeVariables[0], timeVariables[1] 32 | replacementCost, costPerHour, investmentCost = costVariables[ 33 | 0], costVariables[1], costVariables[2] 34 | battMaxThroughput, batteryMaxLife = batteryVariables[0], batteryVariables[ 35 | 1] 36 | 37 | capitalCost = investmentCost 38 | 39 | replacementTime = batteryLifeTime(powerTimeVector, timeStep, 40 | battMaxThroughput, batteryMaxLife) 41 | totReplacementCost = totalReplacementCost(replacementTime, lifespan, 42 | replacementCost, discountRate) 43 | 44 | operCost = operatingCost(costPerHour, discountRate, lifespan) 45 | 46 | hoursLeft = remainingHours(replacementTime, lifespan) 47 | salvCost = salvageCost(hoursLeft, replacementCost, lifespan, 48 | replacementTime, discountRate) 49 | 50 | totalCost = capitalCost + (totReplacementCost - salvCost) + operCost 51 | # print("The battery cost is : {}$\n".format(totalCost)) 52 | return totalCost 53 | 54 | -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/dg/__init__.py -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/dg/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/__pycache__/auxilliaryCostFunctions.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/dg/__pycache__/auxilliaryCostFunctions.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/__pycache__/dgCost.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/dg/__pycache__/dgCost.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/__pycache__/dgCostAlt.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/dg/__pycache__/dgCostAlt.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/auxilliaryCostFunctions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Apr 9 15:45:58 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | import numpy as np 8 | 9 | def workingHours(powerTimeVector, timeStep, lifespan): 10 | """The function takes the numpy array of the power output of 11 | the machine at each time step as well as the time step for 12 | the simulation and the life span of the entire project. 13 | It returns the number of working hours of the machine during 14 | the whole project time span""" 15 | wholePeriod = len(powerTimeVector) * timeStep 16 | numberTimeUnits = len([x for x in powerTimeVector if x != 0.]) 17 | numberHours = numberTimeUnits * timeStep 18 | workingHours = numberHours * (lifespan/wholePeriod) 19 | return workingHours 20 | 21 | def totalReplacementCost(nbWorkingHours, machineLifeTime, replacementCost, 22 | discountRate): 23 | """The function takes the total amount of working hours, 24 | the life time of the machine, the replacementCost of the machine in €/kW 25 | and the discount Rate. 26 | It returns the total and discounted replacement cost 27 | of the machine during the project (we might have to replace 28 | it several times)""" 29 | machineLifeTimeYear = machineLifeTime / (24 * 365) 30 | numberReplacements = int(nbWorkingHours / machineLifeTime) #If lifespan = 20y and machineLifeTimeYears = 6y then numberReplacements = 3 31 | totalCost = np.sum([ 32 | replacementCost / ((1 + discountRate) ** (i * machineLifeTimeYear)) 33 | for i in range(1, numberReplacements + 1) 34 | ]) 35 | return totalCost 36 | 37 | def operatingCost(numberWorkingHours, costPerHour, discountRate, lifespan): 38 | """The function takes the number of working hours of the machine, the cost to operate it for one kW and for one hour, the discount 39 | rate and the lifespan of the whole project. It returns the total operating cost (discounted)""" 40 | nbWorkHoursYear = (numberWorkingHours / lifespan) * 24 * 365 41 | # we suppose that the number of working hours is uniformously scattered on the whole lifespan of the project, on a year scale 42 | discountedCost = np.sum([ 43 | (costPerHour * nbWorkHoursYear) / ((1 + discountRate)**i) 44 | for i in range(lifespan//(365 * 24)) 45 | ]) 46 | return discountedCost 47 | 48 | def fuelCost(powerTimeVector, fuelCurve, fuelPrice, discountRate, lifespan, timeStep): 49 | """ 50 | INPUT : 51 | - powerTimeVector : np.array of the power output of the machine 52 | - fuelCurve : function that returns the fuel consumption of the machine (in liters of fuel) for a given functionning power and a given time 53 | - fuelPrice : float giving the price of one liter of fuel 54 | - discountRate : float 55 | - lifespan : int giving the lifespan of the project in hours 56 | - timeStep : float, time step of the simulation in hours 57 | OUTPUT : 58 | - fuelCost : float giving the discounted cost of the fuel during the whole project 59 | """ 60 | simulationDuration = len(powerTimeVector) * timeStep 61 | costVector = [fuelCurve(power, timeStep) * fuelPrice for power in powerTimeVector] 62 | # Now we still need to discount the prices 63 | # First, we need the cost vector for the whole project 64 | newVector = [] 65 | time = 0 66 | while time < lifespan: 67 | newVector += costVector 68 | time += simulationDuration * timeStep 69 | # Then, we discount the prices considering the number of years spent since the launch of the project 70 | discountedCostVect = [newVector[i]/((1 + discountRate)**((i * timeStep)//(24 * 365))) for i in range(len(newVector))] 71 | discountedCost = 0 72 | i = 0 73 | while i * timeStep < lifespan: 74 | discountedCost += discountedCostVect[i] 75 | i += 1 76 | return discountedCost 77 | 78 | def remainingHours(nbWorkingHours, machineLifeTime): 79 | """The function takes the number of working hours of the project and the machineLifeTime, both in hours, and then returns the number hours 80 | the machine could still have worked starting from the end of the project""" 81 | remainingHours = machineLifeTime - (nbWorkingHours % machineLifeTime) 82 | return remainingHours 83 | 84 | def salvageCost(hoursLeft, replacementCost, lifespan, machineLifeTime, discountRate): 85 | """The function takes the number of remaining hours the machine has, the replacement cost (€/kW) the lifespan of the project, the lifetime of the machine and the 86 | discount rate. It returns the discounted salvage cost""" 87 | # print(replacementCost, hoursLeft, lifespan, machineLifeTime, discountRate) 88 | salvageCost = (hoursLeft/machineLifeTime) * replacementCost / ((1 + discountRate)**(lifespan // (24 * 365))) 89 | return salvageCost 90 | 91 | def totalInvestmentCost(investmentCost, dgMaxPower): 92 | """ 93 | INPUT: 94 | - investmentCost: float, the investment cost for a 1kW dg in $ 95 | - dgMaxPower: float, the maximum power output of the dg in kW 96 | OUTPUT: 97 | - totalCost: float, the total investment cost of the dg 98 | """ 99 | totalCost = investmentCost * dgMaxPower 100 | return totalCost -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/dgCost.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import datetime as dt 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pandas as pd 7 | import math 8 | import requests 9 | 10 | def dgCost(Pg, 11 | N_years, 12 | genNomPower, 13 | fuelCost, 14 | dgCapitalCost, 15 | dgReplacementCost, 16 | dgOMcost, 17 | dgLifetime, 18 | discountFactor, 19 | fuelCostGrad, 20 | fuelCostIntercept): 21 | # print("debut") 22 | N = N_years*8760 # Project Lifetime in hours 23 | 24 | def dg_lifetime(): 25 | """ 26 | Output: 27 | Number of operational hours of DG across entire project lifespan 28 | Number of operational hours per year: Array 29 | """ 30 | # Number of operational hours of DG across entire project lifespan 31 | 32 | countTotal = 0 33 | for i in range(len(Pg)): # Assuming res has (8760*N) elements (Total number of hours in a year * Number of years) 34 | if Pg[i] > 0: 35 | countTotal += 1 36 | dgLifetimeTotal = (countTotal) 37 | 38 | # Number of operational hours per year: Array 39 | 40 | yearlyOpHour = [] 41 | for elem in range(1,N_years+1): 42 | countYear = 0 43 | for i in range(8760*(elem-1),8760*elem): 44 | if Pg[i] > 0: 45 | countYear += 1 46 | yearlyOpHour.append(countYear) 47 | 48 | 49 | return dgLifetimeTotal, yearlyOpHour 50 | 51 | #print("The DG Lifetime would be:", (dg_lifetime()[0]/len(Pg))*N_years, "years when the Project lifetime would be", N_years,"years.") 52 | ##print("The DG Lifetime would be:", dg_lifetime()[0], "hours when the Project lifetime would be", N,"hours.") 53 | #print("The DG Replacement number would be: ", math.ceil(N/dgLifetime)-1) 54 | dgReplacementNumber = math.ceil(N/dgLifetime)-1 55 | 56 | 57 | def discount_factor(discountFactor,N): 58 | """ 59 | Inputs: 60 | 61 | i: Real Discount Rate (%) Float 62 | N: Total Number of Project Hours 63 | 64 | Outputs: 65 | discountFactor : Array of discount factor wrt. year starting from year 0 to year N 66 | """ 67 | discount = [] 68 | for n in range(N+1): 69 | df = 1/((1+discountFactor/100)**n) 70 | discount.append(df) 71 | return discount 72 | 73 | def fuel_cost(Pg): 74 | """ 75 | Input: 76 | Generator Output Power for the entire project lifespan (kW) 77 | 78 | Output: 79 | Total nominal fuel cost for each respective year of operation 80 | """ 81 | 82 | # yearlyFuelCost = [] 83 | # for elem in range(1,N_years+1): 84 | # fuelConsumptionResult = [] #L/hr 85 | # for i in range(8760*(elem-1) , 8760*elem): 86 | # if Pg[i] > 0.25*genNomPower: 87 | # fCon = fuelCostGrad*Pg[i] + fuelCostIntercept 88 | # fuelConsumptionResult.append(fCon) 89 | # total_fuel_consumption = sum(fuelConsumptionResult) 90 | # total_fuel_cost = fuelCost * total_fuel_consumption 91 | # yearlyFuelCost.append(total_fuel_cost) 92 | 93 | ## BELOW AN ALTERNATIVE THAT COULD HELP GAIN SOME TIME 94 | yearlyFuelCost = [] 95 | for elem in range(1, N_years + 1): 96 | total_fuel_consumption = 0 #L/hr 97 | for i in range(8760 * (elem - 1) , 8760 * elem): 98 | if Pg[i] > 0.25 * genNomPower: 99 | fCon = fuelCostGrad * Pg[i] + fuelCostIntercept 100 | total_fuel_consumption += fCon 101 | total_fuel_cost = fuelCost * total_fuel_consumption 102 | yearlyFuelCost.append(total_fuel_cost) 103 | 104 | return yearlyFuelCost 105 | 106 | def dg_replacement_cost_nominal(): 107 | nomcost = [] 108 | for n in range(1, 1 + dgReplacementNumber): 109 | nomcost.append(dgReplacementCost) 110 | return nomcost 111 | 112 | def dg_replacement_cost_discountfactor(): 113 | dgReplacementCostDF = [] 114 | dgLifetime_year = dgLifetime/8760 115 | 116 | for elem in range(1,dgReplacementNumber+1): 117 | dis = 1/((1+(discountFactor/100))**(dgLifetime_year*elem)) 118 | dgReplacementCostDF.append(dis) 119 | 120 | return dgReplacementCostDF 121 | 122 | def dg_replacement_cost_discount(nomReplacementCost,replacementCostDF): 123 | costDiscount = np.multiply(nomReplacementCost, replacementCostDF) 124 | return costDiscount 125 | 126 | def dg_capital_cost(): 127 | cost = dgCapitalCost*genNomPower 128 | return cost 129 | 130 | def dg_OM_cost(): 131 | yearlyOpHour = dg_lifetime()[1] 132 | omCostYearly = [] 133 | for i in range(len(yearlyOpHour)): 134 | cost = yearlyOpHour[i]*genNomPower*dgOMcost 135 | omCostYearly.append(cost) 136 | return omCostYearly 137 | 138 | # Only appears at the end of the project cycle 139 | def dg_salvage_cost_nominal(): 140 | replacementCostDuration = math.floor(N/dgLifetime) * dgLifetime 141 | remaining_lifetime = dgLifetime - (N -replacementCostDuration) 142 | salvageValueNominal = dgReplacementCost*(remaining_lifetime/dgLifetime) 143 | return salvageValueNominal 144 | 145 | def dg_salvage_cost_discount(): 146 | dis = 1/((1+(discountFactor/100))**N_years) 147 | dgSalvageValueDiscount = dg_salvage_cost_nominal() * dis 148 | return dgSalvageValueDiscount 149 | 150 | # CAPITAL COST 151 | capitalCost = np.zeros(N_years+1) 152 | 153 | # OM COST 154 | omCost = np.zeros(N_years+1) 155 | replacementCost = np.zeros(N_years+1) 156 | 157 | # SALVAGE COST 158 | salvageCost = np.zeros(N_years+1) 159 | 160 | # FUEL COST 161 | fuelCostnom = np.zeros(N_years+1) 162 | 163 | # Yearly Generation kWh 164 | annualEnergy = np.ones(N_years+1)*8760*genNomPower 165 | # annualEnergy = [8760 * genNomPower for year in range(N_years + 2)] 166 | 167 | # CONSTRUCTING DATA TABLE 168 | data = {'Year of Operation': list(range(0,N_years+1)), 169 | 'Discount Factor': discount_factor(discountFactor,N_years), 170 | 171 | 'Capital Cost Nominal':list(capitalCost), 172 | # 'Capital Cost Nominal': (N_years + 1) * [0.], 173 | 'Replacement Cost Nominal': np.zeros(N_years+1), 174 | 'OM Cost Nominal':list(omCost), 175 | # 'OM Cost Nominal': (N_years + 1) * [0.], 176 | 'Salvage Cost Nominal':list(salvageCost), 177 | # 'Salvage Cost Nominal': (N_years + 1) * [0.], 178 | 'Fuel Cost Nominal':list(fuelCostnom), 179 | # 'Fuel Cost Nominal': (N_years + 1) * [0.], 180 | 'Annual Electricity kWh': annualEnergy, 181 | 'Total Nominal Cost': list(fuelCostnom), 182 | # 'Total Nominal Cost': (N_years + 1) * [0.] 183 | } 184 | 185 | cashFlowTable = pd.DataFrame.from_dict(data) 186 | 187 | column_list = list(cashFlowTable) 188 | column_list = column_list[2:7] 189 | column_list 190 | 191 | capitalCost[0] = -dg_capital_cost() 192 | omCost[1:N_years+1] = np.multiply(dg_OM_cost(),-1) 193 | salvageCost[N_years] = dg_salvage_cost_nominal() 194 | fuelCostnom[1:N_years+1] = np.multiply(fuel_cost(Pg),-1) 195 | 196 | 197 | cashFlowTable['Year of Operation'] = list(range(0,N_years+1)) 198 | cashFlowTable['Discount Factor'] = discount_factor(discountFactor,N_years) 199 | cashFlowTable['Capital Cost Nominal'] =list(capitalCost) 200 | cashFlowTable['Replacement Cost Nominal'] = np.zeros(N_years+1) 201 | cashFlowTable['OM Cost Nominal'] = list(omCost) 202 | cashFlowTable['Salvage Cost Nominal'] = list(salvageCost) 203 | cashFlowTable['Fuel Cost Nominal'] = list(fuelCostnom) 204 | cashFlowTable['Annual Electricity kWh'] = list(annualEnergy) 205 | cashFlowTable['Total Nominal Cost'] = cashFlowTable[column_list].sum(axis=1) 206 | 207 | # REPLACEMENT COST 208 | yearOfReplacement = [] 209 | 210 | for i in range(1,dgReplacementNumber+1): 211 | fac = dgLifetime/8760 212 | yr = math.floor(fac*i) 213 | yearOfReplacement.append(yr) 214 | 215 | a = dg_replacement_cost_nominal() 216 | b = dg_replacement_cost_discountfactor() 217 | replacementCosts = dg_replacement_cost_discount(a,b) 218 | 219 | for i in range(len(yearOfReplacement)): 220 | cashFlowTable['Replacement Cost Nominal'][yearOfReplacement[i]] = -replacementCosts[i]/cashFlowTable['Discount Factor'][i] 221 | 222 | # ADDING IN DISCOUNTED VALUES 223 | cashFlowTable['Capital Cost Discount'] = cashFlowTable['Capital Cost Nominal'] * cashFlowTable['Discount Factor'] 224 | cashFlowTable['Replacement Cost Discount'] = cashFlowTable['Replacement Cost Nominal']* cashFlowTable['Discount Factor'] 225 | cashFlowTable['OM Cost Discount'] = cashFlowTable['OM Cost Nominal'] * cashFlowTable['Discount Factor'] 226 | cashFlowTable['Salvage Cost Discount'] = cashFlowTable['Salvage Cost Nominal'] * cashFlowTable['Discount Factor'] 227 | cashFlowTable['Fuel Cost Discount'] = cashFlowTable['Fuel Cost Nominal'] * cashFlowTable['Discount Factor'] 228 | cashFlowTable['Annual Electricity kWh Discount'] = cashFlowTable['Annual Electricity kWh'] * cashFlowTable['Discount Factor'] 229 | cashFlowTable['Total Discounted Cost'] = cashFlowTable['Total Nominal Cost'] * cashFlowTable['Discount Factor'] 230 | cashFlowTable['LCOE Annual'] = abs(cashFlowTable['Total Discounted Cost']) / cashFlowTable['Annual Electricity kWh Discount'] 231 | for i in range(N_years+1): 232 | cashFlowTable['LCOE Annual'][i] = abs(sum(cashFlowTable['Total Discounted Cost'][0:i+1])) / sum(cashFlowTable['Annual Electricity kWh Discount'][0:i+1]) 233 | 234 | 235 | 236 | totalCost = abs(sum(cashFlowTable['Total Discounted Cost'])) 237 | # print("The generator cost is : {}$\n".format(totalCost)) 238 | return totalCost 239 | 240 | -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dg/dgCostAlt.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Apr 9 15:47:36 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | 8 | from simulator.costs.dollars.dg.auxilliaryCostFunctions import totalReplacementCost, totalInvestmentCost, workingHours, operatingCost, fuelCost, remainingHours, salvageCost 9 | 10 | def dgCost(powerTimeVector, dgMaxPower, discountRate, timeVariables, costVariables, fuelVariables): 11 | """ 12 | INPUT : 13 | - powerTimeVector : np.array of the power output of the DG at each time step of the simulation 14 | - dgMaxPower: float, the maximum power output of the diesel generator (kW) 15 | - discountRate : float 16 | - timeVariables : list of 17 | - timeStep : float, the time step of the simulation in hours 18 | - dgLifeSpan : int, the lifespan of the diesel generator in hours 19 | - lifespan : int, the lifespan of the project in hours 20 | - costVariables : list of 21 | - replacementCost : float, the cost in €/kW to replace the DG 22 | - costPerHour : float, how much it costs to operate the DG per kW and per hour 23 | - investmentCost : float, investment cost of the dg 24 | - fuelVariables : list of 25 | - fuelPrice : float, the price of one liter of fuel 26 | - fuelCurve : a function which given a power and working duration gives the amount of fuel consumed 27 | OUTPUT : 28 | - totalCost : float, the total discounted cost of the dg during the whole project 29 | 30 | """ 31 | timeStep, dgLifeTime, lifespan = timeVariables[0], timeVariables[1], timeVariables[2] 32 | replacementCost, costPerHour, investmentCost = costVariables[0], costVariables[1], costVariables[2] 33 | fuelPrice, fuelCurve = fuelVariables[0], fuelVariables[1] 34 | 35 | capitalCost = totalInvestmentCost(investmentCost, dgMaxPower) 36 | replacementCost = totalReplacementCost(lifespan, dgLifeTime, replacementCost, discountRate) 37 | nbWorkingHours = workingHours(powerTimeVector, timeStep, lifespan) 38 | operCost = operatingCost(nbWorkingHours, costPerHour, discountRate, lifespan) 39 | oilCost = fuelCost(powerTimeVector, fuelCurve, fuelPrice, discountRate, lifespan, timeStep) 40 | hoursLeft = remainingHours(nbWorkingHours, dgLifeTime) 41 | salvCost = salvageCost(hoursLeft, replacementCost, lifespan, dgLifeTime, discountRate) 42 | 43 | totalCost = capitalCost + (replacementCost - salvCost) + operCost + oilCost 44 | return totalCost 45 | 46 | 47 | -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/dollarCost.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 6 12:33:58 2020 4 | 5 | @author: bastien velitchkine 6 | 7 | Edited on Tues Apr 7 07:19:43 2020 8 | 9 | @author: yun bin choh 10 | """ 11 | from simulator.costs.dollars.battery.batteryCost import batteryCost 12 | from simulator.costs.dollars.dg.dgCostAlt import dgCost # dgCostAlt has a function that is 4 times faster than the one in dgCost, on average 13 | from simulator.costs.dollars.pv.pvCostAlt import pvCost # pvCostAlt much faster than pvCost as well 14 | 15 | import time 16 | 17 | def dollarCost(gridComponents, timeStep, loadVector, projectDuration, discountRate, strategy, dispatchingResult): 18 | """ 19 | INPUTS : 20 | - gridComponents : 21 | { 22 | "battery" : { 23 | "maxStorage": float, the battery capacity storage (kWh), 24 | "initialStorage": float between 0 and 1, the battery initial energy storage as a percentage of its maximum storage capacity, 25 | "maxInputPow": float, the maximum charging power of the battery in kW for a 1kWh battery. The real value will be obtained by multiplying by the maximum storage capacity, 26 | "maxOutputPow": float, the maximum discharging power of the battery in kW for a 1kWh battery. The real value will be obtained by multiplying by the maximum storage capacity, 27 | "SOC_min": float, the minimum amount of energy that can be stored in the battery as a percentage of the maximum storage capacity, 28 | "maxThroughput": float, the number by which we multiply the max storage to get the maximum amount of energy that can flow in and out of the battery during its lifetime (kWh), 29 | "lifetime": int, the nominal lifetime of the battery in hours. It's the time after which we must replace it if we did not exceed the maximum throughput, 30 | "capitalCost": float, the cost of a 1kWh battery in $, 31 | "replacementCost": float, the cost to replace 1kWh of batteries in $, 32 | "operationalCost": float, the cost PER HOUR, to operate and maintain the battery ($) 33 | }, 34 | "diesel" : { 35 | "maxPower": float, the generator nominal power (kW), 36 | "fuelCost": float, the cost of 1l of fuel ($), 37 | "fuelCostGrad": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the slope of the model curve, 38 | "fuelCostIntercept": float, there is a fuel curve modeling the relationship between the functionning power of the dg and it's consumption of liters fuel per kW. This parameter is the intercept of the model curve, 39 | "lifetime": int, the nominal lifetime of the dg in hours, 40 | "capitalCost": float, the cost of the dg in $, 41 | "replacementCost": float, the cost to replace the dg in $, 42 | "operationalCost": float, the cost PER HOUR, to operate and maintain the dg, 43 | }, 44 | "photovoltaic" : { 45 | "maxPower": float, the nominal power of a pannel (kWP), 46 | "lifetime": int, the nominal lifetime of a pannel in hours, 47 | "capitalCost": float, the cost of a pannel in $, 48 | "replacementCost": float, the cost to replace a pannel in $, 49 | "operationalCost": float, the cost PER HOUR, to operate and maintain the pannel ($), 50 | "powerTimeVector": numpy array, the power output of the pannel at each time step (kW) 51 | } 52 | } 53 | - timeStep : float, the time step of the simulation that generated the load in hours 54 | - loadVector : numpy array of floats, the power demand on the grid (kW) 55 | - projectDuration : int, the duration of the whole project in hours (e.g 25 * 365 * 24) 56 | - discountRate: float, the discount ratio 57 | - strategy : "LF" or "CC", respectively for "Load Following" or "Cycle Charging" 58 | - dispatchingResult : the output of the function dispatchingLoop, type help(dispatchingLoop) for more info 59 | OUTPUTS : 60 | - totalCost : float, the total cost of the project in $ 61 | """ 62 | # debut = time.time() 63 | 64 | # First, we need to compute the netLoadVetor from the loadVector and the power generated by renewables 65 | normalizingFactor = 52 66 | pvPowerVector = ((gridComponents["photovoltaic"]["powerTimeVector"])/normalizingFactor)*(gridComponents["photovoltaic"]["maxPower"]) 67 | # netLoadVector = loadVector - pvPowerVector 68 | 69 | # # Now we need to run the dispatching loop 70 | # battMaxInputPow = gridComponents["battery"]["maxInputPow"] * gridComponents["battery"]["maxStorage"] 71 | # battMaxOutputPow = gridComponents["battery"]["maxOutputPow"] * gridComponents["battery"]["maxStorage"] 72 | # SOC_min = gridComponents["battery"]["SOC_min"] * gridComponents["battery"]["maxStorage"] 73 | 74 | # specifications = [gridComponents["battery"]["maxStorage"], gridComponents["diesel"]["maxPower"], battMaxInputPow, battMaxOutputPow, SOC_min] 75 | # batteryInitialStorage = gridComponents["battery"]["initialStorage"] * gridComponents["battery"]["maxStorage"] 76 | # dispatchingResult = dispatchingLoop(timeStep, netLoadVector, batteryInitialStorage, specifications, strategy) 77 | 78 | # Now we extract the battery storage at each time step as well as the power of the dg at each time step 79 | batteryStorageVector, generatorPowerVector = dispatchingResult[0], dispatchingResult[1] 80 | batteryPowerVector = [energy/timeStep for energy in batteryStorageVector] 81 | 82 | # Now we have everything we need to compute each specific cost 83 | 84 | # BATTERY COST 85 | timeVariables = [timeStep, projectDuration] 86 | costVariables = [gridComponents["battery"]["replacementCost"], gridComponents["battery"]["operationalCost"], gridComponents["battery"]["capitalCost"]] 87 | batteryVariables = [gridComponents["battery"]["maxThroughput"] * gridComponents["battery"]["maxStorage"], gridComponents["battery"]["lifetime"]] 88 | battery = batteryCost(batteryPowerVector, discountRate, timeVariables, costVariables, batteryVariables) 89 | 90 | # DIESEL GENERATOR COST 91 | # generatorPowerVector = np.tile(generatorPowerVector, projectDuration//(24 * 365)) 92 | # dg = dgCost(generatorPowerVector, 93 | # projectDuration//(24 * 365), 94 | # gridComponents["diesel"]["maxPower"], 95 | # gridComponents["diesel"]["fuelCost"], 96 | # gridComponents["diesel"]["capitalCost"], 97 | # gridComponents["diesel"]["replacementCost"], 98 | # gridComponents["diesel"]["operationalCost"], 99 | # gridComponents["diesel"]["lifetime"], 100 | # discountRate*100, 101 | # gridComponents["diesel"]["fuelCostGrad"], 102 | # gridComponents["diesel"]["fuelCostIntercept"]) 103 | 104 | dgTimeVariables = [timeStep, gridComponents["diesel"]["lifetime"], projectDuration] 105 | dgCostVariables = [gridComponents["diesel"]["replacementCost"], gridComponents["diesel"]["operationalCost"], gridComponents["diesel"]["capitalCost"]] 106 | dgMaxPower = gridComponents["diesel"]["maxPower"] 107 | grad = gridComponents["diesel"]["fuelCostGrad"] 108 | intercept = gridComponents["diesel"]["fuelCostIntercept"] 109 | fuelVariables = [gridComponents["diesel"]["fuelCost"], lambda power, timeStep : timeStep * grad * power + intercept] 110 | dg = dgCost(generatorPowerVector, dgMaxPower, discountRate, dgTimeVariables, dgCostVariables, fuelVariables) 111 | 112 | # PV COST 113 | # pvPowerVector = np.tile(pvPowerVector,projectDuration//(24 * 365)) 114 | # pv = pvCost(pvPowerVector, 115 | # projectDuration//(24 * 365), 116 | # gridComponents["photovoltaic"]["maxPower"], 117 | # gridComponents["photovoltaic"]["capitalCost"], 118 | # gridComponents["photovoltaic"]["replacementCost"], 119 | # gridComponents["photovoltaic"]["operationalCost"], 120 | # gridComponents["photovoltaic"]["lifetime"], 121 | # discountRate*100) 122 | 123 | pvTimeVariables = [timeStep, gridComponents["photovoltaic"]["lifetime"], projectDuration] 124 | pvCostVariables = [gridComponents["photovoltaic"]["replacementCost"], gridComponents["photovoltaic"]["operationalCost"], gridComponents["photovoltaic"]["capitalCost"]] 125 | pvMaxPower = gridComponents["photovoltaic"]["maxPower"] 126 | pv = pvCost(pvPowerVector, pvMaxPower, discountRate, pvTimeVariables, pvCostVariables) 127 | 128 | totalCost = battery + dg + pv 129 | # print("dollarCost took {}s to compute".format(time.time() - debut))s 130 | return totalCost -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/pv/__init__.py -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/pv/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/__pycache__/auxilliaryCostFunctions.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/pv/__pycache__/auxilliaryCostFunctions.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/__pycache__/pvCost.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/pv/__pycache__/pvCost.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/__pycache__/pvCostAlt.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/costs/dollars/pv/__pycache__/pvCostAlt.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/auxilliaryCostFunctions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Apr 9 17:22:40 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | 8 | import numpy as np 9 | 10 | def workingHours(powerTimeVector, timeStep, lifespan): 11 | """The function takes the numpy array of the power output of 12 | the machine at each time step as well as the time step for 13 | the simulation and the life span of the entire project. 14 | It returns the number of working hours of the machine during 15 | the whole project time span""" 16 | wholePeriod = len(powerTimeVector) * timeStep 17 | numberTimeUnits = len([x for x in powerTimeVector if x != 0.]) 18 | numberHours = numberTimeUnits * timeStep 19 | workingHours = numberHours * (lifespan/wholePeriod) 20 | return workingHours 21 | 22 | def totalReplacementCost(nbWorkingHours, machineLifeTime, replacementCost, 23 | discountRate): 24 | """The function takes the total amount of working hours, 25 | the life time of the machine, the replacementCost of the machine in €/kW 26 | and the discount Rate. 27 | It returns the total and discounted replacement cost 28 | of the machine during the project (we might have to replace 29 | it several times)""" 30 | machineLifeTimeYear = machineLifeTime / (24 * 365) 31 | numberReplacements = int(nbWorkingHours / machineLifeTime) #If lifespan = 20y and machineLifeTimeYears = 6y then numberReplacements = 3 32 | totalCost = np.sum([ 33 | replacementCost / ((1 + discountRate) ** (i * machineLifeTimeYear)) 34 | for i in range(1, numberReplacements + 1) 35 | ]) 36 | return totalCost 37 | 38 | def remainingHours(nbWorkingHours, machineLifeTime): 39 | """The function takes the number of working hours of the project and the machineLifeTime, both in hours, and then returns the number hours 40 | the machine could still have worked starting from the end of the project""" 41 | remainingHours = machineLifeTime - (nbWorkingHours % machineLifeTime) 42 | return remainingHours 43 | 44 | def salvageCost(hoursLeft, replacementCost, lifespan, machineLifeTime, discountRate): 45 | """The function takes the number of remaining hours the machine has, the replacement cost (€/kW) the lifespan of the project, the lifetime of the machine and the 46 | discount rate. It returns the discounted salvage cost""" 47 | salvageCost = (hoursLeft/machineLifeTime) * replacementCost / ((1 + discountRate)**(lifespan // (24 * 365))) 48 | return salvageCost 49 | 50 | def totalInvestmentCost(investmentCost, pvMaxPower): 51 | """ 52 | INPUT: 53 | - investmentCost: float, the investment cost for a 1kWp pv in $ 54 | - pvMaxPower: float, the maximum power output of the solar pannels (kW) 55 | OUTPUT: 56 | - totalCost: float, the total investment cost of the pvs 57 | """ 58 | totalCost = investmentCost * pvMaxPower 59 | return totalCost 60 | 61 | def operatingCost(numberWorkingHours, costPerHour, discountRate, lifespan): 62 | """The function takes the number of working hours of the machine, the cost to operate it for one kW and for one hour, the discount 63 | rate and the lifespan of the whole project. It returns the total operating cost (discounted)""" 64 | nbWorkHoursYear = (numberWorkingHours / lifespan) * 24 * 365 65 | # we suppose that the number of working hours is uniformously scattered on the whole lifespan of the project, on a year scale 66 | discountedCost = np.sum([ 67 | (costPerHour * nbWorkHoursYear) / ((1 + discountRate)**i) 68 | for i in range(lifespan//(365 * 24)) 69 | ]) 70 | return discountedCost -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/pvCost.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import datetime as dt 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pandas as pd 7 | import math 8 | import requests 9 | 10 | def pvCost(Ppv, 11 | N_years, 12 | pvNomPower, 13 | pvCapitalCost, 14 | pvReplacementCost, 15 | pvOMcost, 16 | pvLifetime, 17 | discountFactor): 18 | 19 | N = N_years*8760 # Project Lifetime in hours 20 | 21 | def pv_lifetime(): 22 | """ 23 | Output: 24 | Number of operational hours of pv across entire project lifespan 25 | Number of operational hours per year: Array 26 | """ 27 | # Number of operational hours of pv across entire project lifespan 28 | 29 | countTotal = 0 30 | for i in range(len(Ppv)): # Assuming res has (8760*N) elements (Total number of hours in a year * Number of years) 31 | if Ppv[i] > 0: 32 | countTotal += 1 33 | pvLifetimeTotal = (countTotal) 34 | 35 | # Number of operational hours per year: Array 36 | 37 | yearlyOpHour = [] 38 | for elem in range(1,N_years+1): 39 | countYear = 0 40 | for i in range(8760*(elem-1),8760*elem): 41 | if Ppv[i] > 0: 42 | countYear += 1 43 | yearlyOpHour.append(countYear) 44 | 45 | 46 | return pvLifetimeTotal, yearlyOpHour 47 | 48 | #print("The pv Lifetime would be:", (pv_lifetime()[0]/len(Ppv))*N_years, "years when the Project lifetime would be", N_years,"years.") 49 | #print("The pv Lifetime would be:", pv_lifetime()[0], "hours when the Project lifetime would be", N,"hours.") 50 | #print("The pv Replacement number would be: ", math.ceil(N/pvLifetime)-1) 51 | pvReplacementNumber = math.ceil(N/pvLifetime)-1 52 | 53 | 54 | def discount_factor(discountFactor,N): 55 | """ 56 | Inputs: 57 | 58 | i: Real Discount Rate (%) Float 59 | N: Total Number of Project Hours 60 | 61 | Outputs: 62 | discountFactor : Array of discount factor wrt. year starting from year 0 to year N 63 | """ 64 | discount = [] 65 | for n in range(N+1): 66 | df = 1/((1+discountFactor/100)**n) 67 | discount.append(df) 68 | return discount 69 | 70 | 71 | def pv_replacement_cost_nominal(): 72 | nomcost = [] 73 | for n in range(1,1+pvReplacementNumber): 74 | nomcost.append(pvReplacementCost) 75 | return nomcost 76 | 77 | def pv_replacement_cost_discountfactor(): 78 | pvReplacementCostDF = [] 79 | pvLifetime_year = pvLifetime/8760 80 | 81 | for elem in range(1,pvReplacementNumber+1): 82 | dis = 1/((1+(discountFactor/100))**(pvLifetime_year*elem)) 83 | pvReplacementCostDF.append(dis) 84 | 85 | return pvReplacementCostDF 86 | 87 | def pv_replacement_cost_discount(nomReplacementCost,replacementCostDF): 88 | costDiscount = np.multiply(nomReplacementCost, replacementCostDF) 89 | return costDiscount 90 | 91 | def pv_capital_cost(): 92 | cost = pvCapitalCost*pvNomPower 93 | return cost 94 | 95 | def pv_OM_cost(): 96 | yearlyOpHour = pv_lifetime()[1] 97 | omCostYearly = [] 98 | for i in range(len(yearlyOpHour)): 99 | cost = yearlyOpHour[i]*pvNomPower*pvOMcost 100 | omCostYearly.append(cost) 101 | return omCostYearly 102 | 103 | # Only appears at the end of the project cycle 104 | def pv_salvage_cost_nominal(): 105 | replacementCostDuration = math.floor(N/pvLifetime) * pvLifetime 106 | remaining_lifetime = pvLifetime - (N -replacementCostDuration) 107 | salvageValueNominal = pvReplacementCost*(remaining_lifetime/pvLifetime) 108 | return salvageValueNominal 109 | 110 | def pv_salvage_cost_discount(): 111 | dis = 1/((1+(discountFactor/100))**N_years) 112 | pvSalvageValueDiscount = pv_salvage_cost_nominal() * dis 113 | return pvSalvageValueDiscount 114 | 115 | # CAPITAL COST 116 | capitalCost = np.zeros(N_years+1) 117 | 118 | # OM COST 119 | omCost = np.zeros(N_years+1) 120 | replacementCost = np.zeros(N_years+1) 121 | 122 | # SALVAGE COST 123 | salvageCost = np.zeros(N_years+1) 124 | 125 | 126 | # Yearly generation kWh 127 | annualEnergy = np.ones(N_years+1)*8760*pvNomPower 128 | 129 | # CONSTRUCTING DATA TABLE 130 | data = {'Year of Operation': list(range(0,N_years+1)), 131 | 'Discount Factor': discount_factor(discountFactor,N_years), 132 | 133 | 'Capital Cost Nominal':list(capitalCost), 134 | 'Replacement Cost Nominal':np.zeros(N_years+1), 135 | 'OM Cost Nominal':list(omCost), 136 | 'Salvage Cost Nominal':list(salvageCost), 137 | 'Annual Electricity kWh': list(annualEnergy), 138 | 'Total Nominal Cost':list(salvageCost)} 139 | 140 | cashFlowTable = pd.DataFrame.from_dict(data) 141 | 142 | column_list = list(cashFlowTable) 143 | column_list = column_list[2:6] 144 | column_list 145 | 146 | capitalCost[0] = -pv_capital_cost() 147 | omCost[1:N_years+1] = np.multiply(pv_OM_cost(),-1) 148 | salvageCost[N_years] = pv_salvage_cost_nominal() 149 | 150 | 151 | cashFlowTable['Year of Operation'] = list(range(0,N_years+1)) 152 | cashFlowTable['Discount Factor'] = discount_factor(discountFactor,N_years) 153 | cashFlowTable['Capital Cost Nominal'] =list(capitalCost) 154 | cashFlowTable['Replacement Cost Nominal'] = np.zeros(N_years+1) 155 | cashFlowTable['OM Cost Nominal'] = list(omCost) 156 | cashFlowTable['Salvage Cost Nominal'] = list(salvageCost) 157 | cashFlowTable['Annual Electricity kWh'] = list(annualEnergy) 158 | cashFlowTable['Total Nominal Cost'] = cashFlowTable[column_list].sum(axis=1) 159 | 160 | # REPLACEMENT COST 161 | yearOfReplacement = [] 162 | 163 | for i in range(1,pvReplacementNumber+1): 164 | fac = pvLifetime/8760 165 | yr = math.floor(fac*i) 166 | yearOfReplacement.append(yr) 167 | 168 | a = pv_replacement_cost_nominal() 169 | b = pv_replacement_cost_discountfactor() 170 | replacementCosts = pv_replacement_cost_discount(a,b) 171 | 172 | for i in range(len(yearOfReplacement)): 173 | cashFlowTable['Replacement Cost Nominal'][yearOfReplacement[i]] = -replacementCosts[i]/cashFlowTable['Discount Factor'][i] 174 | 175 | # ADDING IN DISCOUNTED VALUES 176 | cashFlowTable['Capital Cost Discount'] = cashFlowTable['Capital Cost Nominal'] * cashFlowTable['Discount Factor'] 177 | cashFlowTable['Replacement Cost Discount'] = cashFlowTable['Replacement Cost Nominal']* cashFlowTable['Discount Factor'] 178 | cashFlowTable['OM Cost Discount'] = cashFlowTable['OM Cost Nominal'] * cashFlowTable['Discount Factor'] 179 | cashFlowTable['Salvage Cost Discount'] = cashFlowTable['Salvage Cost Nominal'] * cashFlowTable['Discount Factor'] 180 | cashFlowTable['Annual Electricity kWh Discount'] = cashFlowTable['Annual Electricity kWh'] * cashFlowTable['Discount Factor'] 181 | cashFlowTable['Total Discounted Cost'] = cashFlowTable['Total Nominal Cost'] * cashFlowTable['Discount Factor'] 182 | cashFlowTable['LCOE Annual'] = abs(cashFlowTable['Total Discounted Cost']) / cashFlowTable['Annual Electricity kWh Discount'] 183 | for i in range(N_years+1): 184 | cashFlowTable['LCOE Annual'][i] = abs(sum(cashFlowTable['Total Discounted Cost'][0:i+1])) / sum(cashFlowTable['Annual Electricity kWh Discount'][0:i+1]) 185 | 186 | 187 | totalCost = abs(sum(cashFlowTable['Total Discounted Cost'])) 188 | # print("The pv cost is : {}$\n".format(totalCost)) 189 | return totalCost 190 | 191 | -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/pv/pvCostAlt.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Apr 9 17:24:17 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | 8 | from simulator.costs.dollars.pv.auxilliaryCostFunctions import totalInvestmentCost, totalReplacementCost, workingHours, operatingCost, remainingHours, salvageCost 9 | 10 | def pvCost(powerTimeVector, pvMaxPower, discountRate, timeVariables, costVariables): 11 | """ 12 | INPUT : 13 | - powerTimeVector : np.array of the power output of the pv at each time step of the simulation 14 | - pvMaxPower: float, the power of pv installed (kWp) 15 | - discountRate : float 16 | - timeVariables : list of 17 | - timeStep : float, the time step of the simulation in hours 18 | - pvLifeSpan : int, the lifespan of the pvs in hours 19 | - lifespan : int, the lifespan of the project in hours 20 | - costVariables : list of 21 | - replacementCost : float, the cost in €/kW to replace the pvs 22 | - costPerHour : float, how much it costs to operate the pv per kWp and per hour 23 | - investmentCost : float, investment cost of a 1 kWp pv 24 | OUTPUT : 25 | - totalCost : float, the total discounted cost of the dg during the whole project 26 | 27 | """ 28 | timeStep, pvLifeTime, lifespan = timeVariables[0], timeVariables[1], timeVariables[2] 29 | replacementCost, costPerHour, investmentCost = costVariables[0], costVariables[1], costVariables[2] 30 | 31 | capitalCost = totalInvestmentCost(investmentCost, pvMaxPower) 32 | replacementCost = totalReplacementCost(lifespan, pvLifeTime, replacementCost, discountRate) 33 | nbWorkingHours = workingHours(powerTimeVector, timeStep, lifespan) 34 | operCost = operatingCost(nbWorkingHours, costPerHour, discountRate, lifespan) 35 | hoursLeft = remainingHours(nbWorkingHours, pvLifeTime) 36 | salvCost = salvageCost(hoursLeft, replacementCost, lifespan, pvLifeTime, discountRate) 37 | 38 | totalCost = capitalCost + (replacementCost - salvCost) + operCost 39 | return totalCost 40 | 41 | -------------------------------------------------------------------------------- /optimizer/simulator/costs/dollars/windmill/windCost.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """windCost.ipynb 3 | 4 | Automatically generated by Colaboratory. 5 | 6 | Original file is located at 7 | https://colab.research.google.com/drive/1gtmPqPJ91aVp0w-Md54Fx0lGiidhxNB_ 8 | """ 9 | 10 | N_years = 25 # Project Lifetime in years 11 | N = N_years*8760 # Project Lifetime in hours 12 | 13 | wtCapitalCost=2000 14 | wtReplacementCost=2000 15 | wtOMcost=500/8760 16 | wtLifetime=25*8760 17 | number_of_turbines=2 18 | capacityWT=1500000*number_of_turbines 19 | 20 | discountFactor = 5.88 # Discount Factor (%) 21 | 22 | i = 5 # Discount Factor (%) 23 | 24 | import datetime as dt 25 | import numpy as np 26 | import matplotlib.pyplot as plt 27 | import pandas as pd 28 | import math 29 | 30 | pv_lifetime=[175200,[8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760]] 31 | wt_lifetime=[175200,[8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760,8760]] 32 | 33 | def costAnalysisWT(wt_lifetime, 34 | N_years, 35 | capacityWT, 36 | wtCapitalCost, 37 | wtReplacementCost, 38 | wtOMcost, 39 | wtLifetime, 40 | discountFactor): 41 | 42 | 43 | 44 | def discount_factor(i,N): 45 | """ 46 | Inputs: 47 | 48 | i: Real Discount Rate (%) Float 49 | N: Total Number of Project Hours 50 | 51 | Outputs: 52 | discountFactor : Array of discount factor wrt. year starting from year 0 to year N 53 | """ 54 | discount = [] 55 | for n in range(N+1): 56 | df = 1/((1+discountFactor/100)**n) 57 | discount.append(df) 58 | return discount 59 | 60 | wtReplacementNumber = math.ceil(N/wtLifetime)-1 61 | 62 | # Create an array of total replacement costs over entire project lifespan 63 | 64 | def wt_replacement_cost_nominal(): 65 | nomcost = [] 66 | for n in range(1,1+wtReplacementNumber): 67 | nomcost.append(wtReplacementCost) 68 | return nomcost 69 | 70 | def wt_replacement_cost_discountfactor(): 71 | wtReplacementCostDF = [] 72 | wtLifetime_year = wtLifetime/8760 73 | 74 | for elem in range(1,wtReplacementNumber+1): 75 | dis = 1/((1+(5/100))**(wtLifetime_year*elem)) 76 | wtReplacementCostDF.append(dis) 77 | 78 | return wtReplacementCostDF 79 | 80 | def wt_replacement_cost_discount(nomReplacementCost,replacementCostDF): 81 | costDiscount = np.multiply(nomReplacementCost, replacementCostDF) 82 | return costDiscount 83 | 84 | def wt_capital_cost(): 85 | cost = wtCapitalCost*capacityWT 86 | return cost 87 | 88 | def wt_OM_cost(): 89 | yearlyOpHour = wt_lifetime[1] 90 | omCostYearly = [] 91 | for i in range(len(yearlyOpHour)): 92 | cost = yearlyOpHour[i]*capacityWT*wtOMcost 93 | omCostYearly.append(cost) 94 | return omCostYearly 95 | 96 | np.multiply(wt_OM_cost(),-1) 97 | 98 | # Only appears at the end of the project cycle 99 | def wt_salvage_cost_nominal(): 100 | replacementCostDuration = math.floor(N/wtLifetime) * wtLifetime 101 | remaining_lifetime = wtLifetime - (N -replacementCostDuration) 102 | salvageValueNominal = wtReplacementCost*(remaining_lifetime/wtLifetime) 103 | return salvageValueNominal 104 | 105 | def wt_salvage_cost_discount(): 106 | dis = 1/((1+(5/100))**N_years) 107 | wtSalvageValueDiscount = wt_salvage_cost_nominal() * dis 108 | return wtSalvageValueDiscount 109 | 110 | 111 | # CAPITAL COST 112 | WTcapitalCost = np.zeros(N_years+1) 113 | # OM COST 114 | WTomCost = np.zeros(N_years+1) 115 | WTreplacementCost = np.zeros(N_years+1) 116 | # SALVAGE COST 117 | WTsalvageCost = np.zeros(N_years+1) 118 | 119 | # Yearly Generation kWh 120 | WTannualEnergy = np.ones(N_years+1)*8760*capacityWT 121 | 122 | # CONSTRUCTING DATA TABLE 123 | data = {'Year of Operation': list(range(0,N_years+1)), 124 | 'Discount Factor': discount_factor(discountFactor,N_years), 125 | 'Capital Cost Nominal':list(WTcapitalCost), 126 | 'Replacement Cost Nominal':np.zeros(N_years+1), 127 | 'OM Cost Nominal':list(WTomCost), 128 | 'Salvage Cost Nominal':list(WTsalvageCost), 129 | 'Annual Electricity kWh': list(WTannualEnergy)} 130 | cashFlowTable = pd.DataFrame.from_dict(data) 131 | column_list = list(cashFlowTable) 132 | column_list = column_list[2:7] 133 | column_list 134 | WTcapitalCost[0] = -wt_capital_cost() 135 | WTomCost[1:N_years+1] = np.multiply(wt_OM_cost(),-1) 136 | WTsalvageCost[N_years] = wt_salvage_cost_nominal() 137 | 138 | cashFlowTable['Year of Operation'] = list(range(0,N_years+1)) 139 | cashFlowTable['Discount Factor'] = discount_factor(discountFactor,N_years) 140 | cashFlowTable['Capital Cost Nominal'] =list(WTcapitalCost) 141 | cashFlowTable['Replacement Cost Nominal'] = np.zeros(N_years+1) 142 | cashFlowTable['OM Cost Nominal'] = list(WTomCost) 143 | cashFlowTable['Salvage Cost Nominal'] = list(WTsalvageCost) 144 | cashFlowTable['Annual Electricity kWh'] = list(WTannualEnergy) 145 | cashFlowTable['Total Nominal Cost'] = cashFlowTable[column_list].sum(axis=1) 146 | # REPLACEMENT COST 147 | WTyearOfReplacement = [] 148 | for i in range(1,wtReplacementNumber+1): 149 | fac = wtLifetime/8760 150 | yr = math.floor(fac*i) 151 | WTyearOfReplacement.append(yr) 152 | 153 | a = wt_replacement_cost_nominal() 154 | b = wt_replacement_cost_discountfactor() 155 | WTreplacementCosts = wt_replacement_cost_discount(a,b) 156 | for i in range(len(WTyearOfReplacement)): 157 | cashFlowTable['Replacement Cost Nominal'][WTyearOfReplacement[i]] = -WTreplacementCosts[i] 158 | # ADDING IN DISCOUNTED VALUES 159 | cashFlowTable['Capital Cost Discount'] = cashFlowTable['Capital Cost Nominal'] * cashFlowTable['Discount Factor'] 160 | cashFlowTable['Replacement Cost Discount'] = cashFlowTable['Replacement Cost Nominal'] 161 | cashFlowTable['OM Cost Discount'] = cashFlowTable['OM Cost Nominal'] * cashFlowTable['Discount Factor'] 162 | cashFlowTable['Salvage Cost Discount'] = cashFlowTable['Salvage Cost Nominal'] * cashFlowTable['Discount Factor'] 163 | cashFlowTable['Annual Electricity kWh Discount'] = cashFlowTable['Annual Electricity kWh'] * cashFlowTable['Discount Factor'] 164 | cashFlowTable['Total Discounted Cost'] = cashFlowTable['Total Nominal Cost'] * cashFlowTable['Discount Factor'] 165 | cashFlowTable['LCOE Annual'] = abs(cashFlowTable['Total Discounted Cost']) / cashFlowTable['Annual Electricity kWh Discount'] 166 | for i in range(N_years+1): 167 | cashFlowTable['LCOE Annual'][i] = abs(sum(cashFlowTable['Total Discounted Cost'][0:i+1])) / sum(cashFlowTable['Annual Electricity kWh Discount'][0:i+1]) 168 | 169 | 170 | return cashFlowTable 171 | 172 | costAnalysisWT(wt_lifetime, 173 | N_years, 174 | capacityWT, 175 | wtCapitalCost, 176 | wtReplacementCost, 177 | wtOMcost, 178 | wtLifetime, 179 | discountFactor) 180 | 181 | cashFlow = costAnalysisWT(wt_lifetime, 182 | N_years, 183 | capacityWT, 184 | wtCapitalCost, 185 | wtReplacementCost, 186 | wtOMcost, 187 | wtLifetime, 188 | discountFactor) 189 | npcWT = abs(sum(cashFlow['Total Discounted Cost'])) 190 | npcOutputWT = abs(sum(cashFlow['Annual Electricity kWh Discount'])) 191 | LCOE_WT = npcWT/npcOutputWT 192 | print('The net present value of total cost is: ', npcWT, 'euros') 193 | print('The net present value of total output is: ', npcOutputWT, 'kWh') 194 | print('The LCOE is: ', LCOE_WT, 'euros/kWh') 195 | 196 | -------------------------------------------------------------------------------- /optimizer/simulator/dispatching/__pycache__/dispatchingLoop.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/dispatching/__pycache__/dispatchingLoop.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/dispatching/__pycache__/dispatchingStrategy.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukecyb8687/microgrid/4bb277d3e8bad348eccca857d2bb1f492707f60f/optimizer/simulator/dispatching/__pycache__/dispatchingStrategy.cpython-37.pyc -------------------------------------------------------------------------------- /optimizer/simulator/dispatching/dispatchingLoop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 6 12:44:32 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | import numpy as np 8 | from simulator.dispatching.dispatchingStrategy import dispatchingStrategy 9 | 10 | def dispatchingLoop(timeStep, netLoad, batteryInitialStorage, specifications, strategy): 11 | """ 12 | INPUT: 13 | - timeStep : float, the time step used to compute the net load in hours 14 | - netLoadVector : numpy array, the net load at each time step 15 | - batteryInitialStorage : float, the amount of energy in the battery at the beginning in kWh 16 | - specifications : list of: 17 | - battMaxStorage : float, the storage capacity of the battery on kWh 18 | - genMaxPow : float, the maximum power the dg can deliver in kW 19 | - battMaxInputPow : float, the maximum input power of the battery in kW 20 | - battMaxOutputPow : float, the maximum output power of the battery in kW 21 | - SOC_min : the minimum storage of the battery in kWh 22 | - strategy : string, whether it's load following or cycle charging. Accepted values are "LF" and "CC". 23 | OUTPUT: 24 | list : 25 | - batteryStorageVector : numpy array, the energy stored in the battery at each timeStep 26 | - generatorPowerVector : numpy array, the functionning power of the dg at each timeStep 27 | """ 28 | batteryStorageVector = [batteryInitialStorage] 29 | generatorPowerVector = [] 30 | simulationRepetitions = len(netLoad) 31 | newStrat = "1" if strategy == "LF" else "2" 32 | for i in range(simulationRepetitions): 33 | temp = dispatchingStrategy([netLoad[i], timeStep], 34 | [batteryStorageVector[-1]], specifications, 35 | newStrat) 36 | generatorPowerVector.append(temp[0]) 37 | batteryStorageVector.append(temp[1]) 38 | return [np.array(batteryStorageVector)[1:], np.array(generatorPowerVector)] 39 | 40 | -------------------------------------------------------------------------------- /optimizer/simulator/dispatching/dispatchingStrategy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 7 12:24:20 2020 4 | 5 | @author: bastien velitchkine 6 | """ 7 | import time 8 | 9 | #def batteryManagement(batteryInputPower, battMaxInputPow, battMaxOutputPow, battMaxStorage, batteryStorage, timeStep): 10 | # """ 11 | # The function returns the amount of energy stored in the battery by the end of the time period given charge/discharge 12 | # """ 13 | # # batteryInputPower Positive (inwards flow into battery) 14 | # if batteryInputPower <= battMaxInputPow and batteryInputPower >= 0: 15 | # chargingPower = batteryInputPower 16 | # if (battMaxStorage - batteryStorage) >= chargingPower * timeStep: 17 | # newBattStorage = batteryStorage + chargingPower * timeStep 18 | # else: 19 | # newBattStorage = battMaxStorage 20 | # 21 | # # batteryInputPower Positive (inwards flow into battery) 22 | # if batteryInputPower > battMaxInputPow and batteryInputPower >= 0: 23 | # chargingPower = battMaxInputPow 24 | # if (battMaxStorage - batteryStorage) >= chargingPower * timeStep: 25 | # newBattStorage = batteryStorage + chargingPower * timeStep 26 | # else: 27 | # newBattStorage = battMaxStorage 28 | # 29 | # # batteryInputPower Positive (outwards flow into battery) 30 | # if batteryInputPower < 0 and abs(batteryInputPower) <= battMaxOutputPow: 31 | # dischargingPower = batteryInputPower 32 | # if (batteryStorage + dischargingPower*timeStep) < 0: 33 | # newBattStorage = 0 34 | # else: 35 | # newBattStorage = batteryStorage + dischargingPower*timeStep 36 | # 37 | # # batteryInputPower Positive (outwards flow into battery) 38 | # if batteryInputPower < 0 and abs(batteryInputPower) > battMaxOutputPow: 39 | # dischargingPower = -battMaxInputPow 40 | # if (batteryStorage + dischargingPower*timeStep) < 0: 41 | # newBattStorage = 0 42 | # else: 43 | # newBattStorage = batteryStorage + dischargingPower*timeStep 44 | # 45 | # return newBattStorage 46 | # 47 | #def generatorManagement(powerNeeded, newBattStorage, genMaxPow, battMaxInputPow, battMaxStorage, timeStep, strategy): 48 | # """ 49 | # The function tells us the power at which the generator should work as well as the energy stored in the battery by the end of the 50 | # time period 51 | # """ 52 | # if strategy == "1": 53 | # generatorPow = round(min(genMaxPow, powerNeeded), 3) 54 | # return [generatorPow, newBattStorage] 55 | # if strategy == "2": 56 | # chargingPow = min(battMaxInputPow, (battMaxStorage - newBattStorage)/timeStep) 57 | # generatorPow = round( 58 | # min(powerNeeded + chargingPow, genMaxPow), 3) 59 | # newBattStorage += chargingPow * timeStep 60 | # return [generatorPow, newBattStorage] 61 | 62 | def dispatchingStrategy(powerVariables, energyVariables, 63 | componentSpecifications, strategy): 64 | """ 65 | INPUT : 66 | powerVariables : 67 | list: 68 | - netLoad (the power demand on the grid, float) 69 | - timeStep (the timeStep considered, float) --> it will help us switching from power to energyVariablesVariablesVariablesVariables 70 | energyVariables : 71 | list: 72 | - batteryStorage (the energy stored in the battery, float) 73 | componentSpecifications : 74 | list : 75 | - battMaxStorage (the energy storage capacity of the battery, float), 76 | - genMaxPow (the maximum power output of the generator, float), 77 | - battMaxInputPow (the maximum power admissible to charge the battery, float), 78 | - battMaxOutputPow (the maximum power yielded by the battery) 79 | - SOC_min (the minimum admissible charge for the battery, float) 80 | - strategy (the chosen dispatching strategy, string) 81 | 82 | OUPUT : 83 | list : 84 | - genPower (the power at which the generator functioned, float) 85 | - newBattStorage (the new amount of energy stored in the battery, float) 86 | 87 | Given a load, the energy stored in the battery and the specifications of the different components of the grid as well as the dispatching 88 | strategy, the function yields the power at which the generator had to function to meet the requirements of the load, as well 89 | as the new battery storage. Ultimately, this function will be called after each step forward in time. 90 | """ 91 | debut = time.time() 92 | 93 | def batteryManagement(batteryInputPower): 94 | """ 95 | The function returns the amount of energy stored in the battery by the end of the time period given charge/discharge 96 | """ 97 | # batteryInputPower Positive (inwards flow into battery) 98 | if batteryInputPower <= battMaxInputPow and batteryInputPower >= 0: 99 | chargingPower = batteryInputPower 100 | if (battMaxStorage - batteryStorage) >= chargingPower * timeStep: 101 | newBattStorage = batteryStorage + chargingPower * timeStep 102 | else: 103 | newBattStorage = battMaxStorage 104 | 105 | # batteryInputPower Positive (inwards flow into battery) 106 | if batteryInputPower > battMaxInputPow and batteryInputPower >= 0: 107 | chargingPower = battMaxInputPow 108 | if (battMaxStorage - batteryStorage) >= chargingPower * timeStep: 109 | newBattStorage = batteryStorage + chargingPower * timeStep 110 | else: 111 | newBattStorage = battMaxStorage 112 | 113 | # batteryInputPower Positive (outwards flow into battery) 114 | if batteryInputPower < 0 and abs(batteryInputPower) <= battMaxOutputPow: 115 | dischargingPower = batteryInputPower 116 | if (batteryStorage + dischargingPower*timeStep) < 0: 117 | newBattStorage = 0 118 | else: 119 | newBattStorage = batteryStorage + dischargingPower*timeStep 120 | 121 | # batteryInputPower Positive (outwards flow into battery) 122 | if batteryInputPower < 0 and abs(batteryInputPower) > battMaxOutputPow: 123 | dischargingPower = -battMaxInputPow 124 | if (batteryStorage + dischargingPower*timeStep) < 0: 125 | newBattStorage = 0 126 | else: 127 | newBattStorage = batteryStorage + dischargingPower*timeStep 128 | 129 | return newBattStorage 130 | 131 | def generatorManagement(powerNeeded, newBattStorage): 132 | """ 133 | The function tells us the power at which the generator should work as well as the energy stored in the battery by the end of the 134 | time period 135 | """ 136 | if strategy == "1": 137 | generatorPow = round(min(genMaxPow, powerNeeded), 3) 138 | return [generatorPow, newBattStorage] 139 | if strategy == "2": 140 | chargingPow = min(battMaxInputPow, (battMaxStorage - newBattStorage)/timeStep) 141 | generatorPow = round( 142 | min(powerNeeded + chargingPow, genMaxPow), 3) 143 | newBattStorage += chargingPow * timeStep 144 | return [generatorPow, newBattStorage] 145 | 146 | # We need to define a few variables first 147 | netLoad, timeStep = powerVariables[0], powerVariables[1] 148 | batteryStorage = energyVariables[0] 149 | battMaxStorage, genMaxPow, battMaxInputPow, battMaxOutputPow, SOC_min = componentSpecifications[ 150 | 0], componentSpecifications[1], componentSpecifications[ 151 | 2], componentSpecifications[3], componentSpecifications[4] 152 | 153 | # We check whether we produce more than needed or not 154 | if netLoad <= 0: 155 | newBattStorage = batteryManagement(abs(netLoad)) 156 | # newBattStorage = batteryManagement(abs(netLoad), battMaxInputPow, battMaxOutputPow, battMaxStorage, batteryStorage, timeStep) 157 | generatorPower = 0 158 | # print(time.time() - debut) 159 | return [generatorPower, round(newBattStorage, 3)] 160 | else: 161 | dischargingPow = min(netLoad, (batteryStorage - SOC_min) / timeStep, battMaxOutputPow) 162 | newBattStorage = round(batteryStorage - dischargingPow * timeStep, 3) 163 | # If the battery can cover the power needs of the grid 164 | if dischargingPow >= netLoad: # Output power battery depends on the amount of energy left in it 165 | res = [0, newBattStorage] 166 | # If we don't meet either of the previous conditions, we fall back in the case where we have to turn the generator on 167 | else: 168 | powerNeeded = netLoad - dischargingPow 169 | res = generatorManagement(powerNeeded, newBattStorage) 170 | # res = generatorManagement(powerNeeded, newBattStorage, genMaxPow, battMaxInputPow, battMaxStorage, timeStep, strategy) 171 | 172 | # print(time.time() - debut) 173 | return res 174 | 175 | --------------------------------------------------------------------------------