├── scratch.js ├── .gitignore ├── Data ├── SolarOutput.csv ├── EnergyPrice.csv └── basic_data.csv ├── truffle ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js ├── truffle.js ├── contracts │ ├── Migrations.sol │ └── Aggregator.sol ├── scratch.js └── build │ └── contracts │ ├── Migrations.json │ └── Aggregator.json ├── LICENSE ├── README.md ├── scratch.ipynb └── Central Optimization Code.ipynb /scratch.js: -------------------------------------------------------------------------------- 1 | compile; 2 | migrate; 3 | 4 | 5 | whitelist = [web3.eth.accounts[0],web3.eth.accounts[1],web3.eth.accounts[2]]; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | .DS_Store 3 | .save 4 | .pyc 5 | .env 6 | .ipynb_checkpoints/ 7 | env/ 8 | Data 9 | nohup.out 10 | -------------------------------------------------------------------------------- /Data/SolarOutput.csv: -------------------------------------------------------------------------------- 1 | 1,0 2,0 3,0 4,0 5,0 6,96 7,345 8,594 9,821 10,1011 11,1149 12,1228 13,1241 14,1187 15,1071 16,899 17,685 18,442 19,189 20,9 21,0 22,0 23,0 24,0 -------------------------------------------------------------------------------- /truffle/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /Data/EnergyPrice.csv: -------------------------------------------------------------------------------- 1 | 10.69031 9.75753 9.53744 9.62128 12 15 18.72901 22.61735 22.92129 23.18331 22.50206 22.47062 20.11246 20.18583 20.14391 20.11246 20.92996 22 24 22.66975 20.39544 17.36652 15.19702 9.35927 -------------------------------------------------------------------------------- /truffle/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | host: "localhost", 5 | port: 8545, 6 | network_id: "*" // Match any network id 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /truffle/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | // var ConvertLib = artifacts.require("./ConvertLib.sol"); 2 | var Aggregator = artifacts.require("./Aggregator.sol"); 3 | var accts = [web3.eth.accounts[0],web3.eth.accounts[1],web3.eth.accounts[2]] 4 | console.log(accts) 5 | 6 | module.exports = function(deployer) { 7 | deployer.deploy(Aggregator, accts); 8 | }; 9 | -------------------------------------------------------------------------------- /truffle/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.4; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Data/basic_data.csv: -------------------------------------------------------------------------------- 1 | 1,2,0.16,0.388 2,3,0.824,0.315 2,4,0.144,0.349 4,5,1.026,0.421 4,6,0.741,0.466 4,7,0.528,0.468 7,8,0.358,0.314 8,9,2.032,0.798 8,10,0.502,0.441 10,11,0.372,0.327 11,12,1.431,0.999 11,13,0.429,0.377 13,14,0.671,0.257 13,15,0.457,0.401 15,16,1.008,0.385 15,17,0.153,0.134 17,18,0.971,0.722 18,19,1.885,0.721 4,20,0.138,0.334 20,21,0.251,0.096 21,22,1.818,0.695 20,23,0.225,0.542 23,24,0.127,0.028 23,25,0.284,0.687 25,26,0.171,0.414 26,27,0.414,0.386 27,28,0.21,0.196 28,29,0.395,0.369 29,30,0.248,0.232 30,31,0.279,0.26 26,32,0.205,0.495 32,33,0.263,0.073 32,34,0.071,0.171 34,35,0.625,0.273 34,36,0.51,0.209 36,37,2.018,0.829 34,38,1.062,0.406 38,39,0.61,0.238 39,40,2.349,0.964 34,41,0.115,0.278 41,42,0.159,0.384 42,43,0.934,0.383 42,44,0.506,0.163 42,45,0.095,0.195 42,46,1.915,0.769 41,47,0.157,0.379 47,48,1.641,0.67 47,49,0.081,0.196 49,50,1.727,0.709 49,51,0.112,0.27 51,52,0.674,0.275 51,53,0.07,0.17 53,54,2.041,0.78 53,55,0.813,0.334 53,56,0.141,0.34 -------------------------------------------------------------------------------- /truffle/scratch.js: -------------------------------------------------------------------------------- 1 | compile; 2 | migrate; 3 | 4 | addr1 = web3.eth.accounts[0]; 5 | addr2 = web3.eth.accounts[1]; 6 | addr3 = web3.eth.accounts[2]; 7 | whitelist = [addr1,addr2,addr3]; 8 | 9 | var agg; 10 | Aggregator.deployed().then(function(instance){agg=instance;}) 11 | 12 | // Get waiting status: either of these: 13 | agg.whitelist(0).then(function(addr){agg.waiting(addr).then(function(iswaiting){console.log(iswaiting)})}) 14 | agg.waiting(addr1).then(function(iswaiting){console.log(iswaiting);}) 15 | 16 | agg.whitelistLength.call().then(function(a){console.log(a)}) 17 | 18 | agg.getAverage.call(1).then(function(a){console.log(a)}) 19 | 20 | var iteration; 21 | agg.iteration().then(function(result){iteration = result;}) 22 | iteration = iteration.toNumber(); 23 | agg.submitValue(12, iteration, {from: addr1}).then(function(i){}) 24 | agg.submitValue(12, iteration, {from: addr2}).then(function(i){}) 25 | agg.submitValue(13, iteration, {from: addr3}).then(function(i){}) 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Eric Munsing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /truffle/build/contracts/Migrations.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "Migrations", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "new_address", 9 | "type": "address" 10 | } 11 | ], 12 | "name": "upgrade", 13 | "outputs": [], 14 | "payable": false, 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "last_completed_migration", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "uint256" 25 | } 26 | ], 27 | "payable": false, 28 | "type": "function" 29 | }, 30 | { 31 | "constant": true, 32 | "inputs": [], 33 | "name": "owner", 34 | "outputs": [ 35 | { 36 | "name": "", 37 | "type": "address" 38 | } 39 | ], 40 | "payable": false, 41 | "type": "function" 42 | }, 43 | { 44 | "constant": false, 45 | "inputs": [ 46 | { 47 | "name": "completed", 48 | "type": "uint256" 49 | } 50 | ], 51 | "name": "setCompleted", 52 | "outputs": [], 53 | "payable": false, 54 | "type": "function" 55 | }, 56 | { 57 | "inputs": [], 58 | "payable": false, 59 | "type": "constructor" 60 | } 61 | ], 62 | "unlinked_binary": "0x6060604052341561000c57fe5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101a0806100396000396000f300606060405263ffffffff60e060020a6000350416630900f0108114610042578063445df0ac146100605780638da5cb5b14610082578063fdacd576146100ae575bfe5b341561004a57fe5b61005e600160a060020a03600435166100c3565b005b341561006857fe5b61007061013d565b60408051918252519081900360200190f35b341561008a57fe5b610092610143565b60408051600160a060020a039092168252519081900360200190f35b34156100b657fe5b61005e600435610152565b005b6000805433600160a060020a03908116911614156101375781905080600160a060020a031663fdacd5766001546040518263ffffffff1660e060020a02815260040180828152602001915050600060405180830381600087803b151561012557fe5b6102c65a03f1151561013357fe5b5050505b5b5b5050565b60015481565b600054600160a060020a031681565b60005433600160a060020a039081169116141561016f5760018190555b5b5b505600a165627a7a72305820f8b13a328b6d028c58291b308c4574c1dbe2dcd498c5f92c5b87c363a32ffb800029", 63 | "networks": { 64 | "1499728778316": { 65 | "events": {}, 66 | "links": {}, 67 | "address": "0x69333d917ccf7e0b23ab6387a9b4285525f7fcc4", 68 | "updated_at": 1499803338291 69 | }, 70 | "1499899197302": { 71 | "events": {}, 72 | "links": {}, 73 | "address": "0xc0e88ae70662622382d8f720b65f5c4893a4e75b", 74 | "updated_at": 1499900666060 75 | } 76 | }, 77 | "schema_version": "0.0.5", 78 | "updated_at": 1499900666060 79 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README - Energy Blockchain Project 2 | 3 | Goal: 4 | - Create a schedule for electric devices based on private device constraints, using a blockchain for coordination between the devices. 5 | 6 | Ideal structure: 7 | - Each 'device' (this could represent a home or feeder) runs a node on a private blockchain 8 | - Each device computes a convex optimization problem that is the private, device-specific part of the scheduling problem, while a central smart contract is used to fairly/securely compute an update 9 | - Once the schedule is finalized, it is stored on the blockchain and available for everyone to access. 10 | - Individual devices (e.g. wifi lightbulbs and switches) can read their scheduled commitments, and switch on/off accordingly. 11 | 12 | Broadly, this means that we can think of there being two types of actors on the system: the devices, and the smart contract. 13 | 14 | The devices go through these steps: 15 | - Compute the private optimization updates which lead to consensus on the system's schedule 16 | - Trade this information with the aggregator on the blockchain until a schedule is reached 17 | - Once a schedule is finalized, turn the device on/off to follow the schedule 18 | 19 | The aggregator smart contract goes through these steps: 20 | - Post a draft set of variables for all the devices to access 21 | - Wait for updates from all the devices, then compute an update for the 22 | 23 | 24 | ## Pseudocode for each component 25 | The following roughly outlines the functions computed by each participant 26 | 27 | **The devices** 28 | - Loop: 29 | - Check the smart contract for current central variables 30 | - If the smart contract's variables are from a more recent time step, get them and save them 31 | - If not, wait and check again 32 | - Compute private optimization 33 | - Post update to the aggregator smart contract 34 | - Once a schedule is finalized, retrieve that schedule and switch hardware off and on as needed to satisfy requirements 35 | - *(Future work)* Smart meter posts actual consumption to a billing smart contract and automatically charges devices for the power consumed 36 | Note that the scheduling loop and the 37 | 38 | **The Smart Contract(s)** 39 | - Hold a whitelist of devices which will provide updates 40 | - When requested, provide a schedule with timestamp for creation or None if the schedule has not yet been computed 41 | - When requested, provide a current version of the aggregator variables or None if the schedule has already been computed for the day 42 | - When provided with a new set of device variables, store the reported variables and take the device name off of the list of devices we are waiting for. If the list is empty, call the update function 43 | - Update function: compute the update for the aggregator variables, and send the updated iteration number and variable to the contract holding the schedule 44 | - If the variables have converged, save the power consumption to the schedule and set the 45 | 46 | 47 | The blockchain holds 2 relevant smart contracts: 48 | - ADMM aggregation: 49 | - Holds a set of central variables for each iteration of the algorithm. Recieves inputs from each device, and when all devices have submitted their updates for a step computes the new iteration's variables based on an average of all devices' proposals. 50 | - 51 | 52 | 53 | - Run a private blockchain 54 | - Each of the devices submits its private cost to the ADMM averaging step on the blockchain to get the status of the ADMM iterations (ideally using their own blockchain node) 55 | - Once all nodes have checked in their ADMM update, they 56 | - Once a sufficient number of blocks have passed, the nodes pull the most recent ADMM variable status, and update their private calculation 57 | 58 | 59 | Key components of code: 60 | 61 | - Optimization: This creates a schedule for all the devices on the system 62 | - Ethereum interface: Private -------------------------------------------------------------------------------- /truffle/contracts/Aggregator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.4; 2 | 3 | /* 4 | Eric Munsing - e.munsing@berkeley.edu 5 | MIT license 2017 6 | 7 | GOAL: 8 | - Accept estimates of state variables from each devices 9 | - Publish an updated aggregator aggregator variable when all state estimates have been recieved 10 | - Once the error between estimates is small, stop computing iterations 11 | - Save the schedule for all nodes on the network 12 | 13 | PSEUDOCODE: 14 | - Store a set of whitelisted devices (this may be changed later) 15 | 16 | - Initialize at iteration zero 17 | - For each iteration, accept one estimate from each device 18 | - Check to make sure that the device hasn't submitted an estimate yet 19 | - Check to make sure that the estimate is for the current iteration 20 | - Check to make sure we haven't already generated a schedule 21 | - Once estimates from all devices have been submitted for the iteration, 22 | - Compute and save the aggregator average 23 | - Compute the error 24 | - If the error is below the threshhold, save the schedule and flag that the schedule is complete 25 | - Otherwise, 26 | - Increment the iteration 27 | - Begin accepting estimates from all devices 28 | - Once the schedule is saved, don't do anything (in the future, this should reset on each market clearing block) 29 | */ 30 | 31 | /* TO-DO: 32 | 33 | - Turn variables into vectors 34 | - Include all consensus variables 35 | - Compute full aggregator averaging step 36 | - 37 | 38 | */ 39 | 40 | contract Aggregator { 41 | // ADMM aggregator step 42 | struct Estimate{ 43 | int256 P; // There are no fixed-point or floating-point variables in Ethereum. Multiply by 1e9 instead 44 | } 45 | 46 | address owner; 47 | uint16 public iteration; 48 | uint256 public tolerance; 49 | uint256 public iterationError; 50 | address[] public whitelist; 51 | mapping (address => bool) public waiting; 52 | mapping (address => Estimate) public allEstimates; 53 | Estimate public average; 54 | bool public scheduleComplete; 55 | Estimate public schedule; 56 | 57 | /* CONSTRUCTOR */ 58 | function Aggregator(address[] _whitelist) public{ 59 | scheduleComplete = false; 60 | owner = msg.sender; 61 | whitelist = _whitelist; 62 | iteration = 0; 63 | resetWaiting(); // Set the waiting flag to 1 for everybody 64 | } 65 | 66 | function whitelistLength () returns (uint256){ 67 | return whitelist.length; 68 | } 69 | 70 | function stillWaiting () returns (bool) { 71 | for (uint8 i=0; i bool) public waiting;\n", 178 | " \n", 179 | " \n", 180 | " /* CONSTRUCTOR */\n", 181 | " function aggregator (address[] _whitelist) public{\n", 182 | " whitelist = _whitelist;\n", 183 | " iteration = 1;\n", 184 | " resetWaiting(); // Set the waiting flag to 1 for everybody\n", 185 | " }\n", 186 | " \n", 187 | " function stillWaiting () returns (bool) {\n", 188 | " for (uint8 i=0; i:aggregator']['abi']" 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": 12, 260 | "metadata": {}, 261 | "outputs": [ 262 | { 263 | "name": "stdout", 264 | "output_type": "stream", 265 | "text": [ 266 | "Contract transaction id is 0x50f15255ba7079c5c53ef6e4214428564c24c4f84f2c2faa2485d29e276d3194\n", 267 | "Waiting for the contract to be mined into the blockchain...\n", 268 | "Contract address is 0x884446527421b7a178c42d12620fa9bc98238442\n" 269 | ] 270 | } 271 | ], 272 | "source": [ 273 | "# Basic contract compiling process.\n", 274 | "# Requires that the creating account be unlocked.\n", 275 | "# Note that by default, the account will only be unlocked for 5 minutes (300s). \n", 276 | "# Specify a different duration in the geth personal.unlockAccount('acct','passwd',300) call, or 0 for no limit\n", 277 | "\n", 278 | "compiled = compile_source(source)\n", 279 | "compiledCode = compiled[':aggregator']['bin']\n", 280 | "compiledCode = '0x'+compiledCode # This is a hack which makes the system work\n", 281 | "\n", 282 | "addressList = [x[2:] for x in c.eth_accounts()]\n", 283 | "contractTx = c.create_contract(c.eth_coinbase(), compiledCode, gas=3000000,sig='aggregator(address[])',args=[addressList])\n", 284 | "# contractTx = c.create_contract(c.eth_coinbase(), compiledCode, gas=3000000,sig='greeter(string)',args=['Hello World!'])\n", 285 | "print(\"Contract transaction id is \"+contractTx)\n", 286 | "\n", 287 | "print(\"Waiting for the contract to be mined into the blockchain...\")\n", 288 | "while c.eth_getTransactionReceipt(contractTx) is None:\n", 289 | " time.sleep(1)\n", 290 | "\n", 291 | "contractAddr = c.get_contract_address(contractTx)\n", 292 | "print(\"Contract address is \"+contractAddr)" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 45, 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "data": { 302 | "text/plain": [ 303 | "[3]" 304 | ] 305 | }, 306 | "execution_count": 45, 307 | "metadata": {}, 308 | "output_type": "execute_result" 309 | } 310 | ], 311 | "source": [ 312 | "c.call('0x3f211b2256bc7b64f365cbc7bbff4ae77e22a151','indexer()',[],['int8'])" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 55, 318 | "metadata": {}, 319 | "outputs": [ 320 | { 321 | "data": { 322 | "text/plain": [ 323 | "[350]" 324 | ] 325 | }, 326 | "execution_count": 55, 327 | "metadata": {}, 328 | "output_type": "execute_result" 329 | } 330 | ], 331 | "source": [ 332 | "c.call('0x3f211b2256bc7b64f365cbc7bbff4ae77e22a151','names(uint256)',[2],['uint256'])" 333 | ] 334 | }, 335 | { 336 | "cell_type": "markdown", 337 | "metadata": {}, 338 | "source": [ 339 | "# Other Stuff\n", 340 | "Stackoverflow question at https://stackoverflow.com/questions/44373531/contract-method-not-returning-the-value-while-using-ethjsonrpc-and-pyethapp" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 132, 346 | "metadata": {}, 347 | "outputs": [ 348 | { 349 | "ename": "OSError", 350 | "evalue": "[Errno 2] No such file or directory", 351 | "output_type": "error", 352 | "traceback": [ 353 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 354 | "\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)", 355 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 14\u001b[0m \"\"\"\n\u001b[1;32m 15\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0mcompiled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcompile_source\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msource\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[0;31m# compiled = compile_files(['Solidity/ethjsonrpc_tutorial.sol']) #Note: Use this to compile from a file\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mcompiledCode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcompiled\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'Example'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'bin'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 356 | "\u001b[0;32m/Users/emunsing/Documents/Coding/github/energyblockchain/env/lib/python2.7/site-packages/solc/main.pyc\u001b[0m in \u001b[0;36mcompile_source\u001b[0;34m(source, allow_empty, output_values, **kwargs)\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mcompiler_kwargs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstdin\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msource\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcombined_json\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcombined_json\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 110\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 111\u001b[0;31m \u001b[0mstdoutdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstderrdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommand\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mproc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolc_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mcompiler_kwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 112\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 113\u001b[0m \u001b[0mcontracts\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_parse_compiler_output\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstdoutdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 357 | "\u001b[0;32m/Users/emunsing/Documents/Coding/github/energyblockchain/env/lib/python2.7/site-packages/solc/utils/string.pyc\u001b[0m in \u001b[0;36minner\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mfunctools\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwraps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 85\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mforce_obj_to_text\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 86\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0minner\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 358 | "\u001b[0;32m/Users/emunsing/Documents/Coding/github/energyblockchain/env/lib/python2.7/site-packages/solc/wrapper.pyc\u001b[0m in \u001b[0;36msolc_wrapper\u001b[0;34m(solc_binary, stdin, help, version, add_std, combined_json, optimize, optimize_runs, libraries, output_dir, gas, assemble, link, source_files, import_remappings, ast, ast_json, asm, asm_json, opcodes, bin, bin_runtime, clone_bin, abi, interface, hashes, userdoc, devdoc, formal, allow_paths, standard_json, success_return_code)\u001b[0m\n\u001b[1;32m 153\u001b[0m \u001b[0mstdin\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPIPE\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 154\u001b[0m \u001b[0mstdout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPIPE\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 155\u001b[0;31m stderr=subprocess.PIPE)\n\u001b[0m\u001b[1;32m 156\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 157\u001b[0m \u001b[0mstdoutdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstderrdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mproc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcommunicate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstdin\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 359 | "\u001b[0;32m/usr/local/Cellar/python/2.7.12/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.pyc\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)\u001b[0m\n\u001b[1;32m 709\u001b[0m \u001b[0mp2cread\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mp2cwrite\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 710\u001b[0m \u001b[0mc2pread\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mc2pwrite\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 711\u001b[0;31m errread, errwrite)\n\u001b[0m\u001b[1;32m 712\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 713\u001b[0m \u001b[0;31m# Preserve original exception in case os.close raises.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 360 | "\u001b[0;32m/usr/local/Cellar/python/2.7.12/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.pyc\u001b[0m in \u001b[0;36m_execute_child\u001b[0;34m(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, to_close, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite)\u001b[0m\n\u001b[1;32m 1341\u001b[0m \u001b[0;32mraise\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1342\u001b[0m \u001b[0mchild_exception\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpickle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1343\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mchild_exception\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1344\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1345\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 361 | "\u001b[0;31mOSError\u001b[0m: [Errno 2] No such file or directory" 362 | ] 363 | } 364 | ], 365 | "source": [ 366 | "source = \"\"\"pragma solidity ^0.4.2;\n", 367 | "contract Example {\n", 368 | " // Device Registry\n", 369 | " mapping (uint => string) public _registry;\n", 370 | " uint nIndex= 0; \n", 371 | " function set_s(string new_s) {\n", 372 | " _registry[nIndex] = new_s;\n", 373 | " nIndex = nIndex + 1;\n", 374 | " }\n", 375 | " function get_s(uint number) returns (string) {\n", 376 | " return _registry[number];\n", 377 | " } \n", 378 | "}\n", 379 | "\"\"\"\n", 380 | "\n", 381 | "compiled = compile_source(source)\n", 382 | "# compiled = compile_files(['Solidity/ethjsonrpc_tutorial.sol']) #Note: Use this to compile from a file\n", 383 | "compiledCode = compiled['Example']['bin']\n", 384 | "compiledCode = '0x'+compiledCode # This is a hack which makes the system work\n", 385 | "\n", 386 | "# Put the contract in the pool for mining, with a gas reward for processing\n", 387 | "contractTx = c.create_contract(c.eth_coinbase(), compiledCode, gas=3000000)\n", 388 | "print(\"Contract transaction id is \"+contractTx)\n", 389 | "\n", 390 | "print(\"Waiting for the contract to be mined into the blockchain...\")\n", 391 | "while c.eth_getTransactionReceipt(contractTx) is None:\n", 392 | " time.sleep(1)\n", 393 | "\n", 394 | "contractAddr = c.get_contract_address(contractTx)\n", 395 | "print(\"Contract address is \"+contractAddr)" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": 110, 401 | "metadata": { 402 | "collapsed": true 403 | }, 404 | "outputs": [], 405 | "source": [ 406 | "tx = c.call_with_transaction(c.eth_coinbase(), contractAddr, 'set_s(string)', ['Dinesh'])\n", 407 | "while c.eth_getTransactionReceipt(tx) is None:\n", 408 | " time.sleep(1)" 409 | ] 410 | } 411 | ], 412 | "metadata": { 413 | "kernelspec": { 414 | "display_name": "Python 2", 415 | "language": "python", 416 | "name": "python2" 417 | }, 418 | "language_info": { 419 | "codemirror_mode": { 420 | "name": "ipython", 421 | "version": 2 422 | }, 423 | "file_extension": ".py", 424 | "mimetype": "text/x-python", 425 | "name": "python", 426 | "nbconvert_exporter": "python", 427 | "pygments_lexer": "ipython2", 428 | "version": "2.7.12" 429 | } 430 | }, 431 | "nbformat": 4, 432 | "nbformat_minor": 2 433 | } 434 | -------------------------------------------------------------------------------- /Central Optimization Code.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from cvxpy import *\n", 12 | "# import gurobipy\n", 13 | "import numpy as np\n", 14 | "import scipy as sp\n", 15 | "import pandas as pd\n", 16 | "import cvxopt" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "metadata": { 23 | "collapsed": true 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "# Function definition for constraints\n", 28 | "def deferrableConstraints(p=np.asmatrix([1,2]).T,nT=24,earliestStart=1,latestStart=24):\n", 29 | "# function constraintStruct = deferrableConstraints(p,nT,earliestStart,latestStart)\n", 30 | " # p: vector of power consumption in each time period; tall and skinny\n", 31 | " # nT: Number of time steps in the study period (i.e. 24h in a day)\n", 32 | " # earliestStart: Integer number of time step for allowable start\n", 33 | " # latestEnd: Integer number of time time step for latest start,\n", 34 | "\n", 35 | " # For Testing\n", 36 | "# p = def_profile\n", 37 | "# nT=24\n", 38 | "# earliestStart=8\n", 39 | " latestStart=nT-len(p)\n", 40 | "\n", 41 | " L = nT\n", 42 | " G = len(p) # Number of steps in operation of the deferrable load\n", 43 | " lbar = 1 # For MPC/CEC, this is the time step we're currently considering\n", 44 | "\n", 45 | " dim1 = L - lbar + 1\n", 46 | " dim2 = L - lbar + min(lbar, G)\n", 47 | "\n", 48 | " c1 = np.zeros((dim1,1))\n", 49 | " c1[0] = p[0]\n", 50 | " r1 = np.vstack((p,np.zeros((dim2-G,1))))\n", 51 | " omega = sp.linalg.toeplitz(c1,r1)\n", 52 | "\n", 53 | " r1 = np.zeros((dim2,1))\n", 54 | " r1[0]= 1\n", 55 | " r1[1]= -1\n", 56 | " c1 = np.zeros((dim2,1))\n", 57 | " c1[0]= r1[0]\n", 58 | " y = sp.linalg.toeplitz(c1,r1)\n", 59 | "\n", 60 | " # y.T * omega# * d # This will produce the power consumption schedule for a given d \n", 61 | "\n", 62 | " # Set up the a-vector (the dispatch command vector)\n", 63 | " maxDelay = L - latestStart - earliestStart\n", 64 | " zeta = maxDelay #zeta term from eqn (13b)\n", 65 | "\n", 66 | " a = np.vstack((np.zeros((earliestStart-1,1)),np.ones((L-earliestStart+1,1)))) # Not sure what these are doing\n", 67 | " a_shift = np.vstack((np.zeros((earliestStart+zeta,1)),np.ones((latestStart,1))))\n", 68 | "\n", 69 | " # Constraint matrix for d(l-1)<=d(l), or d(l-1)-d(l)<=0.\n", 70 | " # Assumes that we prepend 0 to d for this constraint.\n", 71 | " A_increasing = sp.linalg.toeplitz(np.vstack((1,np.zeros((L-1,1)))),np.vstack((1,-1,np.zeros((L-1,1)))))\n", 72 | " # A_increasing = toeplitz([1;zeros(L-1,1)],[1;-1;zeros(L-1,1)]);\n", 73 | "\n", 74 | " constraintStruct = {}\n", 75 | " constraintStruct['y'] = y\n", 76 | " constraintStruct['omega'] = omega\n", 77 | " constraintStruct['A_increasing'] = A_increasing\n", 78 | " constraintStruct['a'] = a\n", 79 | " constraintStruct['a_shift'] = a_shift\n", 80 | "\n", 81 | " return constraintStruct" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 28, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "# Network Data\n", 91 | "\n", 92 | "# Base values for Test Network\n", 93 | "s_base = 1.0e3 #VA\n", 94 | "v_base = 12.35e3 #V\n", 95 | "z_base = (v_base**2)/s_base\n", 96 | "\n", 97 | "# Test Network Data\n", 98 | "basic_data = pd.read_csv('Data/basic_data.csv',header=None)#, columns=['f','t','r','x'])\n", 99 | "basic_data.columns = ['f','t','r','x']\n", 100 | "\n", 101 | "cutNode = len(basic_data); # include the whole network\n", 102 | "cutNode = 17; # Cut the network off after this node for faster run time. Good cuts: 17. Using Python indexing this is still 17, due to indexing!\n", 103 | "\n", 104 | "# Convert into a model of the network\n", 105 | "f = np.array(basic_data['f'].iloc[:cutNode])-1\n", 106 | "t = np.array(basic_data['t'].iloc[:cutNode])-1\n", 107 | "r = np.array(basic_data['r'].iloc[:cutNode])\n", 108 | "x = np.array(basic_data['x'].iloc[:cutNode])\n", 109 | "r = np.insert(r,0,0)/z_base\n", 110 | "x = np.insert(x,0,0)/z_base\n", 111 | "R = np.diag(r)\n", 112 | "X = np.diag(x)\n", 113 | "Rsub = R[1:,1:] # need these for constraints later\n", 114 | "Xsub = X[1:,1:]\n", 115 | "\n", 116 | "# Network Properties\n", 117 | "nb = np.int64(max(t)+1) # Number of buses\n", 118 | "nl = np.int64(len(t)) # Number of lines\n", 119 | "\n", 120 | "# parent = sparse(t,f,ones(nl,1),nb,nb); #column is the parent of row\n", 121 | "# child = sparse(f,t,ones(nl,1),nb,nb); #column is the child of row (interestingly child = parent').\n", 122 | "\n", 123 | "from scipy.sparse import coo_matrix\n", 124 | "\n", 125 | "#column is the parent of row\n", 126 | "parent = coo_matrix((np.ones((nl,)), (t, f)), shape=(nb,nb)).toarray() # Column is the parent of this row # Lose the to array if dealing with big matrices and use the Constant function for cvxpy.\n", 127 | "child = coo_matrix((np.ones((nl,)), (f, t)), shape=(nb,nb)).toarray() # Column is the child of this row (interestingly, child = parent')\n", 128 | "parent = parent.astype(int)\n", 129 | "child = child.astype(int)\n", 130 | "# parent = Constant(parent)\n", 131 | "# child = Constant(child)\n", 132 | "\n", 133 | "# Time Data\n", 134 | "T = np.int64(24) # if there is no time coupling, leave at 1 for low solve time.\n", 135 | "dt = np.int64(1) # time step in hours\n", 136 | "\n", 137 | "# Generator cost coefficients - random for now\n", 138 | "# alpha = zeros(nb,1);\n", 139 | "alpha = (0.1 + (0.2-0.1)*np.random.rand(nb,1))*s_base**2\n", 140 | "beta = (10 + (20-10)*np.random.rand(nb,1))*s_base\n", 141 | "gamma = 100 + (200-100)*np.random.rand(nb,1)\n", 142 | "\n", 143 | "# Price of electricity from the grid (if we want grid-connected)\n", 144 | "LMP_price = np.array(pd.read_csv('Data/EnergyPrice.csv',header=None))\n", 145 | "price = LMP_price / max(LMP_price) # Scale to [0,1]\n", 146 | "\n", 147 | "import numpy.random\n", 148 | "g_min = np.zeros((nb,T)) # These are setup to give feeder lots of power, other buses less so.\n", 149 | "g_max = (0.5e3 + (1e3-0.5e3)*np.random.rand(nl,1))\n", 150 | "g_max = np.insert(g_max,0,nl*10e3)/s_base\n", 151 | "import numpy.matlib\n", 152 | "g_max = np.matlib.repmat(np.asmatrix(g_max).transpose(),1,T)\n", 153 | "\n", 154 | "# Load Data - random for now\n", 155 | "fixed_load_p = np.vstack((np.zeros((1,T)),(2e3 + (10e3-2e3)*np.random.rand(nl,T))))/s_base\n", 156 | "fixed_load_q = np.vstack((np.zeros((1,T)),(100 + (200-1002)*np.random.rand(nl,T))))/s_base\n", 157 | "\n", 158 | "\n", 159 | "# Scale loads using the price profile, with random noise 20% of the signal\n", 160 | "fixed_load_p = 0.3 * fixed_load_p + 0.7 * np.dot(np.asmatrix(np.mean(fixed_load_p,axis=1)).T,price.transpose())\n", 161 | "fixed_load_q = 0.3 * fixed_load_q + 0.7 * np.dot(np.asmatrix(np.mean(fixed_load_q,axis=1)).T,price.transpose())\n", 162 | "\n", 163 | "nbOnes = np.ones((nb, 1)) # Use this to turn scalars into bus-vectors\n", 164 | "ntOnes = np.ones((T,1)) # Use this to turn scalars into time-vectors\n", 165 | "loadScale = 0.001;\n", 166 | "\n", 167 | "# Solar data\n", 168 | "p_sol = pd.read_csv('Data/SolarOutput.csv',header=None) # Just take solar output (not hours)\n", 169 | "p_sol = p_sol[1] / max(p_sol[1]) # Normalize profile\n", 170 | "p_sol = np.matlib.repmat(np.asmatrix(p_sol),nb,1) # Stack for each node\n", 171 | "solar_probability = 0.6\n", 172 | "solar_present = np.round(solar_probability - 0.5 + (np.random.rand(nb,1)))\n", 173 | "max_sol = 1.5*np.multiply(np.max(fixed_load_p,axis=1),solar_present)# 1.5 is the factor by which the solar panel is designed to exceed demand;\n", 174 | "p_sol = np.dot(np.diag(np.asarray(max_sol).flatten()),p_sol) # Scale solar output by the max power of each node\n", 175 | "\n", 176 | "# Deferrable Load \n", 177 | "def_profile = np.asmatrix([0.1, 1.5, 2.0, 0.5, 0.5, 1.0]).T # Demand profile for the deferrable load\n", 178 | "# def_profile= np.asmatrix([1,2]).T # Simple test load\n", 179 | "def_profile = def_profile / max(def_profile)\n", 180 | "def_profile = def_profile*np.mean(np.max(fixed_load_p,axis=1)) # Scale so that the fixed load maximum is the same as the average household maximum\n", 181 | "steps = len(def_profile)\n", 182 | "defConst = deferrableConstraints(def_profile, T, 8) # Generate constraints in form (p, T, ,earliestStart,latestStart)\n", 183 | "\n", 184 | "# Battery parameters\n", 185 | "eff_in = 0.95 # Battery efficiency\n", 186 | "eff_out = 0.95 \n", 187 | "p_battmax = np.max(fixed_load_p,axis=1)*0.5;\n", 188 | "hours = 4\n", 189 | "battsize = hours * p_battmax # Battery size in Watts*dt (i.e. Wh if dt=1 hour)\n", 190 | "E_min = 0.2 * battsize # SOC lower limits on batteries\n", 191 | "E_max = 0.8 * battsize # SOC lower limits on batteries\n", 192 | "# E_init = 0.5 * battsize # Starting state of charge\n", 193 | "eps = 0.001 # Acceptable band for ending SOC \n", 194 | "\n", 195 | "# Shapeable load parameters\n", 196 | "P_shpmin = 0 * np.max(fixed_load_p,axis=1)\n", 197 | "P_shpmax = 2 * np.max(fixed_load_p,axis=1)\n", 198 | "E_dem = 3 * np.multiply(np.random.rand(nb,1),P_shpmax) # Effectively how many hours of demand we have\n", 199 | "minStartTime = 9\n", 200 | "minStartTime = min(T-4,minStartTime) # Deal with short time horizons\n", 201 | "startProbPeriod = 9 # Start times will be uniformly distributed over this range\n", 202 | "startAfter = minStartTime + np.round(startProbPeriod * np.random.rand(nb,1).flatten())\n", 203 | "chargeTime = 4 + np.round(min(6,T-4) * np.random.rand(nb,1).flatten())\n", 204 | "endBy = np.minimum(startAfter + chargeTime,T)\n", 205 | "\n", 206 | "# Create a matrix where infeasible hours are 0 - buses in rows, times in columns\n", 207 | "selectShp = np.zeros((nb,T))\n", 208 | "for i in range(nb):# = 1:nb:\n", 209 | " tooEarly = np.ones( ( 1,int(startAfter[i]) ) )\n", 210 | " okTimes = np.zeros( ( 1,int(endBy[i]-startAfter[i]) ) )\n", 211 | " tooLate = np.ones( ( 1,int(T-endBy[i]) ) )\n", 212 | " selectShp[i,:] = np.hstack(( tooEarly, okTimes, tooLate))\n" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 40, 218 | "metadata": {}, 219 | "outputs": [ 220 | { 221 | "name": "stdout", 222 | "output_type": "stream", 223 | "text": [ 224 | "Objective value: 30014.079\n" 225 | ] 226 | } 227 | ], 228 | "source": [ 229 | "### Simple OPF Problem ###\n", 230 | "# Dispatchable generators, linear generation costs, \n", 231 | "# Fixed loads, no deferrable or shapeable loads\n", 232 | "\n", 233 | "nb = int(nb)\n", 234 | "T = int(T)\n", 235 | "\n", 236 | "# Line flow variables\n", 237 | "P = Variable(nb,T) # Real power flow from node to parent\n", 238 | "Q = Variable(nb,T) # Reactive power flow from node to parent\n", 239 | "# l = Variable(nb,T)\n", 240 | "v = Variable(nb,T) # squared voltage at node\n", 241 | "\n", 242 | "# Dummy variables\n", 243 | "p_g = Variable(nb,T) # Generation real power\n", 244 | "q_g = Variable(nb,T) # Generation reactive power\n", 245 | "p = Variable(nb,T) # Nodal net power injection\n", 246 | "q = Variable(nb,T)\n", 247 | "# temp_var = Variable(3,T)\n", 248 | "\n", 249 | "alpha = np.random.rand(nb,1)\n", 250 | "beta = 20*np.random.rand(nb,1)\n", 251 | "obj = 0\n", 252 | "for t in range(T):\n", 253 | "# obj = obj + beta.T*p_g[:,t]\n", 254 | " obj = obj+alpha.T*(square(p_g[:,t]))+beta.T*p_g[:,t]\n", 255 | " \n", 256 | "objective = Minimize(obj)\n", 257 | "constraints = []\n", 258 | "\n", 259 | "# Linearized Power Flow Equations\n", 260 | "constraints.append(p == P - child*P) # Lambda (prices) associated with this equation\n", 261 | "constraints.append(q == Q - child*Q)\n", 262 | "constraints.append(v[1:,:] == parent[1:,:]*v+2*(Rsub*P[1:,:]+Xsub*Q[1:,:]))\n", 263 | "\n", 264 | "# Injection Equations - Simple\n", 265 | "constraints.append(p == p_g - fixed_load_p)\n", 266 | "constraints.append(q == q_g - fixed_load_q)\n", 267 | "\n", 268 | "# Voltage constraints\n", 269 | "constraints.append(v <= 1.05)\n", 270 | "constraints.append(v >= 0.95)\n", 271 | "# Generator constraints\n", 272 | "constraints.append(g_min <= p_g)\n", 273 | "constraints.append(p_g <= g_max)\n", 274 | "\n", 275 | "# Root node constraints (root voltage is slack)\n", 276 | "constraints.append(P[0,:] == 0)\n", 277 | "constraints.append(Q[0,:] == 0)\n", 278 | "\n", 279 | "prob = Problem(objective, constraints)\n", 280 | "prob.solve(solver=ECOS)\n", 281 | "\n", 282 | "# Solve it!\n", 283 | "result = prob.solve()\n", 284 | "print(\"Objective value: %.3f\"%prob.value)\n", 285 | "# print(\"Value of p: %s\"%np.round(p.value,3).T.tolist()[0]) \n", 286 | "\n", 287 | "# Pull out values like\n", 288 | "# P.value[1,:] # Power flow from bus 1 to parent over every hour\n" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": 38, 294 | "metadata": {}, 295 | "outputs": [ 296 | { 297 | "name": "stdout", 298 | "output_type": "stream", 299 | "text": [ 300 | "Objective value: 104215.059\n" 301 | ] 302 | } 303 | ], 304 | "source": [ 305 | "### FULL SMARTGRID PROBLEM ####\n", 306 | "# Deferable and shapeable loads\n", 307 | "\n", 308 | "# Set up constants\n", 309 | "# a = np.array([1,1]).T\n", 310 | "# b = 3\n", 311 | "# c = np.array([-2,1]).T\n", 312 | "# rho = np.array([0.5,0.5]);\n", 313 | "\n", 314 | "# n = max(a.shape)\n", 315 | "\n", 316 | "# Set up CVXPy problem\n", 317 | "\n", 318 | "nb = int(nb)\n", 319 | "T = int(T)\n", 320 | "\n", 321 | "# Opt variables\n", 322 | "P = Variable(nb,T)\n", 323 | "Q = Variable(nb,T)\n", 324 | "l = Variable(nb,T)\n", 325 | "v = Variable(nb,T)\n", 326 | "\n", 327 | "# Dummy variables\n", 328 | "p_g = Variable(nb,T)\n", 329 | "q_g = Variable(nb,T)\n", 330 | "p = Variable(nb,T)\n", 331 | "q = Variable(nb,T)\n", 332 | "\n", 333 | "# Deferrable decision variable\n", 334 | "# d = Variable(nb,T) # nonnegative\n", 335 | "# Battery variables\n", 336 | "p_bc = Variable(nb,T)\n", 337 | "p_bd = Variable(nb,T)\n", 338 | "E_batt = Variable(nb,T)\n", 339 | "# Shapeable load variables\n", 340 | "p_shp = Variable(nb,T) #nonnegative\n", 341 | "\n", 342 | "# p_def = np.zeros((nb,T))\n", 343 | "# for i in range(nb):\n", 344 | "# p_def[i,:] = (np.dot(defConst['y'].T,defConst['omega'].T)*d[i,:].T).T # Deferrable load calculation\n", 345 | "\n", 346 | "# p_def = d*(np.dot(defConst['y'].T,defConst['omega'].T))\n", 347 | "p_batt = p_bd-p_bc # batteries\n", 348 | "\n", 349 | "alpha = np.random.rand(nb,1)\n", 350 | "beta = np.vstack((LMP_price.T,20*np.random.rand(nb-1,T)))\n", 351 | "obj = 0\n", 352 | "for t in range(T):\n", 353 | "# obj = obj + beta.T*p_g[:,t]\n", 354 | " obj = obj+alpha.T*(square(p_g[:,t]))+beta[:,t].T*p_g[:,t]\n", 355 | " \n", 356 | "objective = Minimize(obj)\n", 357 | "constraints = []\n", 358 | "\n", 359 | "# # Power Flow Equations\n", 360 | "# constraints.append(p == P - child*P + R*l) # Lambda (prices) associated with this equation\n", 361 | "# constraints.append(q == Q - child*Q + X*l)\n", 362 | "# constraints.append(v[1:,:] == parent.astype(int)[1:,:]*v+2*(Rsub*P[1:,:]+Xsub*Q[1:,:])-(Rsub*Rsub+Xsub*Xsub)*l[1:,:])\n", 363 | "\n", 364 | "# Linearized Power Flow Equations\n", 365 | "constraints.append(p == P - child*P) # Lambda (prices) associated with this equation\n", 366 | "constraints.append(q == Q - child*Q)\n", 367 | "constraints.append(v[1:,:] == parent[1:,:]*v+2*(Rsub*P[1:,:]+Xsub*Q[1:,:]))\n", 368 | "\n", 369 | "# for t in np.arange(T):\n", 370 | "# for i in np.arange(1,nb):\n", 371 | "# # constraints.append(l[i,t]+v[i,t] >= norm(np.vstack((2*P[i,t],2*Q[i,t],l[i,t]-v[i,t])),2))\n", 372 | "# temp_square = square(2*P[i,t])+square(2*Q[i,t])+square(l[i,t]-v[i,t])\n", 373 | "# constraints.append(l[i,t]+v[i,t] >= sqrt(temp_square))#norm([[2*P[i,t]],[2*Q[i,t]],[l[i,t]-v[i,t]]],2))\n", 374 | "# [[2*P[i,t]],[2*Q[i,t]],[l[i,t]-v[i,t]]]\n", 375 | " #l(i,t) >= (P(i,t)^2+Q(i,t)^2)/v(i,t);\n", 376 | "# [2*P[i,t];2*Q[i,t];l[i,t]-v[i,t]]\n", 377 | "\n", 378 | "# Injection Equations\n", 379 | "# constraints.append(p == p_g + p_sol + p_batt - p_def - p_shp - fixed_load_p) # Uncomment this to include solar\n", 380 | "constraints.append(p == p_g + p_sol + p_batt - p_shp - fixed_load_p) # Uncomment this to include solar\n", 381 | "\n", 382 | "# p == p_g + p_batt - p_def - p_shp - fixed_load_p; % This assumes no solar\n", 383 | "constraints.append(q == q_g - fixed_load_q)\n", 384 | "\n", 385 | "# # Injection Equations - Simple\n", 386 | "# constraints.append(p == p_g - fixed_load_p)\n", 387 | "# constraints.append(q == q_g - fixed_load_q)\n", 388 | "\n", 389 | "# Voltage cons\n", 390 | "constraints.append(v <= 1.05)\n", 391 | "constraints.append(v >= 0.95)\n", 392 | "# Gen cons\n", 393 | "constraints.append(g_min <= p_g)\n", 394 | "constraints.append(p_g <= g_max)\n", 395 | "# Root node cons (root voltage is slack)\n", 396 | "constraints.append(P[0,:] == 0)\n", 397 | "constraints.append(Q[0,:] == 0)\n", 398 | "\n", 399 | "# # Deferrable loads\n", 400 | "# for i in range(nb):\n", 401 | "# constraints.append(defConst['A_increasing'] * np.insert(d[i,:].T,0,0) <= 0) # d(l-1) <= d(l), eqn (13a)\n", 402 | "# constraints.append(d[i,:].T <= defConst['a']) # d(l) <= a(l) , eqn (13a)\n", 403 | "# constraints.append(defConst['a_shift'] <= d[i,:].T) # a(l-zeta) <= d(l), eqn (13b)\n", 404 | "# constraints.append(d[i,0]==0) # d(0) == 0\n", 405 | "# constraints.append(d[i,T]== defConst['a'][T]) # d(L) == a(L) eqn (13c)\n", 406 | " \n", 407 | "# # Nonnegative constraints\n", 408 | "# constraints.append(d >= 0)\n", 409 | " \n", 410 | "# Batteries\n", 411 | "constraints.append(0 <= p_bd)\n", 412 | "constraints.append(p_bd<= p_battmax * ntOnes.T)\n", 413 | "constraints.append(0 <= p_bc)\n", 414 | "constraints.append(p_bc<= p_battmax * ntOnes.T)\n", 415 | "constraints.append(E_min * ntOnes.T <= E_batt)\n", 416 | "constraints.append(E_batt <= E_max * ntOnes.T)\n", 417 | "\n", 418 | "constraints.append(E_batt[:,0] == E_batt[:,T-1] + p_bc[:,0] * dt * eff_in - p_bd[:,0]*dt/eff_out)\n", 419 | "for t in np.arange(1,T):\n", 420 | " constraints.append(E_batt[:,t] == E_batt[:,t-1] + p_bc[:,t] * dt * eff_in - p_bd[:,t]*dt/eff_out)\n", 421 | " \n", 422 | "# constraints.append(E_batt[:,1] == E_init)\n", 423 | "constraints.append((1-eps)*E_batt[:,0] <= E_batt[:,T-1])# <= (1+eps)*E_batt[:,0])\n", 424 | "constraints.append(E_batt[:,T-1] <= (1+eps)*E_batt[:,0])\n", 425 | "\n", 426 | "# Shapeable loads\n", 427 | "constraints.append(P_shpmin * ntOnes.T <= p_shp)\n", 428 | "constraints.append(p_shp <= P_shpmax * ntOnes.T)\n", 429 | "for i in np.arange(nb):\n", 430 | " constraints.append(ntOnes.T * p_shp[i,:].T == E_dem[i]) # Total energy consumption\n", 431 | " constraints.append(selectShp[i,:] * p_shp[i,:].T == 0) # No operation during non-allowed hours\n", 432 | "\n", 433 | "# Nonnegative constraints\n", 434 | "constraints.append(p_shp >= 0)\n", 435 | "\n", 436 | "prob = Problem(objective, constraints)\n", 437 | "prob.solve(solver=ECOS)\n", 438 | "\n", 439 | "# Solve it!\n", 440 | "result = prob.solve()\n", 441 | "print(\"Objective value: %.3f\"%prob.value)" 442 | ] 443 | }, 444 | { 445 | "cell_type": "code", 446 | "execution_count": 139, 447 | "metadata": {}, 448 | "outputs": [ 449 | { 450 | "data": { 451 | "image/png": "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\n2N8AZpyx7SFgmZRyKLCs+bHXKioqIjIyEl9fX8A+MSk2NhaARYsWtZTt3bPHXnly48aNjBs3jszM\nTC688EL27t0LwBtvvMFVV13FpEmTGDp0KI8//njLOd5++23GjBlDRkYGt912W7uTopS2WY1Gt/fY\nWwK7SsUoHqozS+OtFEIknrH5KmBS8/03gRVAtyt4FS9cSGO+c8v2+o4cQcwjj5xzn2nTpvHEE08w\nbNgwLrnkEmbPns3FF18M2IP81q1beemll3j22Wd59dVXGTFiBKtWrUKn0/HTTz/xyCOP8MknnwD2\noL9r1y4CAgLIyclh5syZBAYG8sEHH7BmzRr0ej133nkn77zzDjfddJNT32tv5s5a7A4aHx80gYHq\n4qnisbo7QamflLKo+X4x0K+9HYUQtwK3AiQkJHTztD0jKCiILVu2sGrVKn7++Wdmz57N008/DcC1\n114LwOjRo/n0008BexneOXPmsH//foQQLcW+AC699NKW0rvXXnstq1evRqfTsWXLFnJycgB7bZro\n6GhXvkWvJm02t66edDo1+1TxZE6beSqllEIIeY7nFwOLAbKzs9vdDzhvz7onabXallouqampvPnm\nmwAt6RmtVtuynN2f//xnJk+ezGeffUZBQUGr+i9njtoQQiClZM6cOTz11FOueTO9jK26Gmw2jwjs\nOoNBpWIUj9XdUTElQoj+AM23pd1vkvvs3buX/fv3tzzOzc1l4MCB7e5fWVnJgOYFH954441Wz/34\n449UVFRQX1/P559/zvjx45k6dSoff/wxpaX2X1NFRUVLCWDl/E7NOnXvxVNoLgSmUjGKh+puYP8S\nmNN8fw7wRTeP51Y1NTXMmTOH5ORk0tLSyMvL47HHHmt3/wceeICHH36YzMzMsxalHjNmDNdddx1p\naWlcd911ZGdnk5yczF//+lemTZtGWloal156KUVFRe0cXTmTJ8w6ddAawlUqRvFYnRnu+B72C6WR\nQojjwKPA08CHQohbgCPADT3RSFcZPXo0a9euPWt7QUFBy/3s7GxWrFgB2FdZ2rdvX8tzf/3rX1vu\nx8XFtVl7ffbs2cyePdt5je5DLB5Q2dFBZzBgMRqRUrp1spSitKUzo2J+085TU53UFkU5J6uxObB7\nQo893ABmM7aaGrTBwe5ujqK0osr29oC5c+cyd+5cdzej12mpxe4Jgb159qm1vFwFdsXjqJICitew\n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\nzCOTI5dcMMcAAAAASUVORK5CYII=\n", 452 | "text/plain": [ 453 | "" 454 | ] 455 | }, 456 | "metadata": {}, 457 | "output_type": "display_data" 458 | } 459 | ], 460 | "source": [ 461 | "# p_g + p_sol + p_batt - p_shp - fixed_load_p\n", 462 | "# p_g\n", 463 | "# E_batt.value.astype(int)\n", 464 | "# p.value.astype(int)\n", 465 | "\n", 466 | "import matplotlib.pyplot as plt\n", 467 | "%matplotlib inline \n", 468 | "\n", 469 | "node = 3\n", 470 | "plt.plot(np.arange(T),p_g[node,:].value.T,label='Gen')\n", 471 | "plt.plot(np.arange(T),-p_sol[node,:].T,label='Solar')\n", 472 | "plt.plot(np.arange(T),-p_batt[node,:].value.T,label='Battery')\n", 473 | "plt.plot(np.arange(T),p_shp[node,:].value.T,label='Shape')\n", 474 | "plt.plot(np.arange(T),fixed_load_p[node,:].T,label='Fixed')\n", 475 | "# plt.legend([a,b,c,d,e], ['Gen', 'Solar','Battery','Shape','Fixed'])\n", 476 | "plt.legend(loc=2)\n", 477 | "# ax.set_xticklabels(end.strftime('%m-%Y'),size = 'small')\n", 478 | "# plt.xticks(end.strftime('%m%Y'))#,size='small')\n", 479 | "# plt.xlabel('Date')\n", 480 | "# plt.ylabel('Number of Members')\n", 481 | "plt.show()" 482 | ] 483 | } 484 | ], 485 | "metadata": { 486 | "kernelspec": { 487 | "display_name": "Python 2", 488 | "language": "python", 489 | "name": "python2" 490 | }, 491 | "language_info": { 492 | "codemirror_mode": { 493 | "name": "ipython", 494 | "version": 2 495 | }, 496 | "file_extension": ".py", 497 | "mimetype": "text/x-python", 498 | "name": "python", 499 | "nbconvert_exporter": "python", 500 | "pygments_lexer": "ipython2", 501 | "version": "2.7.12" 502 | } 503 | }, 504 | "nbformat": 4, 505 | "nbformat_minor": 2 506 | } 507 | --------------------------------------------------------------------------------