{ "metadata": { "name": "", "signature": "sha256:233382bde1f176cfcd518d4000c854e372cbc6e9773ad47abd19ed5f4d3d877e" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "EnergyParser" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook shows the basic functionality of loading an IDF file and operating on it in XML. " ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I'm using Python 2.7.5 (default, Sep 16 2013, 23:11:01) [MSC v.1500 64 bit (AMD64)]\n", "However, I always use the \"from __future__ import division\" and \"from __future__ import print_function\" to make transition easy" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print('Python ' + sys.version)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Python 2.7.5 (default, Sep 16 2013, 23:11:01) [MSC v.1500 64 bit (AMD64)]\n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Extensive use of logging to track what's happening each step, if this is not clear read up on logging" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import logging\n", "logging.basicConfig(format='%(funcName)-20s %(levelno)-3s: %(message)s', level=logging.DEBUG, datefmt='%I:%M:%S')\n", "logging.debug('Test logging')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ " 10 : Test logging\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's how I'm loading the module into IPython notebook " ] }, { "cell_type": "code", "collapsed": false, "input": [ "sys.path.append(r'C:\\EclipseWorkspace\\EnergyParser')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Load the module containing the IDF class" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import idf.idf_parser as idf\n", "#idf.IDF?" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Basic loading" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's what's happening below:
\n", "Load an .idf file from a path on disk
\n", "the from_IDF_file is the common way to instantiate
\n", "from_IDF_file calls:
\n", "1. load_IDF() which loads file into attribute IDF_string
\n", "2. parse_IDF_to_XML() which parses the IDF_string into attribute XML - self.XML is a lxml tree object!\n", "3. A random 'ID' string is created so you can track what's happening with multiple instances of the class" ] }, { "cell_type": "code", "collapsed": false, "input": [ "path_test_idf = r\"C:\\EclipseWorkspace\\EnergyParser\\SampleIDFs\\5ZoneElectricBaseboard.idf\"\n", "new_idf = idf.IDF.from_IDF_file(path_test_idf)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "load_IDF 10 : NhXk: Loaded IDF C:\\EclipseWorkspace\\EnergyParser\\SampleIDFs\\5ZoneElectricBaseboard.idf with 3679 lines\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "parse_IDF_to_XML 10 : NhXk: Converted IDF to XML: , 348 objects\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "from_IDF_file 10 : NhXk: Created an IDF object named NhXk, with 348 objects\n" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "print(new_idf)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "IDF:NhXk, IDF Lines:3679, XML Objects:348, XML_root:\n" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is some raw IDF text in the IDF_string attribute" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for line in new_idf.IDF_string.split('\\n')[987:1010]:\n", " print(line)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ " BuildingSurface:Detailed,\n", " C1-1P, !- Name\n", " FLOOR, !- Surface Type\n", " CLNG-1, !- Construction Name\n", " PLENUM-1, !- Zone Name\n", " Surface, !- Outside Boundary Condition\n", " C1-1, !- Outside Boundary Condition Object\n", " NoSun, !- Sun Exposure\n", " NoWind, !- Wind Exposure\n", " 0.0, !- View Factor to Ground\n", " 4, !- Number of Vertices\n", " 26.8,3.7,2.4, !- X,Y,Z ==> Vertex 1 {m}\n", " 30.5,0.0,2.4, !- X,Y,Z ==> Vertex 2 {m}\n", " 0.0,0.0,2.4, !- X,Y,Z ==> Vertex 3 {m}\n", " 3.7,3.7,2.4; !- X,Y,Z ==> Vertex 4 {m}\n", "\n", " BuildingSurface:Detailed,\n", " C2-1P, !- Name\n", " FLOOR, !- Surface Type\n", " CLNG-1, !- Construction Name\n", " PLENUM-1, !- Zone Name\n", " Surface, !- Outside Boundary Condition\n", " C2-1, !- Outside Boundary Condition Object\n" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "And here is the raw XML tree:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for cnt,line in enumerate(new_idf.XML):\n", " print(line)\n", " if cnt > 8: break" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] } ], "prompt_number": 8 }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Basic introspection and printing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the core IDF class loaded, it's time to inspect and manipulate it" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import idf.utilities_xml as util_xml" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convenience functions exist for listing objects, below zone names" ] }, { "cell_type": "code", "collapsed": false, "input": [ "util_xml.get_zone_name_list(new_idf)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 10, "text": [ "['PLENUM-1', 'SPACE1-1', 'SPACE2-1', 'SPACE3-1', 'SPACE4-1', 'SPACE5-1']" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using 'PrettyTable 0.5' module, nicely formatted summaries are possible
\n", "Below, a full listing of all classes and their names, and then a table showing the idf class and how many instances of each class exist\n", "The print_table utility function has an argument for the number of rows, remove it to show all" ] }, { "cell_type": "code", "collapsed": false, "input": [ "table = util_xml.get_table_all_names(new_idf)\n", "util_xml.print_table(table,5)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "+--------------------------------------------+-----------------------+\n", "| Class | Name |\n", "+--------------------------------------------+-----------------------+\n", "| AirLoopHVAC | VAV Sys 1 |\n", "| AirLoopHVAC:ControllerList | OA Sys 1 Controllers |\n", "| AirLoopHVAC:ControllerList | VAV Sys 1 Controllers |\n", "| AirLoopHVAC:OutdoorAirSystem | OA Sys 1 |\n", "| AirLoopHVAC:OutdoorAirSystem:EquipmentList | OA Sys 1 Equipment |\n", "+--------------------------------------------+-----------------------+\n" ] } ], "prompt_number": 11 }, { "cell_type": "code", "collapsed": false, "input": [ "table = util_xml.get_table_object_count(new_idf)\n", "util_xml.print_table(table, 10)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "+--------------------------------------------+-------+\n", "| Class | Count |\n", "+--------------------------------------------+-------+\n", "| AirLoopHVAC | 1 |\n", "| AirLoopHVAC:ControllerList | 2 |\n", "| AirLoopHVAC:OutdoorAirSystem | 1 |\n", "| AirLoopHVAC:OutdoorAirSystem:EquipmentList | 1 |\n", "| AirLoopHVAC:ReturnPath | 1 |\n", "| AirLoopHVAC:ReturnPlenum | 1 |\n", "| AirLoopHVAC:SupplyPath | 1 |\n", "| AirLoopHVAC:ZoneSplitter | 1 |\n", "| AirTerminal:SingleDuct:VAV:NoReheat | 2 |\n", "| AirTerminal:SingleDuct:VAV:Reheat | 3 |\n", "+--------------------------------------------+-------+\n" ] } ], "prompt_number": 12 }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we see that there are 3 'AirTerminal:SingleDuct:VAV:Reheat' objects. A selection can be made around all instances matching a class name." ] }, { "cell_type": "code", "collapsed": false, "input": [ "util_xml.tree_get_class(new_idf, 'AirTerminal:SingleDuct:VAV:Reheat')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "tree_get_class 10 : Search of ^AirTerminal:SingleDuct:VAV:Reheat$ 3 hits in \n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 13, "text": [ "[,\n", " ,\n", " ]" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Regular expressions are fully supported in the code (note the '^' and '$' sigils!)
\n", "By default, an exact '^$' match" ] }, { "cell_type": "code", "collapsed": false, "input": [ "selection = util_xml.tree_get_class(new_idf, 'AirTerminal', flgExact = False)\n", "print(selection)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "tree_get_class 10 : Search of AirTerminal 5 hits in \n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "[, , , , ]\n" ] } ], "prompt_number": 14 }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we found the 2 VAV:NoReheat plus the 3 VAV:Reheat. What are they? Each selection in the list is an XML node. XML nodes can be operated on, printed, etc., according to the lxml module. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "util_xml.printXML(selection[0])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", " AirTerminal:SingleDuct:VAV:NoReheat\n", " SPACE2-1 VAV System\n", " ReheatCoilAvailSched\n", " SPACE2-1 In Node\n", " SPACE2-1 ATU In Node\n", " autosize\n", " Constant\n", " 0.3\n", "\n", "\n" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the general structure of the EnergyPlus XML schema: Each OBJECT represents an IDF object. It has a class name, and 'n' attributes. The first attribute is sometimes, but not always, the name. The parser also captures the comments in the IDF string. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Classes can be deleted. Note below that 2 objects are deleted, reducing XML Object count from 348 to 346. However, the IDF lines remain at 3679. The ASCII text representation is not reflected to XML representation until convert_XML_to_IDF is called. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(new_idf)\n", "util_xml.delete_classes(new_idf, ['AirTerminal:SingleDuct:VAV:NoReheat'])\n", "print(new_idf)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "tree_get_class 10 : Search of ^^AirTerminal:SingleDuct:VAV:NoReheat$$ 2 hits in \n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "delete_classes 10 : NhXk: Deleted 2 ^AirTerminal:SingleDuct:VAV:NoReheat$ objects\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "IDF:NhXk, IDF Lines:3679, XML Objects:348, XML_root:\n", "IDF:NhXk, IDF Lines:3679, XML Objects:346, XML_root:\n" ] } ], "prompt_number": 16 }, { "cell_type": "markdown", "metadata": {}, "source": [ "convert_XML_to_IDF() uses an XLST transfrom to reproduce the IDF. Currently, comments are not written back to ASCII. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "new_idf.convert_XML_to_IDF()\n", "print(new_idf)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "convert_XML_to_IDF 10 : NhXk: Converted XML to IDF, 346 objects\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "IDF:NhXk, IDF Lines:3914, XML Objects:346, XML_root:\n" ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, this new object can be written back to disk. Note that write_IDF() calls convert_XML_to_IDF() first, so manual calls to convert_XML_to_IDF() are usually never necessary. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "new_idf.write_IDF('d:\\\\testing EnergyParser.idf')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "convert_XML_to_IDF 10 : NhXk: Converted XML to IDF, 346 objects\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "write_IDF 10 : NhXk: Wrote IDF d:\\testing EnergyParser.idf, 346 objects\n" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "It might also be interesting to write the XML to disk directly. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "new_idf.write_XML('d:\\\\testing EnergyParser.xml')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "write_XML 10 : NhXk: Wrote XML d:\\testing EnergyParser.xml\n" ] } ], "prompt_number": 19 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Advanced object manipulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The definition of a valid IDF file is described by the Input Data Dictionary IDD file. This concept of validation is also important in XML, with the concepts of a schema (XSD) and Document Type Definition (DTD). Both describe the structure of an XML document. It could be possible to create an XSD from the IDD, and have a powerful definition tool within the XML paradigm. However this project does not support this. Instead, because the IDD has the exact same syntax as IDF, it is read directly as follows." ] }, { "cell_type": "code", "collapsed": false, "input": [ "path_idd = r\"D:\\Apps\\EnergyPlusV8-1-0\\Energy+.idd\"\n", "idd_definition = idf.IDF.from_IDD_file(path_idd)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "load_IDF 10 : None: Loaded IDF D:\\Apps\\EnergyPlusV8-1-0\\Energy+.idd with 90012 lines\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "parse_IDF_to_XML_2 10 : None: Converted IDD to XML: , 727 objects\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "from_IDD_file 10 : None: Created an IDD (DEFINITION) object named None, with 727 objects\n" ] } ], "prompt_number": 20 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that this is the from_IDD_file() method, NOT from_IDF_file()\n", "
The IDD file has a different syntax compared to IDF which describes all aspects of each object\n", "
The speed of loading can be increased by writing this back to XML and using from_XML_file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below is an example of an IDD object converted into XML. There is signifantly more information describing each attribute of each class, all of which is captured by the parser. This is a fairly flat representation where the information is captured in XML attributes. A clearer representation would be more hierarchical, but this suffices. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "target_class = util_xml.tree_get_class(idd_definition, 'Site:WeatherStation')[0]\n", "util_xml.printXML(target_class)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "tree_get_class 10 : Search of ^Site:WeatherStation$ 1 hits in \n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", " Site:WeatherStation\n", " N1\n", " N2\n", " N3\n", " N4\n", "\n", "\n" ] } ], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": {}, "source": [ "An advanced manipulation consists of defining \n", "
1) Which class\n", "
2) The specific instance name\n", "
3) The attribute to change (Retreived from IDD)\n", "
4) The new value of this attribute for all matched items\n", "
All selection criteria are full regex supported, so '.' matches to 'any' matched string\n", "
This definition is contained in a dictionary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For example, let's change the cieling height of all spaces to be 3 m. First, let's look at one of the spaces in detail;" ] }, { "cell_type": "code", "collapsed": false, "input": [ "selection = util_xml.tree_get_class(new_idf, 'Zone')\n", "util_xml.printXML(selection[3])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "tree_get_class 10 : Search of ^Zone$ 6 hits in \n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", " Zone\n", " SPACE3-1\n", " 0\n", " 0\n", " 0\n", " 0\n", " 1\n", " 1\n", " 2.438400269\n", " 239.247360229\n", "\n", "\n" ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, get this class from the IDD (Not this IDF!)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "target_class = util_xml.tree_get_class(idd_definition, 'Zone')[0]\n", "print(target_class)\n", "#util_xml.printXML(target_class)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "tree_get_class 10 : Search of ^Zone$ 1 hits in \n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": {}, "source": [ "And get the integer position of our desired field. This is done again on the IDD object, since the IDF objects may not have this information (no comments in IDF file!). " ] }, { "cell_type": "code", "collapsed": false, "input": [ "util_xml.get_IDD_matched_position(target_class,'field','Ceiling Height')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "get_IDD_matched_position 10 : field=Ceiling Height positions [8]\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 24, "text": [ "8" ] } ], "prompt_number": 24 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This concept can also be used to select objects with a " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we have all information required to make a precise selection of this attribute in the IDF file. Let's select all Zone objects with the name starting with SPACE, so we don't select any PLENUM's. A utility function is provided which handles all of the above steps. It is called with a dictionary defining all aspects of the change; the class name, the object instance name (first ATTR), the attribute aka field name (From the IDD definition), and what value you want matching attributes to have." ] }, { "cell_type": "code", "collapsed": false, "input": [ "this_change = {'class' :'^Zone$',\n", " 'objName' :'^SPACE',\n", " 'attr' :'Ceiling Height',\n", " 'newVal' :'3.0',\n", " } " ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 40 }, { "cell_type": "code", "collapsed": false, "input": [ "util_xml.apply_change(new_idf, idd_definition, this_change)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "apply_change 10 : NhXk: Applied change 5 times: \n", "{'newVal': '3.0', 'attr': 'Ceiling Height', 'class': '^Zone$', 'objName': '^SPACE'} \n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 41, "text": [ "" ] } ], "prompt_number": 41 }, { "cell_type": "code", "collapsed": false, "input": [ "selection = util_xml.tree_get_class(new_idf, 'Zone')\n", "for obj in selection:\n", " break\n", " util_xml.printXML(obj)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "tree_get_class 10 : Search of ^Zone$ 6 hits in \n" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This concept is flexible through Regular expressions. " ] }, { "cell_type": "heading", "level": 6, "metadata": {}, "source": [ "Example: Exact match and update of a zone. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "this_change = {'class' :'^Zone$',\n", " 'objName' :'^SPACE4-1$',\n", " 'attr' :'Ceiling Height',\n", " 'newVal' :'3.5',\n", " } \n", "util_xml.apply_change(new_idf, idd_definition, this_change)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "apply_change 10 : NhXk: Applied change 1 times: \n", "{'newVal': '3.5', 'attr': 'Ceiling Height', 'class': '^Zone$', 'objName': '^SPACE4-1$'} \n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 45, "text": [ "" ] } ], "prompt_number": 45 }, { "cell_type": "heading", "level": 6, "metadata": {}, "source": [ "Example: Match all zones with any name using regex wildcard." ] }, { "cell_type": "code", "collapsed": false, "input": [ "this_change = {'class' :'^Zone$',\n", " 'objName' :'.',\n", " 'attr' :'Ceiling Height',\n", " 'newVal' :'2.8',\n", " } \n", "util_xml.apply_change(new_idf, idd_definition, this_change)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "apply_change 10 : NhXk: Applied change 6 times: \n", "{'newVal': '2.8', 'attr': 'Ceiling Height', 'class': '^Zone$', 'objName': '.'} \n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 48, "text": [ "" ] } ], "prompt_number": 48 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In general my use case is as follows;\n", "
    \n", "
  1. Define n variants with m 'change definition dictionaries'\n", "
  2. Load IDD\n", "
  3. Loop over n:\n", "
      \n", "
    1. Load IDF \n", "
    2. Make deletions of unwanted classes\n", "
    3. Merge in other useful bits of IDF code i.e. ASHRAE rotations, output variables, etc.\n", "
    4. Loop over m change definition dictionaries\n", "\n", "
    5. Write the nth IDF back to disk\n", "
    \n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using Excel or a text file etc., the above dictionary structures can be listed in tables to define a workflow. " ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }