├── CITATION ├── Excel ├── Ninja Automator.vb ├── Ninja Automator.xlsm ├── README.md └── ninja_automator.vb ├── LICENSE ├── R ├── example.r ├── ninja_automator.r ├── renewables.ninja.solar.farms.csv ├── renewables.ninja.wind.farms.csv └── renewables.ninja.wind.output.csv └── README.md /CITATION: -------------------------------------------------------------------------------- 1 | To reference the Renewables.ninja in publications, please cite both of the following papers: 2 | 3 | S Pfenninger and I Staffell, 2016. Long-term patterns of European PV output using 30 years of validated hourly reanalysis and satellite data. Energy, 114, 1251-1265. https://dx.doi.org/10.1016/j.energy.2016.08.060 4 | 5 | I Staffell and S Pfenninger, 2016. Using bias-corrected reanalysis to simulate current and future wind power output. Energy, 114, 1224–1239. https://dx.doi.org/10.1016/j.energy.2016.08.068 6 | -------------------------------------------------------------------------------- /Excel/Ninja Automator.vb: -------------------------------------------------------------------------------- 1 | ' 2 | ' 3 | ' BSD 3-Clause License 4 | ' Copyright (c) 2018, Iain Staffell 5 | ' All rights reserved. 6 | ' See license text at the bottom of this script.. 7 | ' 8 | ' 9 | ' MENU: 10 | ' 11 | ' * Bansenshukai() 12 | ' 13 | ' Run solar PV simulation and download results into the active worksheet 14 | ' 15 | ' Requires the following named ranges to exist (defined at worksheet or workbook level) 16 | ' - for model inputs: 17 | ' TOKEN, DATASET, YEAR, LON, LAT, CAPACITY, AGGREGATION, SYSTEM_LOSS, TRACKING, TILT, AZIMUTH 18 | ' - for writing model results: 19 | ' OUTPUT 20 | ' 21 | ' 22 | ' 23 | ' * Shoninki() 24 | ' 25 | ' Run wind simulation and download results into the active worksheet 26 | ' 27 | ' Requires the following named ranges to exist (defined at worksheet or workbook level) 28 | ' - for model inputs: 29 | ' TOKEN, DATASET, YEAR, LON, LAT, CAPACITY, AGGREGATION, HEIGHT, TURBINE 30 | ' - for writing model results: 31 | ' OUTPUT 32 | ' 33 | ' 34 | ' 35 | ' * Ninpiden(Url, Par, Tok) 36 | ' 37 | ' Background function to communicate with the renewables.ninja API 38 | ' Takes three parameters: 39 | ' Url = the base address of the API model 40 | ' Par = a string containing the parameters to pass to the model 41 | ' Tok = the string containing the user token 42 | ' Returns an array of strings (the rows of CSV data) 43 | ' 44 | ' 45 | 46 | 47 | ' DOWNLOAD SOLAR DATA 48 | Sub Bansenshukai() 49 | 50 | 51 | 52 | ''' 53 | ''' READ IN PARAMETERS 54 | ''' 55 | 56 | Tok = Range("TOKEN") 57 | dataset = Range("DATASET") 58 | date_from = Range("YEAR") & "-01-01" 59 | date_to = Range("YEAR") & "-12-31" 60 | lon = Range("LON") 61 | lat = Range("LAT") 62 | capacity = Range("CAPACITY") 63 | aggregat = Range("AGGREGATION") 64 | 65 | loss = Range("SYSTEM_LOSS") 66 | track = Range("TRACKING") 67 | tilt = Range("TILT") 68 | azimuth = Range("AZIMUTH") 69 | 70 | 71 | 72 | ''' 73 | ''' BUILD THE REQUEST URL 74 | ''' 75 | 76 | Url = "https://www.renewables.ninja/api/data/pv?" 77 | 78 | Par = "lat=" & lat & "&lon=" & lon & "&date_from=" & date_from & "&date_to=" & date_to & _ 79 | "&dataset=" & dataset & "&capacity=" & capacity & "&system_loss=" & loss / 100 & _ 80 | "&tracking=" & track & "&tilt=" & tilt & "&azim=" & azimuth & "&format=csv" 81 | 82 | If (aggregat <> "hour") Then Par = Par & "&mean=" & aggregat 83 | 84 | 85 | ' set a warning that we're updating 86 | Call UpdateMessage 87 | 88 | 89 | 90 | ''' 91 | ''' RUN API SIMULATION & DOWNLOAD DATA 92 | ''' 93 | 94 | csv = Ninpiden(Url, Par, Tok) 95 | 96 | 97 | 98 | ''' 99 | ''' PASTE INTO SPREADSHEET 100 | ''' 101 | 102 | If Not (IsEmpty(csv)) Then 103 | 104 | ' spit it into the workbook 105 | Range("OUTPUT").Offset(1).Select 106 | Range(ActiveCell, ActiveCell.Offset(UBound(csv))).Value = Application.Transpose(csv) 107 | 108 | ' set a clear signal 109 | Call CompleteMessage(Url, Par) 110 | 111 | End If 112 | 113 | End Sub 114 | 115 | 116 | 117 | 118 | ' DOWNLOAD WIND DATA 119 | Sub Shoninki() 120 | 121 | 122 | 123 | ''' 124 | ''' READ IN PARAMETERS 125 | ''' 126 | 127 | Tok = Range("TOKEN") 128 | dataset = Range("DATASET") 129 | date_from = Range("YEAR") & "-01-01" 130 | date_to = Range("YEAR") & "-12-31" 131 | lon = Range("LON") 132 | lat = Range("LAT") 133 | capacity = Range("CAPACITY") 134 | aggregat = Range("AGGREGATION") 135 | 136 | Height = Range("HEIGHT") 137 | turbine = Range("TURBINE") 138 | 139 | 140 | 141 | ''' 142 | ''' BUILD THE REQUEST URL 143 | ''' 144 | 145 | Url = "https://www.renewables.ninja/api/data/wind?" 146 | 147 | Par = "lat=" & lat & "&lon=" & lon & "&date_from=" & date_from & "&date_to=" & date_to & _ 148 | "&dataset=" & dataset & "&capacity=" & capacity & "&height=" & Height & _ 149 | "&turbine=" & turbine & "&format=csv" 150 | 151 | If (aggregat <> "hour") Then Par = Par & "&mean=" & aggregat 152 | 153 | 154 | ' set a warning that we're updating 155 | Call UpdateMessage 156 | 157 | 158 | 159 | ''' 160 | ''' RUN API SIMULATION & DOWNLOAD DATA 161 | ''' 162 | 163 | csv = Ninpiden(Url, Par, Tok) 164 | 165 | 166 | 167 | ''' 168 | ''' PASTE INTO SPREADSHEET 169 | ''' 170 | 171 | If Not (IsEmpty(csv)) Then 172 | 173 | ' spit it into the workbook 174 | Range("OUTPUT").Offset(1).Select 175 | Range(ActiveCell, ActiveCell.Offset(UBound(csv))).Value = Application.Transpose(csv) 176 | 177 | ' set a clear signal 178 | Call CompleteMessage(Url, Par) 179 | 180 | End If 181 | 182 | End Sub 183 | 184 | 185 | 186 | 187 | 188 | ' DOWNLOADER COMPONENT 189 | Function Ninpiden(Url As Variant, Par As Variant, Tok As Variant) As Variant 190 | 191 | 192 | 193 | ''' 194 | ''' DOWNLOAD DATA 195 | ''' 196 | 197 | Set httpObject = CreateObject("MSXML2.XMLHTTP") 198 | 199 | With httpObject 200 | 201 | .Open "GET", Url & Par, False 202 | .setRequestHeader "Authorization", "Token " & Tok 203 | .send (Par) 204 | 205 | 206 | ' wait until data has been downloaded 207 | Do While 1 208 | If .readyState = 4 Then Exit Do 209 | DoEvents 210 | Loop 211 | 212 | ' check if we were successful 213 | If .Status <> 200 Then 214 | MsgBox ("Something went wrong, you got HTTP Status " & .Status & _ 215 | Chr(10) & Chr(10) & .responseText) 216 | Exit Function 217 | End If 218 | 219 | ' return the resulting code 220 | csv = .responseText 221 | 222 | End With 223 | 224 | 225 | ' split the csv into rows 226 | Ninpiden = Split(csv, Chr(10)) 227 | 228 | 229 | End Function 230 | 231 | 232 | 233 | ' UX BABY 234 | Function UpdateMessage() 235 | 236 | Range("OUTPUT").Select 237 | 238 | With Range(Selection, Selection.Offset(0, 1)) 239 | .Interior.ThemeColor = xlThemeColorAccent2 240 | .Interior.TintAndShade = 0.7 241 | .Range("A1").Value = "Simulation running!" 242 | .Range("B1").Value = "" 243 | End With 244 | 245 | End Function 246 | 247 | 248 | Function CompleteMessage(Url As Variant, Par As Variant) 249 | 250 | Range("OUTPUT").Select 251 | 252 | With Range(Selection, Selection.Offset(0, 1)) 253 | .Interior.ThemeColor = xlThemeColorAccent3 254 | .Interior.TintAndShade = 0.9 255 | .Range("A1").Value = "Complete:" 256 | .Range("B1").Value = Url & Par 257 | End With 258 | 259 | End Function 260 | 261 | 262 | ' 263 | ' 264 | ' BSD 3-Clause License 265 | ' Copyright (c) 2019, Iain Staffell 266 | ' All rights reserved. 267 | ' 268 | ' 269 | ' Redistribution and use in source and binary forms, with or without 270 | ' modification, are permitted provided that the following conditions are met: 271 | ' 272 | ' * Redistributions of source code must retain the above copyright notice, this 273 | ' list of conditions and the following disclaimer. 274 | ' 275 | ' * Redistributions in binary form must reproduce the above copyright notice, 276 | ' this list of conditions and the following disclaimer in the documentation 277 | ' and/or other materials provided with the distribution. 278 | ' 279 | ' * Neither the name of the copyright holder nor the names of its 280 | ' contributors may be used to endorse or promote products derived from 281 | ' this software without specific prior written permission. 282 | ' 283 | ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 284 | ' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 285 | ' IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 286 | ' DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 287 | ' FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 288 | ' DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 289 | ' SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 290 | ' CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 291 | ' OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 292 | ' OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 293 | ' 294 | ' 295 | 296 | 297 | -------------------------------------------------------------------------------- /Excel/Ninja Automator.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renewables-ninja/ninja_automator/9dc443416278f8b76f7c7bd145bc3db4d73ec039/Excel/Ninja Automator.xlsm -------------------------------------------------------------------------------- /Excel/README.md: -------------------------------------------------------------------------------- 1 | ## Ninja Automator Excel Interface 2 | 3 | This provides a VBA routine to run simulations via the Renewables.ninja API and deliver results into a spreadsheet. Usage should be self explanatory, read the INFO worksheet to begin. 4 | 5 | The Excel worksheet provides an example implementation, which allows the user to choose model parameters, and download data as either hourly values, daily averages or monthly averages. 6 | 7 | Tested on [Excel](https://products.office.com/en-gb/excel) 2010, 2013 and 2016 on Windows 7 and 10. 8 | 9 | Requires VBA Macros to be enabled. 10 | 11 |
12 | -------------------------------------------------------------------------------- /Excel/ninja_automator.vb: -------------------------------------------------------------------------------- 1 | ' 2 | ' 3 | ' BSD 3-Clause License 4 | ' Copyright (c) 2018, Iain Staffell 5 | ' All rights reserved. 6 | ' See license text at the bottom of this script.. 7 | ' 8 | ' 9 | ' MENU: 10 | ' 11 | ' * Bansenshukai() 12 | ' 13 | ' Run solar PV simulation and download results into the active worksheet 14 | ' 15 | ' Requires the following named ranges to exist (defined at worksheet or workbook level) 16 | ' - for model inputs: 17 | ' TOKEN, DATASET, YEAR, LON, LAT, CAPACITY, AGGREGATION, SYSTEM_LOSS, TRACKING, TILT, AZIMUTH 18 | ' - for writing model results: 19 | ' OUTPUT 20 | ' 21 | ' 22 | ' 23 | ' * Shoninki() 24 | ' 25 | ' Run wind simulation and download results into the active worksheet 26 | ' 27 | ' Requires the following named ranges to exist (defined at worksheet or workbook level) 28 | ' - for model inputs: 29 | ' TOKEN, DATASET, YEAR, LON, LAT, CAPACITY, AGGREGATION, HEIGHT, TURBINE 30 | ' - for writing model results: 31 | ' OUTPUT 32 | ' 33 | ' 34 | ' 35 | ' * Ninpiden(Url, Par, Tok) 36 | ' 37 | ' Background function to communicate with the renewables.ninja API 38 | ' Takes three parameters: 39 | ' Url = the base address of the API model 40 | ' Par = a string containing the parameters to pass to the model 41 | ' Tok = the string containing the user token 42 | ' Returns an array of strings (the rows of CSV data) 43 | ' 44 | ' 45 | 46 | 47 | ' DOWNLOAD SOLAR DAILY DATA 48 | Sub Bansenshukai() 49 | 50 | 51 | 52 | ''' 53 | ''' READ IN PARAMETERS 54 | ''' 55 | 56 | Tok = Range("TOKEN") 57 | dataset = Range("DATASET") 58 | date_from = Range("YEAR") & "-01-01" 59 | date_to = Range("YEAR") & "-12-31" 60 | lon = Range("LON") 61 | lat = Range("LAT") 62 | capacity = Range("CAPACITY") 63 | aggregat = Range("AGGREGATION") 64 | 65 | loss = Range("SYSTEM_LOSS") 66 | track = Range("TRACKING") 67 | tilt = Range("TILT") 68 | azimuth = Range("AZIMUTH") 69 | 70 | 71 | 72 | ''' 73 | ''' BUILD THE REQUEST URL 74 | ''' 75 | 76 | Url = "https://www.renewables.ninja/api/data/pv?" 77 | 78 | Par = "lat=" & lat & "&lon=" & lon & "&date_from=" & date_from & "&date_to=" & date_to & _ 79 | "&dataset=" & dataset & "&capacity=" & capacity & "&system_loss=" & loss & _ 80 | "&tracking=" & track & "&tilt=" & tilt & "&azim=" & azimuth & "&format=csv" 81 | 82 | If (aggregat <> "hour") Then Par = Par & "&mean=" & aggregat 83 | 84 | 85 | ' set a warning that we're updating 86 | Range("OUTPUT").Select 87 | With Range(Selection, Selection.Offset(0, 1)).Interior 88 | .ThemeColor = xlThemeColorAccent2 89 | .TintAndShade = 0.7 90 | End With 91 | 92 | 93 | 94 | ''' 95 | ''' RUN API SIMULATION & DOWNLOAD DATA 96 | ''' 97 | 98 | csv = Ninpiden(Url, Par, Tok) 99 | 100 | 101 | 102 | ''' 103 | ''' PASTE INTO SPREADSHEET 104 | ''' 105 | 106 | If Not (IsEmpty(csv)) Then 107 | 108 | ' spit it into the workbook 109 | Range("OUTPUT").Offset(1).Select 110 | Range(ActiveCell, ActiveCell.Offset(UBound(csv))).Value = Application.Transpose(csv) 111 | 112 | ' set a clear signal 113 | Range("OUTPUT").Select 114 | With Range(Selection, Selection.Offset(0, 1)).Interior 115 | .ThemeColor = xlThemeColorAccent3 116 | .TintAndShade = 0.9 117 | End With 118 | 119 | End If 120 | 121 | End Sub 122 | 123 | 124 | 125 | 126 | ' DOWNLOAD WIND DAILY DATA 127 | Sub Shoninki() 128 | 129 | 130 | 131 | ''' 132 | ''' READ IN PARAMETERS 133 | ''' 134 | 135 | Tok = Range("TOKEN") 136 | dataset = Range("DATASET") 137 | date_from = Range("YEAR") & "-01-01" 138 | date_to = Range("YEAR") & "-12-31" 139 | lon = Range("LON") 140 | lat = Range("LAT") 141 | capacity = Range("CAPACITY") 142 | aggregat = Range("AGGREGATION") 143 | 144 | Height = Range("HEIGHT") 145 | turbine = Range("TURBINE") 146 | 147 | 148 | 149 | ''' 150 | ''' BUILD THE REQUEST URL 151 | ''' 152 | 153 | Url = "https://www.renewables.ninja/api/data/wind?" 154 | 155 | Par = "lat=" & lat & "&lon=" & lon & "&date_from=" & date_from & "&date_to=" & date_to & _ 156 | "&dataset=" & dataset & "&capacity=" & capacity & "&height=" & Height & _ 157 | "&turbine=" & turbine & "&format=csv" 158 | 159 | If (aggregat <> "hour") Then Par = Par & "&mean=" & aggregat 160 | 161 | 162 | 163 | ' set a warning that we're updating 164 | Range("OUTPUT").Select 165 | With Range(Selection, Selection.Offset(0, 1)).Interior 166 | .ThemeColor = xlThemeColorAccent2 167 | .TintAndShade = 0.7 168 | End With 169 | 170 | 171 | 172 | ''' 173 | ''' RUN API SIMULATION & DOWNLOAD DATA 174 | ''' 175 | 176 | csv = Ninpiden(Url, Par, Tok) 177 | 178 | 179 | 180 | ''' 181 | ''' PASTE INTO SPREADSHEET 182 | ''' 183 | 184 | If Not (IsEmpty(csv)) Then 185 | 186 | ' spit it into the workbook 187 | Range("OUTPUT").Offset(1).Select 188 | Range(ActiveCell, ActiveCell.Offset(UBound(csv))).Value = Application.Transpose(csv) 189 | 190 | ' set a clear signal 191 | Range("OUTPUT").Select 192 | With Range(Selection, Selection.Offset(0, 1)).Interior 193 | .ThemeColor = xlThemeColorAccent3 194 | .TintAndShade = 0.9 195 | End With 196 | 197 | End If 198 | 199 | End Sub 200 | 201 | 202 | 203 | 204 | 205 | ' DOWNLOADER COMPONENT 206 | Function Ninpiden(Url As Variant, Par As Variant, Tok As Variant) As Variant 207 | 208 | 209 | 210 | ''' 211 | ''' DOWNLOAD DATA 212 | ''' 213 | 214 | Set httpObject = CreateObject("MSXML2.XMLHTTP") 215 | 216 | With httpObject 217 | 218 | .Open "GET", Url & Par, False 219 | .setRequestHeader "Authorization", "Token " & Tok 220 | .send (Par) 221 | 222 | 223 | ' wait until data has been downloaded 224 | Do While 1 225 | If .readyState = 4 Then Exit Do 226 | DoEvents 227 | Loop 228 | 229 | ' check if we were successful 230 | If .Status <> 200 Then 231 | MsgBox ("Something went wrong, you got HTTP Status " & .Status) 232 | Exit Function 233 | End If 234 | 235 | ' return the resulting code 236 | csv = .responseText 237 | 238 | End With 239 | 240 | 241 | ' split the csv into rows 242 | Ninpiden = Split(csv, Chr(10)) 243 | 244 | 245 | End Function 246 | 247 | 248 | 249 | 250 | 251 | ' 252 | ' 253 | ' BSD 3-Clause License 254 | ' Copyright (c) 2018, Iain Staffell 255 | ' All rights reserved. 256 | ' 257 | ' 258 | ' Redistribution and use in source and binary forms, with or without 259 | ' modification, are permitted provided that the following conditions are met: 260 | ' 261 | ' * Redistributions of source code must retain the above copyright notice, this 262 | ' list of conditions and the following disclaimer. 263 | ' 264 | ' * Redistributions in binary form must reproduce the above copyright notice, 265 | ' this list of conditions and the following disclaimer in the documentation 266 | ' and/or other materials provided with the distribution. 267 | ' 268 | ' * Neither the name of the copyright holder nor the names of its 269 | ' contributors may be used to endorse or promote products derived from 270 | ' this software without specific prior written permission. 271 | ' 272 | ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 273 | ' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 274 | ' IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 275 | ' DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 276 | ' FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 277 | ' DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 278 | ' SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 279 | ' CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 280 | ' OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 281 | ' OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 282 | ' 283 | ' 284 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | Copyright (c) 2012-2017, Iain Staffell 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /R/example.r: -------------------------------------------------------------------------------- 1 | ########################### 2 | #### ### 3 | #### ### 4 | ## ## RENEWABLES.NINJA 5 | ##### WEBSITE AUTOMATOR 6 | ## 7 | # 8 | # simple instructions: 9 | # change any file paths in this script from './path/to/' to the directory where you saved the R and CSV files 10 | # change the token string to match that from your user account 11 | # run through the five simple examples below 12 | # 13 | 14 | 15 | 16 | 17 | ##### 18 | ## ## MODEL SETUP 19 | ##### 20 | 21 | # pre-requisites 22 | library(curl) 23 | source('./path/to/ninja_automator.r') 24 | 25 | # insert your API authorisation token here 26 | token = 'deadbeef0decafbeef0beeffacade0beefedbabe' 27 | 28 | # establish your authorisation 29 | h = new_handle() 30 | handle_setheaders(h, 'Authorization'=paste('Token ', token)) 31 | 32 | 33 | 34 | 35 | 36 | ##### 37 | ## ## DOWNLOAD RENEWABLE TIME SERIES DATA FOR A SINGLE LOCATION 38 | ##### 39 | 40 | # EXAMPLE 1 ::: look at a very tall wind turbine on top of iain's house 41 | # optional args are (for example): from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, height=60, turbine='Vestas+V80+2000' 42 | w = ninja_get_wind(lat=51.5, lon=0, height=30) 43 | plot(w$time, w$output, type='l', col='blue') 44 | 45 | 46 | # EXAMPLE 2 ::: or the solar panels facing south-east on stefan's house 47 | # optional args are (for example): from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, system_loss=10, tracking=0, tilt=35, azim=180 48 | s = ninja_get_solar(47.5, 8.5, tilt=15, azim=135) 49 | lines(s$time, s$output, col='goldenrod') 50 | 51 | 52 | 53 | 54 | 55 | ##### 56 | ## ## DOWNLOAD RENEWABLE TIME SERIES DATA FOR MULTIPLE LOCATIONS 57 | ##### 58 | 59 | # EXAMPLE 3 ::: look at wind farms in each of the UK's capitals 60 | # args are the same as for ninja_get_wind - either pass a single values or vectors of values 61 | lat = c(51.5, 56, 51.5, 54.6) 62 | lon = c(0, -3.2, -3.2, -5.9) 63 | turbine = c('Vestas+V80+2000', 'Enercon+E66+1800', 'Siemens+SWT+2.3+93', 'GE+1.5sl') 64 | y = ninja_aggregate_wind(lat, lon, turbine=turbine) 65 | 66 | # how does the hourly data look? 67 | dev.new(width=20) 68 | plot(y$time, y$outputV1, type='l', col='#0E60BA') 69 | lines(y$time, y$outputV2, col='#C80028') 70 | lines(y$time, y$outputV3, col='#2E8C39') 71 | lines(y$time, y$outputV4, col='#FFCF01') 72 | 73 | # how do they correlate? 74 | dev.new() 75 | plot(y[ , -1], pch='.') 76 | 77 | # what about the daily averages? 78 | yd = aggregate(y, by=list(as.Date(y$time)), mean) 79 | dev.new(width=20) 80 | plot(yd$time, yd$outputV1, type='l', col='#0E60BA') 81 | lines(yd$time, yd$outputV2, col='#C80028') 82 | lines(yd$time, yd$outputV3, col='#2E8C39') 83 | lines(yd$time, yd$outputV4, col='#FFCF01') 84 | 85 | 86 | 87 | 88 | 89 | ##### 90 | ## ## DOWNLOAD RENEWABLE TIME SERIES DATA FOR MULTIPLE LOCATIONS 91 | ## ## USING CSV FILES FOR DATA INPUT AND OUTPUT 92 | ##### 93 | 94 | # EXAMPLE 4 :::: read a set of wind farms from CSV - save their outputs to CSV 95 | # this is the same as example 3 - the UK capital cities 96 | # your csv must have a strict structure: one row per farm, colums = lat, lon, from, to, dataset, capacity, height, turbine - and optionally name (all lowercase!) 97 | 98 | farms = read.csv('./path/to/renewables.ninja.wind.farms.csv', stringsAsFactors=FALSE) 99 | 100 | z = ninja_aggregate_wind(farms$lat, farms$lon, farms$from[1], farms$to[1], farms$dataset, farms$capacity, farms$height, farms$turbine) 101 | 102 | write.csv(z, './path/to/renewables.ninja.wind.output.csv', row.names=FALSE) 103 | 104 | 105 | 106 | # EXAMPLE 5 :::: read a set of solar farms from CSV - save their outputs to CSV 107 | # this is the ten largest US cities - and uses the 'name' column to identify our farms 108 | # your csv must have a strict structure: one row per farm, colums = lat, lon, from, to, dataset, capacity, system_loss, tracking, tilt, azim - and optionally name (all lowercase!) 109 | 110 | farms = read.csv('./path/to/renewables.ninja.solar.farms.csv', stringsAsFactors=FALSE) 111 | 112 | z = ninja_aggregate_solar(farms$lat, farms$lon, farms$from[1], farms$to[1], farms$dataset, farms$capacity, farms$system_loss, farms$tracking, farms$tilt, farms$azim, name=farms$name) 113 | 114 | write.csv(z, './path/to/renewables.ninja.solar.output.csv', row.names=FALSE) 115 | 116 | # how productive are these places 117 | colMeans(z[ , -1]) / farms$capacity 118 | 119 | 120 | 121 | 122 | # now you know the way of the ninja 123 | # use your power wisely 124 | # fight bravely 125 | -------------------------------------------------------------------------------- /R/ninja_automator.r: -------------------------------------------------------------------------------- 1 | ################################################################## 2 | # # 3 | # BSD 3-Clause License # 4 | # Copyright (C) 2012-2020 Iain Staffell # 5 | # All rights reserved. # 6 | # # 7 | ################################################################## 8 | #### ### 9 | #### ### 10 | ## ## RENEWABLES.NINJA 11 | ##### WEBSITE AUTOMATOR 12 | ## 13 | # 14 | # 15 | # MAIN USER FUNCTIONS: 16 | # 17 | # these contact the renewables.ninja API, perform your simulation 18 | # and then return the results as a dataframe. each function 19 | # requires the latitude and longitude, and optionally takes other 20 | # parameters that you can pass to the API such as wind turbine model 21 | # of solar panel orientation. 22 | # 23 | # 24 | # run a simulation for a single wind or solar farm 25 | # returns a dataframe of timestamps and output values 26 | # ninja_get_wind = function(lat, lon, ...) 27 | # ninja_get_solar = function(lat, lon, ...) 28 | # 29 | # run simulations for a set of wind or solar farms 30 | # returns a dataframe of timestamps and output values for each farm 31 | # ninja_aggregate_wind = function(lat, lon, ...) 32 | # ninja_aggregate_solar = function(lat, lon, ...) 33 | # 34 | # 35 | # 36 | # OTHER BACKGROUND FUNCTIONS: 37 | # 38 | # a list holding data about your api requests 39 | # used to keep track of your usage limits 40 | # apilog$... 41 | # 42 | # background functions to build URLs to access the API 43 | # ninja_build_wind_url = function(lat, lon, ...) 44 | # ninja_build_solar_url = function(lat, lon, ...) 45 | # 46 | # background functions to download and process ninja simulations 47 | # ninja_get_url = function(url) 48 | # ninja_aggregate_urls = function(urls) 49 | # 50 | # background functions that do other stuff 51 | # cat_flush(...) 52 | # format_date(...) 53 | # 54 | # 55 | 56 | 57 | 58 | ##### 59 | ## ## PRE-REQUISITES 60 | ##### 61 | 62 | library(curl) 63 | 64 | 65 | 66 | 67 | 68 | ##### 69 | ## ## FUNCTIONS TO BUILD THE URL FOR API REQUESTS 70 | ##### 71 | 72 | # example wind url: https://www.renewables.ninja/api/data/wind?&lat=-37.23&lon=143.05&date_from=2014-01-01&date_to=2014-02-28&capacity=240&dataset=merra2&height=83&turbine=Vestas+V80+2000&format=csv 73 | ninja_build_wind_url = function(lat, lon, from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, height=60, turbine='Vestas+V80+2000', raw='false', format='csv') 74 | { 75 | from = format_date(from) 76 | to = format_date(to) 77 | paste0('https://www.renewables.ninja/api/data/wind?&lat=', lat, '&lon=', lon, '&date_from=', from, '&date_to=', to, '&capacity=', capacity, '&dataset=', dataset, '&height=', height, '&turbine=', turbine, '&raw=', raw, '&format=', format) 78 | } 79 | 80 | 81 | # example solar url: https://www.renewables.ninja/api/data/pv?lat=45&lon=22&date_from=2014-01-01&date_to=2014-01-31&dataset=merra2&capacity=1&system_loss=0.1&tracking=0&tilt=35&azim=180&raw=false&format=csv 82 | ninja_build_solar_url = function(lat, lon, from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, system_loss=0.1, tracking=0, tilt=35, azim=180, raw='false', format='csv') 83 | { 84 | from = format_date(from) 85 | to = format_date(to) 86 | paste0('https://www.renewables.ninja/api/data/pv?lat=', lat, '&lon=', lon, '&date_from=', from, '&date_to=', to, '&capacity=', capacity, '&dataset=', dataset, '&system_loss=', system_loss, '&tracking=', tracking, '&tilt=', tilt, '&azim=', azim, '&raw=', raw, '&format=', format) 87 | } 88 | 89 | 90 | 91 | 92 | 93 | ##### 94 | ## ## FUNCTIONS TO DOWNLOAD A SINGLE REQUEST FROM THE API 95 | ##### 96 | 97 | # download wind or solar power output for a given location 98 | # 99 | # pass 'lat' and 'lon', and optionally other api variables 100 | # 101 | # returns a data frame with timestamp and power output 102 | # zero error checking :( 103 | # 104 | ninja_get_wind = function(lat, lon, from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, height=60, turbine='Vestas+V80+2000', raw='false') 105 | { 106 | url = ninja_build_wind_url(lat, lon, from, to, dataset, capacity, height, turbine, raw, 'csv') 107 | ninja_get_url(url) 108 | } 109 | 110 | 111 | ninja_get_solar = function(lat, lon, from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, system_loss=0.1, tracking=0, tilt=35, azim=180, raw='false') 112 | { 113 | url = ninja_build_solar_url(lat, lon, from, to, dataset, capacity, system_loss, tracking, tilt, azim, raw, 'csv') 114 | ninja_get_url(url) 115 | } 116 | 117 | 118 | 119 | # behind the scenes - this function contacts the ninja API 120 | # converts the CSV returned by the ninja into a data.frame 121 | # 122 | # it also trackes when you made previous server requests 123 | # to ensure you don't exceed your API rate limits 124 | # 125 | ninja_get_url = function(url) 126 | { 127 | # first - check we aren't exceeding our rate limits 128 | apilog$enforce_rate_limits() 129 | 130 | # make a note of when we made this request 131 | apilog$request_time <<- c(Sys.time(), apilog$request_time) 132 | 133 | # grab the data as csv 134 | req = curl(url, handle=h) 135 | csv = read.csv(req, skip=3, stringsAsFactors=FALSE) 136 | 137 | # convert the time column to POSIX 138 | csv[ , 1] = as.POSIXct(csv[ , 1], format="%Y-%m-%d %H:%M") 139 | 140 | # return 141 | csv 142 | } 143 | 144 | 145 | 146 | # here's the global object we use to track api requests 147 | apilog = list() 148 | 149 | # burst speed - how many seconds between individual requests 150 | apilog$burst_speed = 10 151 | 152 | # hourly_limit - how many requests can be made per hour 153 | apilog$hourly_limit = 50 154 | 155 | # a function to ensure we stick within these limits 156 | apilog$enforce_rate_limits = function() 157 | { 158 | # first - check we aren't exceeding our rate limit 159 | if (length(apilog$request_time) >= apilog$hourly_limit) 160 | { 161 | repeat 162 | { 163 | # get the time since our 50th request 164 | elapsed = difftime(Sys.time(), apilog$request_time[apilog$hourly_limit], units='secs') 165 | 166 | # we are safe to continue 167 | if (elapsed > 3600) break 168 | 169 | # approach iain with money to buy a bigger server if you want this rate increasing :) 170 | cat_flush('The ninja API is limited to', apilog$hourly_limit, 'requests per hour: waiting', 5 * ceiling((3600-elapsed)/5), 'seconds to proceed...') 171 | Sys.sleep(5) 172 | } 173 | 174 | cat_flush() 175 | 176 | } 177 | 178 | # second - check we aren't exceeding the burst limit 179 | elapsed = difftime(Sys.time(), apilog$request_time[1], units='secs') 180 | if (elapsed < apilog$burst_speed) 181 | { 182 | Sys.sleep( apilog$burst_speed - elapsed ) 183 | } 184 | } 185 | 186 | # a vector saying when we made requests 187 | apilog$request_time = Sys.time() 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | ##### 196 | ## ## FUNCTIONS TO DOWNLOAD AND AGGREGATE MULTIPLE REQUESTS FROM THE API 197 | ##### 198 | 199 | # download wind or solar power output for multiple locations 200 | # 201 | # pass 'lat' and 'lon' as vectors, optionally 'from' and 'to' as single strings, optionally other api variables either as single strings or vectors 202 | # optionally pass the 'name' to assign each output time series (column names in the returned data) 203 | # 204 | # returns a data frame with timestamp and power output 205 | # limited error checking :| 206 | # 207 | ninja_aggregate_wind = function(lat, lon, from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, height=60, turbine='Vestas+V80+2000', name=NULL) 208 | { 209 | # check our coordinates are of the same length 210 | if (length(lat) != length(lon)) 211 | stop("Error in ninja_aggregate_wind: lat and lon should be vectors of the same length!\n") 212 | 213 | # check we only want one time span 214 | if (length(from) != 1 | length(to) != 1) 215 | stop("Error in ninja_aggregate_wind: from and to should be a single date only!\n") 216 | 217 | # check our other parameters are either single values or one value per coordinate 218 | length_parms = c(length(dataset), length(capacity), length(height), length(turbine)) 219 | if (!all( length_parms %in% c(1, length(lat)) )) 220 | stop("Error in ninja_aggregate_wind: farm parameters should either be single values or vectors of the same length as lat and lon!\n") 221 | 222 | # convert all our parameters to api request urls 223 | urls = ninja_build_wind_url(lat, lon, from, to, dataset, capacity, height, turbine) 224 | 225 | # do the magic 226 | ninja_aggregate_urls(urls, name) 227 | } 228 | 229 | 230 | ninja_aggregate_solar = function(lat, lon, from='2014-01-01', to='2014-12-31', dataset='merra2', capacity=1, system_loss=0.1, tracking=0, tilt=35, azim=180, name=NULL) 231 | { 232 | # check our coordinates are of the same length 233 | if (length(lat) != length(lon)) 234 | stop("Error in ninja_aggregate_solar: lat and lon should be vectors of the same length!\n") 235 | 236 | # check we only want one time span 237 | if (length(from) != 1 | length(to) != 1) 238 | stop("Error in ninja_aggregate_solar: from and to should be a single date only!\n") 239 | 240 | # check our other parameters are either single values or one value per coordinate 241 | length_parms = c(length(dataset), length(capacity), length(system_loss), length(tracking), length(tilt), length(azim)) 242 | if (!all( length_parms %in% c(1, length(lat)) )) 243 | stop("Error in ninja_aggregate_solar: farm parameters should either be single values or vectors of the same length as lat and lon!\n") 244 | 245 | # convert all our parameters to api request urls 246 | urls = ninja_build_solar_url(lat, lon, from, to, dataset, capacity, system_loss, tracking, tilt, azim) 247 | 248 | # do the magic 249 | ninja_aggregate_urls(urls, name) 250 | } 251 | 252 | 253 | ninja_aggregate_urls = function(urls, name=NULL) 254 | { 255 | n = length(urls) 256 | 257 | # if we are passing names for our farms - check there's the right number of them 258 | if (!is.null(name)) 259 | if (length(name) != n) 260 | stop("Error in ninja_aggregate_urls: name should be a vector the same length as urls / lat and lon!\n") 261 | 262 | 263 | # run through each farm 264 | for (i in 1:n) 265 | { 266 | # i call this UX 267 | cat_flush("Downloading farm", i, "of", n) 268 | 269 | # grab the data for this farm 270 | this_farm = ninja_get_url(urls[i]) 271 | 272 | # aggregate our data together 273 | if (i == 1) 274 | all_farms = data.frame(V1=this_farm[ , 2]) 275 | 276 | if (i > 1) 277 | all_farms[ , i] = this_farm[ , 2] 278 | 279 | } 280 | 281 | # bind the timestamps onto the aggregated data 282 | all_farms = cbind(this_farm[ , 1], all_farms, stringsAsFactors=FALSE) 283 | colnames(all_farms)[1] = colnames(this_farm)[1] 284 | 285 | # assign names to the output columns 286 | if (!is.null(name)) 287 | { 288 | colnames(all_farms)[-1] = make.names(name) 289 | } else { 290 | colnames(all_farms)[-1] = paste0('output', colnames(all_farms)[-1]) 291 | } 292 | 293 | # more UX! 294 | cat_flush("Downloaded", n, "farms...\n") 295 | 296 | # return 297 | all_farms 298 | } 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | ##### 307 | ## ## GENERICS 308 | ##### 309 | 310 | # console - clear line and flush line 311 | cat_flush = function(...) 312 | { 313 | cat("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b") 314 | cat("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b") 315 | cat("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b") 316 | cat("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b") 317 | cat(...) 318 | flush.console() 319 | } 320 | 321 | # convert unknown date formats into standard posix 322 | # this is overkill, but it allows us to handle 2014-12-31 format 323 | # that the ninja expects plus 31/12/2014 that Excel defaults to 324 | # 325 | # @x - a vector of character dates/datetimes 326 | # 327 | # derived from Cole Beck's "Handling date-times in R" http://biostat.mc.vanderbilt.edu/wiki/pub/Main/ColeBeck/datestimes.pdf 328 | # 329 | format_date = function(x) 330 | { 331 | x1 = x 332 | 333 | # replace blanks with NA and remove 334 | x1[x1 == ""] = NA 335 | x1 = x1[!is.na(x1)] 336 | if (length(x1) == 0) 337 | return(NA) 338 | 339 | # if it's already a time variable, set it to character 340 | if ("POSIXt" %in% class(x1[1])) 341 | x1 = as.character(x1) 342 | 343 | dateTimes = do.call(rbind, strsplit(x1, " ")) 344 | 345 | for (i in ncol(dateTimes)) 346 | dateTimes[dateTimes[, i] == "NA"] = NA 347 | 348 | # assume the time part can be found with a colon 349 | timePart = which(apply(dateTimes, MARGIN=2, FUN=function(i) { any(grepl(":", i)) } )) 350 | 351 | # everything not in the timePart should be in the datePart 352 | datePart = setdiff(seq(ncol(dateTimes)), timePart) 353 | 354 | # should have 0 or 1 timeParts and exactly one dateParts 355 | if (length(timePart) > 1 || length(datePart) != 1) 356 | stop("Error in format_date: cannot parse your time variable") 357 | 358 | timeFormat = NA 359 | 360 | if (length(timePart)) 361 | { 362 | # find maximum number of colons in the timePart column 363 | ncolons = max(nchar(gsub("[^:]", "", na.omit(dateTimes[, timePart])))) 364 | 365 | if (ncolons == 1) { 366 | timeFormat = "%H:%M" 367 | } else if (ncolons == 2) { 368 | timeFormat = "%H:%M:%S" 369 | } else stop("Error in format_date: timePart should have 1 or 2 colons") 370 | } 371 | 372 | # remove all non-numeric values 373 | dates = gsub("[^0-9]", "", na.omit(dateTimes[, datePart])) 374 | 375 | # sep is any non-numeric value found, hopefully / or - 376 | sep = unique(na.omit(substr(gsub("[0-9]", "", dateTimes[, datePart]), 1, 1))) 377 | if (length(sep) > 1) 378 | stop("Error in format_date: too many seperators in datePart") 379 | 380 | # maximum number of characters found in the date part 381 | dlen = max(nchar(dates)) 382 | dateFormat = NA 383 | 384 | # when six, expect the century to be omitted 385 | if (dlen == 6) 386 | { 387 | if (sum(is.na(as.Date(dates, format = "%y%m%d"))) == 0) { 388 | dateFormat = paste("%y", "%m", "%d", sep = sep) 389 | } else if (sum(is.na(as.Date(dates, format = "%d%m%y"))) == 0) { 390 | dateFormat = paste("%d", "%m", "%y", sep = sep) 391 | } else stop("Error in format_date: datePart format [six characters] is inconsistent") 392 | 393 | } else if (dlen == 8) { 394 | 395 | if (sum(is.na(as.Date(dates, format = "%Y%m%d"))) == 0) { 396 | dateFormat = paste("%Y", "%m", "%d", sep = sep) 397 | } else if (sum(is.na(as.Date(dates, format = "%d%m%Y"))) == 0) { 398 | dateFormat = paste("%d", "%m", "%Y", sep = sep) 399 | } else stop("Error in format_date: datePart format [eight characters] is inconsistent") 400 | 401 | } else { 402 | 403 | stop(sprintf("Error in format_date: datePart has unusual length: %s", dlen)) 404 | } 405 | 406 | if (is.na(timeFormat)) 407 | { 408 | format = dateFormat 409 | } else if (timePart == 1) { 410 | format = paste(timeFormat, dateFormat) 411 | } else if (timePart == 2) { 412 | format = paste(dateFormat, timeFormat) 413 | } else stop("Error in format_date: cannot parse your time variable") 414 | 415 | x = as.POSIXlt(x, format=format) 416 | return(format(x)) 417 | } 418 | 419 | -------------------------------------------------------------------------------- /R/renewables.ninja.solar.farms.csv: -------------------------------------------------------------------------------- 1 | name,lat,lon,from,to,dataset,capacity,system_loss,tracking,tilt,azim 2 | New York,40.7,-73.9,2014-01-01,2014-12-31,merra2,8550,0.1,0,40.7,176 3 | Los Angeles,34.0,-118.4,2014-01-01,2014-12-31,merra2,3971,0.1,0,34.0,166 4 | Chicago,41.8,-87.7,2014-01-01,2014-12-31,merra2,2720,0.1,0,41.8,226 5 | Houston,29.8,-95.4,2014-01-01,2014-12-31,merra2,2296,0.1,0,29.8,171 6 | Philadelphia,40.0,-75.1,2014-01-01,2014-12-31,merra2,1567,0.1,0,40.0,162 7 | Phoenix,33.6,-112.1,2014-01-01,2014-12-31,merra2,1563,0.1,0,33.6,204 8 | San Antonio,29.5,-98.5,2014-01-01,2014-12-31,merra2,1469,0.1,0,29.5,173 9 | San Diego,32.8,-117.1,2014-01-01,2014-12-31,merra2,1394,0.1,0,32.8,190 10 | Dallas,32.8,-96.8,2014-01-01,2014-12-31,merra2,1300,0.1,0,32.8,150 11 | San Jose,37.3,-121.8,2014-01-01,2014-12-31,merra2,1026,0.1,0,37.3,183 12 | -------------------------------------------------------------------------------- /R/renewables.ninja.wind.farms.csv: -------------------------------------------------------------------------------- 1 | name,lat,lon,from,to,dataset,capacity,height,turbine 2 | London,51.5,0,01/01/2014,31/12/2014,merra2,150,60,Vestas+V80+2000 3 | Edinburgh,56,-3.2,01/01/2014,31/12/2014,merra2,200,40,Enercon+E66+1800 4 | Cardiff,51.5,-3.2,01/01/2014,31/12/2014,merra2,50,80,Siemens+SWT+2.3+93 5 | Belfast,54.6,-5.9,01/01/2014,31/12/2014,merra2,100,50,GE+1.5sl 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Ninja Automator 2 | 3 | This is a multi-langugage tool to scrape data from the [Renewables.ninja](https://www.renewables.ninja/) website. It allows you to download wind and solar output data for multiple locations more easily. 4 | 5 | Currently there are implementations in Excel and R, with a Python version under development. 6 | 7 | 8 | ## Ninja Automator Excel VBA 9 | 10 | You can download the [Ninja Excel Interface here](https://github.com/renewables-ninja/ninja_automator/raw/master/Excel/Ninja%20Automator.xlsm). 11 | 12 | This provides a VBA routine to run simulations via the Renewables.ninja API and deliver results into a spreadsheet. Usage should be self explanatory, read the INFO worksheet to begin. 13 | 14 | The Excel worksheet provides an example implementation, which allows the user to choose model parameters, and download data as either hourly values, daily averages or monthly averages. 15 | 16 | Tested on [Excel](https://products.office.com/en-gb/excel) 2010, 2013 and 2016 on Windows 7 and 10. 17 | 18 | Requires VBA Macros to be enabled. 19 | 20 |
21 | 22 | 23 | ## Ninja Automator R 24 | 25 | This provides a set of worked examples that contact the renewables.ninja API, perform your simulation and return the results as a data.frame. Multiple simulations can be performed by either supplying vectors of input parameters or by reading them in from a CSV file. 26 | 27 | 28 | ### REQUIREMENTS & SETUP 29 | 30 | [R](https://www.r-project.org/) or [MRO](https://mran.revolutionanalytics.com/open/) version 3+, with the `curl` library. 31 | 32 | Download the files from the /R subfolder and you are ready to go. 33 | 34 | Inside `example.r` edit the path names and your API token. 35 | 36 | 37 | ### USAGE INSTRUCTIONS 38 | 39 | `ninja_automator.r` provides the background functions for communicating with the Renewables.ninja API. Each function requires the latitude and longitude, and optionally takes other parameters that you can pass to the API such as wind turbine model or solar panel orientation. 40 | 41 | `example.r` provides a set of five ready-made examples that walk you through running a single simulation, aggregating many simulations together, and reading inputs/output files to fully automate the ninja. 42 | 43 | The functions `ninja_get_wind(lat, lon, ...)` and `ninja_get_solar(lat, lon, ...)` run a simulation for a single wind or solar farm by passing input parameters. They will yield a 2-column dataframe containing timestamp and output. You can expect each to take around 5-10 seconds to complete, due to the time needed to contact the server, the simulation to run, etc. 44 | 45 | The functions `ninja_aggregate_wind(lat, lon, ...)` and `ninja_aggregate_solar(lat, lon, ...)` run simulations for multiple wind or solar farms by passing vectors of input data. These will yield a multi-column dataframe containing timestamp and the output of each farm as a sepearate column. You can expect the function to take around 10 seconds per farm being simulated. 46 | 47 | All the functions keep track of the number of simulations you have run, and will pause when necessary to prevent you from exceeding the hourly API limits. If you'd like it to be faster, contact us via the Renewables.ninja [forum](https://community.renewables.ninja/) or [email](https://www.renewables.ninja/about). 48 | 49 | `renewables.ninja.solar.farms.csv` and `renewables.ninja.wind.farms.csv` are example input files that can be fed into the automator to download a group of farms. `renewables.ninja.wind.output.csv` is an example of the data that will be returned by `ninja_aggregate_wind`. 50 | 51 |
52 | 53 | 54 | ## LICENSE 55 | BSD 3-Clause License 56 | 57 | Copyright (C) 2016-2018 Iain Staffell 58 | 59 | All rights reserved. 60 | 61 | See `LICENSE` for more detail. 62 | 63 | 64 | 65 | ## CREDITS & CONTACT 66 | 67 | The R automator is developed by Iain Staffell. You can try emailing me at contact@renewables.ninja. 68 | 69 | This is part of the [Renewables.ninja](https://renewables.ninja) project, developed by Stefan Pfenninger and Iain Staffell. Use the [contacts page](https://www.renewables.ninja/about) there. 70 | 71 | 72 | ### Citation 73 | 74 | I Staffell and S Pfenninger, 2016. Using bias-corrected reanalysis to simulate current and future wind power output. *Energy*, 114, 1224–1239. [doi: 10.1016/j.energy.2016.08.068](https://dx.doi.org/10.1016/j.energy.2016.08.068) 75 | 76 | S Pfenninger and I Staffell, 2016. Long-term patterns of European PV output using 30 years of validated hourly reanalysis and satellite data. Energy, 114, 1251-1265. [doi: 10.1016/j.energy.2016.08.060](https://dx.doi.org/10.1016/j.energy.2016.08.060) 77 | --------------------------------------------------------------------------------